aboutsummaryrefslogtreecommitdiffstats
path: root/src/classes/share
diff options
context:
space:
mode:
Diffstat (limited to 'src/classes/share')
-rw-r--r--src/classes/share/javax/media/j3d/Alpha.java1005
-rw-r--r--src/classes/share/javax/media/j3d/AlternateAppearance.java594
-rw-r--r--src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java862
-rw-r--r--src/classes/share/javax/media/j3d/AmbientLight.java90
-rw-r--r--src/classes/share/javax/media/j3d/AmbientLightRetained.java39
-rw-r--r--src/classes/share/javax/media/j3d/Appearance.java927
-rw-r--r--src/classes/share/javax/media/j3d/AppearanceRetained.java1391
-rw-r--r--src/classes/share/javax/media/j3d/AssertionFailureException.java35
-rw-r--r--src/classes/share/javax/media/j3d/AttributeBin.java475
-rw-r--r--src/classes/share/javax/media/j3d/AudioDevice.java257
-rw-r--r--src/classes/share/javax/media/j3d/AudioDevice3D.java515
-rw-r--r--src/classes/share/javax/media/j3d/AudioDevice3DL2.java305
-rw-r--r--src/classes/share/javax/media/j3d/AudioDeviceEnumerator.java67
-rw-r--r--src/classes/share/javax/media/j3d/AuralAttributes.java1261
-rw-r--r--src/classes/share/javax/media/j3d/AuralAttributesRetained.java654
-rw-r--r--src/classes/share/javax/media/j3d/BHInsertStructure.java139
-rw-r--r--src/classes/share/javax/media/j3d/BHInternalNode.java196
-rw-r--r--src/classes/share/javax/media/j3d/BHLeafInterface.java26
-rw-r--r--src/classes/share/javax/media/j3d/BHLeafNode.java91
-rw-r--r--src/classes/share/javax/media/j3d/BHNode.java271
-rw-r--r--src/classes/share/javax/media/j3d/BHTree.java1136
-rw-r--r--src/classes/share/javax/media/j3d/Background.java704
-rw-r--r--src/classes/share/javax/media/j3d/BackgroundRetained.java817
-rw-r--r--src/classes/share/javax/media/j3d/BackgroundSound.java136
-rw-r--r--src/classes/share/javax/media/j3d/BackgroundSoundRetained.java28
-rw-r--r--src/classes/share/javax/media/j3d/BadTransformException.java55
-rw-r--r--src/classes/share/javax/media/j3d/Behavior.java500
-rw-r--r--src/classes/share/javax/media/j3d/BehaviorRetained.java508
-rw-r--r--src/classes/share/javax/media/j3d/BehaviorScheduler.java218
-rw-r--r--src/classes/share/javax/media/j3d/BehaviorStructure.java1640
-rw-r--r--src/classes/share/javax/media/j3d/Billboard.java661
-rw-r--r--src/classes/share/javax/media/j3d/BoundingBox.java1975
-rw-r--r--src/classes/share/javax/media/j3d/BoundingLeaf.java159
-rw-r--r--src/classes/share/javax/media/j3d/BoundingLeafRetained.java269
-rw-r--r--src/classes/share/javax/media/j3d/BoundingPolytope.java1741
-rw-r--r--src/classes/share/javax/media/j3d/BoundingSphere.java1765
-rw-r--r--src/classes/share/javax/media/j3d/Bounds.java647
-rw-r--r--src/classes/share/javax/media/j3d/BranchGroup.java207
-rw-r--r--src/classes/share/javax/media/j3d/BranchGroupRetained.java209
-rw-r--r--src/classes/share/javax/media/j3d/CachedFrustum.java571
-rw-r--r--src/classes/share/javax/media/j3d/CachedTargets.java109
-rw-r--r--src/classes/share/javax/media/j3d/Canvas3D.java4209
-rw-r--r--src/classes/share/javax/media/j3d/CanvasViewCache.java2007
-rw-r--r--src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java78
-rw-r--r--src/classes/share/javax/media/j3d/CapabilityBits.java492
-rw-r--r--src/classes/share/javax/media/j3d/CapabilityNotSetException.java37
-rw-r--r--src/classes/share/javax/media/j3d/Clip.java284
-rw-r--r--src/classes/share/javax/media/j3d/ClipRetained.java384
-rw-r--r--src/classes/share/javax/media/j3d/ColorInterpolator.java296
-rw-r--r--src/classes/share/javax/media/j3d/ColoringAttributes.java337
-rw-r--r--src/classes/share/javax/media/j3d/ColoringAttributesRetained.java253
-rw-r--r--src/classes/share/javax/media/j3d/CompileState.java325
-rw-r--r--src/classes/share/javax/media/j3d/CompressedGeometry.java431
-rw-r--r--src/classes/share/javax/media/j3d/CompressedGeometryHeader.java239
-rw-r--r--src/classes/share/javax/media/j3d/CompressedGeometryRenderMethod.java103
-rw-r--r--src/classes/share/javax/media/j3d/CompressedGeometryRetained.java439
-rw-r--r--src/classes/share/javax/media/j3d/ConeSound.java903
-rw-r--r--src/classes/share/javax/media/j3d/ConeSoundRetained.java641
-rw-r--r--src/classes/share/javax/media/j3d/DanglingReferenceException.java44
-rw-r--r--src/classes/share/javax/media/j3d/DecalGroup.java78
-rw-r--r--src/classes/share/javax/media/j3d/DecalGroupRetained.java20
-rw-r--r--src/classes/share/javax/media/j3d/DefaultRenderMethod.java74
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponent.java67
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponentFloat.java117
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponentFloatRetained.java74
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponentInt.java115
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponentIntRetained.java74
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponentNative.java107
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponentNativeRetained.java63
-rw-r--r--src/classes/share/javax/media/j3d/DepthComponentRetained.java48
-rw-r--r--src/classes/share/javax/media/j3d/DetailTextureImage.java219
-rw-r--r--src/classes/share/javax/media/j3d/DirectionalLight.java188
-rw-r--r--src/classes/share/javax/media/j3d/DirectionalLightRetained.java203
-rw-r--r--src/classes/share/javax/media/j3d/DisplayListRenderMethod.java256
-rw-r--r--src/classes/share/javax/media/j3d/DistanceLOD.java294
-rw-r--r--src/classes/share/javax/media/j3d/DrawingSurfaceObject.java42
-rw-r--r--src/classes/share/javax/media/j3d/DrawingSurfaceObjectAWT.java142
-rw-r--r--src/classes/share/javax/media/j3d/EnvironmentSet.java563
-rw-r--r--src/classes/share/javax/media/j3d/EventCatcher.java388
-rw-r--r--src/classes/share/javax/media/j3d/ExceptionStrings.properties892
-rw-r--r--src/classes/share/javax/media/j3d/ExponentialFog.java190
-rw-r--r--src/classes/share/javax/media/j3d/ExponentialFogRetained.java147
-rw-r--r--src/classes/share/javax/media/j3d/Fog.java536
-rw-r--r--src/classes/share/javax/media/j3d/FogRetained.java789
-rw-r--r--src/classes/share/javax/media/j3d/Font3D.java1105
-rw-r--r--src/classes/share/javax/media/j3d/FontExtrusion.java241
-rw-r--r--src/classes/share/javax/media/j3d/FreeListManager.java98
-rw-r--r--src/classes/share/javax/media/j3d/GeneralizedStrip.java875
-rw-r--r--src/classes/share/javax/media/j3d/GeneralizedStripFlags.java73
-rw-r--r--src/classes/share/javax/media/j3d/GeneralizedVertexList.java387
-rw-r--r--src/classes/share/javax/media/j3d/Geometry.java45
-rw-r--r--src/classes/share/javax/media/j3d/GeometryArray.java5763
-rw-r--r--src/classes/share/javax/media/j3d/GeometryArrayRetained.java10631
-rw-r--r--src/classes/share/javax/media/j3d/GeometryAtom.java263
-rw-r--r--src/classes/share/javax/media/j3d/GeometryDecompressor.java1200
-rw-r--r--src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java384
-rw-r--r--src/classes/share/javax/media/j3d/GeometryDecompressorShape3D.java466
-rw-r--r--src/classes/share/javax/media/j3d/GeometryLock.java72
-rw-r--r--src/classes/share/javax/media/j3d/GeometryRetained.java276
-rw-r--r--src/classes/share/javax/media/j3d/GeometryStripArray.java223
-rw-r--r--src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java739
-rw-r--r--src/classes/share/javax/media/j3d/GeometryStructure.java1157
-rw-r--r--src/classes/share/javax/media/j3d/GeometryUpdater.java43
-rw-r--r--src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java372
-rw-r--r--src/classes/share/javax/media/j3d/GraphicsContext3D.java2999
-rw-r--r--src/classes/share/javax/media/j3d/Group.java532
-rw-r--r--src/classes/share/javax/media/j3d/GroupRetained.java3110
-rw-r--r--src/classes/share/javax/media/j3d/HashKey.java240
-rw-r--r--src/classes/share/javax/media/j3d/HiResCoord.java724
-rw-r--r--src/classes/share/javax/media/j3d/IllegalRenderingStateException.java35
-rw-r--r--src/classes/share/javax/media/j3d/IllegalSceneGraphException.java40
-rw-r--r--src/classes/share/javax/media/j3d/IllegalSharingException.java60
-rw-r--r--src/classes/share/javax/media/j3d/ImageComponent.java346
-rw-r--r--src/classes/share/javax/media/j3d/ImageComponent2D.java534
-rw-r--r--src/classes/share/javax/media/j3d/ImageComponent2DRetained.java1384
-rw-r--r--src/classes/share/javax/media/j3d/ImageComponent3D.java711
-rw-r--r--src/classes/share/javax/media/j3d/ImageComponent3DRetained.java217
-rw-r--r--src/classes/share/javax/media/j3d/ImageComponentRetained.java1559
-rw-r--r--src/classes/share/javax/media/j3d/ImageComponentUpdateInfo.java30
-rw-r--r--src/classes/share/javax/media/j3d/IndexedGeometryArray.java883
-rw-r--r--src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java1609
-rw-r--r--src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java210
-rw-r--r--src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java207
-rw-r--r--src/classes/share/javax/media/j3d/IndexedLineArray.java170
-rw-r--r--src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java335
-rw-r--r--src/classes/share/javax/media/j3d/IndexedLineStripArray.java197
-rw-r--r--src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java412
-rw-r--r--src/classes/share/javax/media/j3d/IndexedObject.java54
-rw-r--r--src/classes/share/javax/media/j3d/IndexedPointArray.java170
-rw-r--r--src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java247
-rw-r--r--src/classes/share/javax/media/j3d/IndexedQuadArray.java174
-rw-r--r--src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java387
-rw-r--r--src/classes/share/javax/media/j3d/IndexedTriangleArray.java175
-rw-r--r--src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java350
-rw-r--r--src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java197
-rw-r--r--src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java415
-rw-r--r--src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java196
-rw-r--r--src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java430
-rw-r--r--src/classes/share/javax/media/j3d/IndexedUnorderSet.java598
-rw-r--r--src/classes/share/javax/media/j3d/InputDevice.java153
-rw-r--r--src/classes/share/javax/media/j3d/InputDeviceBlockingThread.java99
-rw-r--r--src/classes/share/javax/media/j3d/InputDeviceScheduler.java204
-rw-r--r--src/classes/share/javax/media/j3d/IntegerFreeList.java40
-rw-r--r--src/classes/share/javax/media/j3d/Interpolator.java126
-rw-r--r--src/classes/share/javax/media/j3d/J3DBuffer.java243
-rw-r--r--src/classes/share/javax/media/j3d/J3DGraphics2D.java171
-rw-r--r--src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java1056
-rw-r--r--src/classes/share/javax/media/j3d/J3dDebug.java442
-rw-r--r--src/classes/share/javax/media/j3d/J3dI18N.java31
-rw-r--r--src/classes/share/javax/media/j3d/J3dMessage.java165
-rw-r--r--src/classes/share/javax/media/j3d/J3dNodeTable.java290
-rw-r--r--src/classes/share/javax/media/j3d/J3dQueryProps.java110
-rw-r--r--src/classes/share/javax/media/j3d/J3dStructure.java153
-rw-r--r--src/classes/share/javax/media/j3d/J3dThread.java316
-rw-r--r--src/classes/share/javax/media/j3d/J3dThreadData.java92
-rw-r--r--src/classes/share/javax/media/j3d/LOD.java220
-rw-r--r--src/classes/share/javax/media/j3d/Leaf.java34
-rw-r--r--src/classes/share/javax/media/j3d/LeafRetained.java48
-rw-r--r--src/classes/share/javax/media/j3d/Light.java696
-rw-r--r--src/classes/share/javax/media/j3d/LightBin.java445
-rw-r--r--src/classes/share/javax/media/j3d/LightRetained.java1062
-rw-r--r--src/classes/share/javax/media/j3d/LightSet.java91
-rw-r--r--src/classes/share/javax/media/j3d/LineArray.java161
-rw-r--r--src/classes/share/javax/media/j3d/LineArrayRetained.java367
-rw-r--r--src/classes/share/javax/media/j3d/LineAttributes.java472
-rw-r--r--src/classes/share/javax/media/j3d/LineAttributesRetained.java335
-rw-r--r--src/classes/share/javax/media/j3d/LineStripArray.java177
-rw-r--r--src/classes/share/javax/media/j3d/LineStripArrayRetained.java463
-rw-r--r--src/classes/share/javax/media/j3d/LinearFog.java232
-rw-r--r--src/classes/share/javax/media/j3d/LinearFogRetained.java197
-rw-r--r--src/classes/share/javax/media/j3d/Link.java140
-rw-r--r--src/classes/share/javax/media/j3d/LinkRetained.java330
-rw-r--r--src/classes/share/javax/media/j3d/Locale.java656
-rw-r--r--src/classes/share/javax/media/j3d/MRSWLock.java74
-rw-r--r--src/classes/share/javax/media/j3d/MasterControl.java3702
-rw-r--r--src/classes/share/javax/media/j3d/MasterControlThread.java48
-rw-r--r--src/classes/share/javax/media/j3d/Material.java689
-rw-r--r--src/classes/share/javax/media/j3d/MaterialRetained.java555
-rw-r--r--src/classes/share/javax/media/j3d/MediaContainer.java320
-rw-r--r--src/classes/share/javax/media/j3d/MediaContainerRetained.java196
-rw-r--r--src/classes/share/javax/media/j3d/MemoryFreeList.java264
-rw-r--r--src/classes/share/javax/media/j3d/ModelClip.java702
-rw-r--r--src/classes/share/javax/media/j3d/ModelClipRetained.java1055
-rw-r--r--src/classes/share/javax/media/j3d/Morph.java654
-rw-r--r--src/classes/share/javax/media/j3d/MorphRetained.java1894
-rw-r--r--src/classes/share/javax/media/j3d/MultipleParentException.java37
-rw-r--r--src/classes/share/javax/media/j3d/NativeAPIInfo.java32
-rw-r--r--src/classes/share/javax/media/j3d/NnuId.java25
-rw-r--r--src/classes/share/javax/media/j3d/NnuIdManager.java335
-rw-r--r--src/classes/share/javax/media/j3d/Node.java733
-rw-r--r--src/classes/share/javax/media/j3d/NodeComponent.java264
-rw-r--r--src/classes/share/javax/media/j3d/NodeComponentRetained.java241
-rw-r--r--src/classes/share/javax/media/j3d/NodeComponentUpdate.java26
-rw-r--r--src/classes/share/javax/media/j3d/NodeData.java21
-rw-r--r--src/classes/share/javax/media/j3d/NodeReferenceTable.java121
-rw-r--r--src/classes/share/javax/media/j3d/NodeRetained.java914
-rw-r--r--src/classes/share/javax/media/j3d/ObjectUpdate.java26
-rw-r--r--src/classes/share/javax/media/j3d/OrderedBin.java109
-rw-r--r--src/classes/share/javax/media/j3d/OrderedChildInfo.java64
-rw-r--r--src/classes/share/javax/media/j3d/OrderedCollection.java58
-rw-r--r--src/classes/share/javax/media/j3d/OrderedGroup.java458
-rw-r--r--src/classes/share/javax/media/j3d/OrderedGroupRetained.java502
-rw-r--r--src/classes/share/javax/media/j3d/OrderedPath.java40
-rw-r--r--src/classes/share/javax/media/j3d/OrderedPathElement.java25
-rw-r--r--src/classes/share/javax/media/j3d/OrientedShape3D.java645
-rw-r--r--src/classes/share/javax/media/j3d/OrientedShape3DRenderMethod.java111
-rw-r--r--src/classes/share/javax/media/j3d/OrientedShape3DRetained.java592
-rw-r--r--src/classes/share/javax/media/j3d/PathInterpolator.java278
-rw-r--r--src/classes/share/javax/media/j3d/PhysicalBody.java356
-rw-r--r--src/classes/share/javax/media/j3d/PhysicalEnvironment.java513
-rw-r--r--src/classes/share/javax/media/j3d/PickBounds.java89
-rw-r--r--src/classes/share/javax/media/j3d/PickCone.java89
-rw-r--r--src/classes/share/javax/media/j3d/PickConeRay.java266
-rw-r--r--src/classes/share/javax/media/j3d/PickConeSegment.java292
-rw-r--r--src/classes/share/javax/media/j3d/PickCylinder.java97
-rw-r--r--src/classes/share/javax/media/j3d/PickCylinderRay.java248
-rw-r--r--src/classes/share/javax/media/j3d/PickCylinderSegment.java262
-rw-r--r--src/classes/share/javax/media/j3d/PickPoint.java94
-rw-r--r--src/classes/share/javax/media/j3d/PickRay.java119
-rw-r--r--src/classes/share/javax/media/j3d/PickSegment.java106
-rw-r--r--src/classes/share/javax/media/j3d/PickShape.java71
-rw-r--r--src/classes/share/javax/media/j3d/Picking.java660
-rw-r--r--src/classes/share/javax/media/j3d/PointArray.java150
-rw-r--r--src/classes/share/javax/media/j3d/PointArrayRetained.java247
-rw-r--r--src/classes/share/javax/media/j3d/PointAttributes.java209
-rw-r--r--src/classes/share/javax/media/j3d/PointAttributesRetained.java208
-rw-r--r--src/classes/share/javax/media/j3d/PointLight.java292
-rw-r--r--src/classes/share/javax/media/j3d/PointLightRetained.java322
-rw-r--r--src/classes/share/javax/media/j3d/PointSound.java528
-rw-r--r--src/classes/share/javax/media/j3d/PointSoundRetained.java289
-rw-r--r--src/classes/share/javax/media/j3d/PolygonAttributes.java464
-rw-r--r--src/classes/share/javax/media/j3d/PolygonAttributesRetained.java351
-rw-r--r--src/classes/share/javax/media/j3d/PositionInterpolator.java204
-rw-r--r--src/classes/share/javax/media/j3d/PositionPathInterpolator.java263
-rw-r--r--src/classes/share/javax/media/j3d/QuadArray.java156
-rw-r--r--src/classes/share/javax/media/j3d/QuadArrayRetained.java460
-rw-r--r--src/classes/share/javax/media/j3d/Raster.java749
-rw-r--r--src/classes/share/javax/media/j3d/RasterRetained.java719
-rw-r--r--src/classes/share/javax/media/j3d/RenderAtom.java366
-rw-r--r--src/classes/share/javax/media/j3d/RenderAtomListInfo.java43
-rw-r--r--src/classes/share/javax/media/j3d/RenderBin.java6891
-rw-r--r--src/classes/share/javax/media/j3d/RenderMethod.java27
-rw-r--r--src/classes/share/javax/media/j3d/RenderMolecule.java3122
-rw-r--r--src/classes/share/javax/media/j3d/Renderer.java1688
-rw-r--r--src/classes/share/javax/media/j3d/RendererStructure.java56
-rw-r--r--src/classes/share/javax/media/j3d/RenderingAttributes.java735
-rw-r--r--src/classes/share/javax/media/j3d/RenderingAttributesRetained.java486
-rw-r--r--src/classes/share/javax/media/j3d/RenderingAttributesStructure.java226
-rw-r--r--src/classes/share/javax/media/j3d/RenderingEnvironmentStructure.java1754
-rw-r--r--src/classes/share/javax/media/j3d/RestrictedAccessException.java37
-rw-r--r--src/classes/share/javax/media/j3d/RotPosPathInterpolator.java378
-rw-r--r--src/classes/share/javax/media/j3d/RotPosScalePathInterpolator.java450
-rw-r--r--src/classes/share/javax/media/j3d/RotationInterpolator.java202
-rw-r--r--src/classes/share/javax/media/j3d/RotationPathInterpolator.java296
-rw-r--r--src/classes/share/javax/media/j3d/ScaleInterpolator.java205
-rw-r--r--src/classes/share/javax/media/j3d/SceneGraphCycleException.java44
-rw-r--r--src/classes/share/javax/media/j3d/SceneGraphObject.java405
-rw-r--r--src/classes/share/javax/media/j3d/SceneGraphObjectRetained.java170
-rw-r--r--src/classes/share/javax/media/j3d/SceneGraphPath.java652
-rw-r--r--src/classes/share/javax/media/j3d/Screen3D.java490
-rw-r--r--src/classes/share/javax/media/j3d/ScreenViewCache.java114
-rw-r--r--src/classes/share/javax/media/j3d/Sensor.java697
-rw-r--r--src/classes/share/javax/media/j3d/SensorRead.java167
-rw-r--r--src/classes/share/javax/media/j3d/SetLiveState.java252
-rw-r--r--src/classes/share/javax/media/j3d/Shape3D.java759
-rw-r--r--src/classes/share/javax/media/j3d/Shape3DCompileRetained.java504
-rw-r--r--src/classes/share/javax/media/j3d/Shape3DRetained.java2770
-rw-r--r--src/classes/share/javax/media/j3d/SharedGroup.java144
-rw-r--r--src/classes/share/javax/media/j3d/SharedGroupRetained.java878
-rw-r--r--src/classes/share/javax/media/j3d/Sound.java1151
-rw-r--r--src/classes/share/javax/media/j3d/SoundException.java35
-rw-r--r--src/classes/share/javax/media/j3d/SoundRenderer.java75
-rw-r--r--src/classes/share/javax/media/j3d/SoundRetained.java1297
-rw-r--r--src/classes/share/javax/media/j3d/SoundScheduler.java3241
-rw-r--r--src/classes/share/javax/media/j3d/SoundSchedulerAtom.java694
-rw-r--r--src/classes/share/javax/media/j3d/SoundStructure.java721
-rw-r--r--src/classes/share/javax/media/j3d/Soundscape.java332
-rw-r--r--src/classes/share/javax/media/j3d/SoundscapeRetained.java440
-rw-r--r--src/classes/share/javax/media/j3d/SpotLight.java341
-rw-r--r--src/classes/share/javax/media/j3d/SpotLightRetained.java341
-rw-r--r--src/classes/share/javax/media/j3d/StructureUpdateThread.java85
-rw-r--r--src/classes/share/javax/media/j3d/Switch.java247
-rw-r--r--src/classes/share/javax/media/j3d/SwitchRetained.java885
-rw-r--r--src/classes/share/javax/media/j3d/SwitchState.java112
-rw-r--r--src/classes/share/javax/media/j3d/SwitchValueInterpolator.java276
-rw-r--r--src/classes/share/javax/media/j3d/Table.java84
-rw-r--r--src/classes/share/javax/media/j3d/Targets.java198
-rw-r--r--src/classes/share/javax/media/j3d/TargetsInterface.java35
-rw-r--r--src/classes/share/javax/media/j3d/TexCoordGeneration.java644
-rw-r--r--src/classes/share/javax/media/j3d/TexCoordGenerationRetained.java423
-rw-r--r--src/classes/share/javax/media/j3d/Text3D.java619
-rw-r--r--src/classes/share/javax/media/j3d/Text3DRenderMethod.java109
-rw-r--r--src/classes/share/javax/media/j3d/Text3DRetained.java985
-rw-r--r--src/classes/share/javax/media/j3d/Texture.java1749
-rw-r--r--src/classes/share/javax/media/j3d/Texture2D.java554
-rw-r--r--src/classes/share/javax/media/j3d/Texture2DRetained.java357
-rw-r--r--src/classes/share/javax/media/j3d/Texture3D.java220
-rw-r--r--src/classes/share/javax/media/j3d/Texture3DRetained.java199
-rw-r--r--src/classes/share/javax/media/j3d/TextureAttributes.java1403
-rw-r--r--src/classes/share/javax/media/j3d/TextureAttributesRetained.java1015
-rw-r--r--src/classes/share/javax/media/j3d/TextureBin.java1683
-rw-r--r--src/classes/share/javax/media/j3d/TextureCubeMap.java344
-rw-r--r--src/classes/share/javax/media/j3d/TextureCubeMapRetained.java301
-rw-r--r--src/classes/share/javax/media/j3d/TextureRetained.java2597
-rw-r--r--src/classes/share/javax/media/j3d/TextureUnitState.java320
-rw-r--r--src/classes/share/javax/media/j3d/TextureUnitStateRetained.java615
-rw-r--r--src/classes/share/javax/media/j3d/TimerThread.java144
-rw-r--r--src/classes/share/javax/media/j3d/Transform3D.java5641
-rw-r--r--src/classes/share/javax/media/j3d/TransformGroup.java180
-rw-r--r--src/classes/share/javax/media/j3d/TransformGroupData.java23
-rw-r--r--src/classes/share/javax/media/j3d/TransformGroupRetained.java1219
-rw-r--r--src/classes/share/javax/media/j3d/TransformInterpolator.java234
-rw-r--r--src/classes/share/javax/media/j3d/TransformStructure.java724
-rw-r--r--src/classes/share/javax/media/j3d/TransparencyAttributes.java531
-rw-r--r--src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java347
-rw-r--r--src/classes/share/javax/media/j3d/TransparencyInterpolator.java271
-rw-r--r--src/classes/share/javax/media/j3d/TransparentRenderingInfo.java104
-rw-r--r--src/classes/share/javax/media/j3d/TriangleArray.java154
-rw-r--r--src/classes/share/javax/media/j3d/TriangleArrayRetained.java414
-rw-r--r--src/classes/share/javax/media/j3d/TriangleFanArray.java179
-rw-r--r--src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java499
-rw-r--r--src/classes/share/javax/media/j3d/TriangleStripArray.java178
-rw-r--r--src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java519
-rw-r--r--src/classes/share/javax/media/j3d/UnorderList.java574
-rw-r--r--src/classes/share/javax/media/j3d/UpdateTargets.java99
-rw-r--r--src/classes/share/javax/media/j3d/VersionInfo.java209
-rw-r--r--src/classes/share/javax/media/j3d/VertexArrayRenderMethod.java88
-rw-r--r--src/classes/share/javax/media/j3d/View.java3360
-rw-r--r--src/classes/share/javax/media/j3d/ViewCache.java343
-rw-r--r--src/classes/share/javax/media/j3d/ViewPlatform.java265
-rw-r--r--src/classes/share/javax/media/j3d/ViewPlatformRetained.java410
-rw-r--r--src/classes/share/javax/media/j3d/ViewSpecificGroup.java324
-rw-r--r--src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java747
-rw-r--r--src/classes/share/javax/media/j3d/VirtualUniverse.java910
-rw-r--r--src/classes/share/javax/media/j3d/WakeupAnd.java115
-rw-r--r--src/classes/share/javax/media/j3d/WakeupAndOfOrs.java116
-rw-r--r--src/classes/share/javax/media/j3d/WakeupCondition.java132
-rw-r--r--src/classes/share/javax/media/j3d/WakeupCriteriaEnumerator.java133
-rw-r--r--src/classes/share/javax/media/j3d/WakeupCriterion.java108
-rw-r--r--src/classes/share/javax/media/j3d/WakeupIndexedList.java584
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnAWTEvent.java145
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnActivation.java65
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnBehaviorPost.java113
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnCollisionEntry.java579
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnCollisionExit.java377
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnCollisionMovement.java385
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnDeactivation.java78
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnElapsedFrames.java164
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnElapsedTime.java94
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnElapsedTimeHeap.java210
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnSensorEntry.java129
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnSensorExit.java128
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnTransformChange.java80
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnViewPlatformEntry.java133
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOnViewPlatformExit.java135
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOr.java100
-rw-r--r--src/classes/share/javax/media/j3d/WakeupOrOfAnds.java100
357 files changed, 191921 insertions, 0 deletions
diff --git a/src/classes/share/javax/media/j3d/Alpha.java b/src/classes/share/javax/media/j3d/Alpha.java
new file mode 100644
index 0000000..d4dcdbb
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Alpha.java
@@ -0,0 +1,1005 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The alpha NodeComponent object provides common methods for
+ * converting a time value into an alpha value (a value in the range 0
+ * to 1). The Alpha object is effectively a function of time that
+ * generates alpha values in the range [0,1] when sampled: f(t) =
+ * [0,1]. A primary use of the Alpha object is to provide alpha
+ * values for Interpolator behaviors. The function f(t) and the
+ * characteristics of the Alpha object are determined by
+ * user-definable parameters:
+ *
+ * <p>
+ * <ul>
+ *
+ * <code>loopCount</code> -- This is the number of times to run this
+ * Alpha; a value of -1 specifies that the Alpha loops
+ * indefinitely.<p>
+ *
+ * <code>triggerTime</code> -- This is the time in milliseconds since
+ * the start time that this object first triggers. If (startTime +
+ * triggerTime >= currentTime) then the Alpha object starts running.<p>
+ *
+ * <code>phaseDelayDuration</code> -- This is an additional number of
+ * milliseconds to wait after triggerTime before actually starting
+ * this Alpha.<p>
+ *
+ * <code>mode</code> -- This can be set to INCREASING_ENABLE,
+ * DECREASING_ENABLE, or the Or'ed value of the two.
+ * INCREASING_ENABLE activates the increasing Alpha parameters listed
+ * below; DECREASING_ENABLE activates the decreasing Alpha parameters
+ * listed below.<p>
+ *
+ * </ul> Increasing Alpha parameters:<p> <ul>
+ *
+ * <code>increasingAlphaDuration</code> -- This is the period of time
+ * during which Alpha goes from zero to one. <p>
+ *
+ * <code>increasingAlphaRampDuration</code> -- This is the period of
+ * time during which the Alpha step size increases at the beginning of
+ * the increasingAlphaDuration and, correspondingly, decreases at the
+ * end of the increasingAlphaDuration. This parameter is clamped to
+ * half of increasingAlphaDuration. When this parameter is non-zero,
+ * one gets constant acceleration while it is in effect; constant
+ * positive acceleration at the beginning of the ramp and constant
+ * negative acceleration at the end of the ramp. If this parameter is
+ * zero, then the effective velocity of the Alpha value is constant
+ * and the acceleration is zero (ie, a linearly increasing alpha
+ * ramp).<p>
+ *
+ * <code>alphaAtOneDuration</code> -- This is the period of time that
+ * Alpha stays at one.<p> </ul> Decreasing Alpha parameters:<p> <ul>
+ *
+ * <code>decreasingAlphaDuration</code> -- This is the period of time
+ * during which Alpha goes from one to zero.<p>
+ *
+ * <code>decreasingAlphaRampDuration</code> -- This is the period of
+ * time during which the Alpha step size increases at the beginning of
+ * the decreasingAlphaDuration and, correspondingly, decreases at the
+ * end of the decreasingAlphaDuration. This parameter is clamped to
+ * half of decreasingAlphaDuration. When this parameter is non-zero,
+ * one gets constant acceleration while it is in effect; constant
+ * positive acceleration at the beginning of the ramp and constant
+ * negative acceleration at the end of the ramp. If this parameter is
+ * zero, the effective velocity of the Alpha value is constant and the
+ * acceleration is zero (i.e., a linearly-decreasing alpha ramp).<p>
+ *
+ * <code>alphaAtZeroDuration</code> -- This is the period of time that
+ * Alpha stays at zero.
+ *
+ * </ul>
+ *
+ * @see Interpolator
+ */
+
+public class Alpha extends NodeComponent {
+
+ // loopCount < -1 --> reserved
+ // loopCount == -1 --> repeat forever
+ // loopCount >= 0 --> repeat count
+ private int loopCount;
+
+ /**
+ * Specifies that the increasing component of the alpha is used.
+ */
+ public static final int INCREASING_ENABLE = 1;
+
+ /**
+ * Specifies that the decreasing component of the alpha is used
+ */
+ public static final int DECREASING_ENABLE = 2;
+
+ /**
+ * This alpha's mode, specifies whether to process
+ * increasing and decreasing alphas.
+ */
+ private int mode;
+
+ private float triggerTime;
+ private float phaseDelay;
+ private float increasingAlpha;
+ private long increasingAlphaRamp;
+ private float incAlphaRampInternal;
+ private float alphaAtOne;
+ private float decreasingAlpha;
+ private long decreasingAlphaRamp;
+ private float decAlphaRampInternal;
+ private float alphaAtZero;
+
+ // For pausing and resuming Alpha
+ private long pauseTime = 0L;
+ private boolean paused = false;
+
+ // Stop time gets used only for loopCount > 0
+ private float stopTime;
+
+ //long startTime = 0L; Convert it to Seconds
+ // NOTE: Start Time is in milliseconds
+ private long startTime = MasterControl.systemStartTime;
+
+ /**
+ * Constructs an Alpha object with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * loopCount : -1<br>
+ * mode : INCREASING_ENABLE<br>
+ * startTime : system start time<br>
+ * triggerTime : 0<br>
+ * phaseDelayDuration : 0<br>
+ * increasingAlphaDuration : 1000<br>
+ * increasingAlphaRampDuration : 0<br>
+ * alphaAtOneDuration : 0<br>
+ * decreasingAlphaDuration : 0<br>
+ * decreasingAlphaRampDuration : 0<br>
+ * alphaAtZeroDuration : 0<br>
+ * isPaused : false<br>
+ * pauseTime : 0<br>
+ * </ul>
+ */
+ public Alpha() {
+ loopCount = -1;
+ mode = INCREASING_ENABLE;
+ increasingAlpha = 1.0f; // converted to seconds internally
+ /*
+ // Java initialize them to zero by default
+ triggerTime = 0L;
+ phaseDelay = 0.0f;
+ increasingAlphaRamp = 0.0f;
+ alphaAtOne = 0.0f;
+ decreasingAlpha = 0.0f;
+ decreasingAlphaRamp = 0.0f;
+ alphaAtZero = 0.0f;
+ */
+ }
+
+
+ /**
+ * This constructor takes all of the Alpha user-definable parameters.
+ * @param loopCount number of times to run this alpha; a value
+ * of -1 specifies that the alpha loops indefinitely
+ * @param mode indicates whether the increasing alpha parameters or
+ * the decreasing alpha parameters or both are active. This parameter
+ * accepts the following values, INCREASING_ENABLE or
+ * DECREASING_ENABLE, which may be ORed together to specify
+ * that both are active.
+ * The increasing alpha parameters are increasingAlphaDuration,
+ * increasingAlphaRampDuration, and alphaAtOneDuration.
+ * The decreasing alpha parameters are decreasingAlphaDuration,
+ * decreasingAlphaRampDuration, and alphaAtZeroDuration.
+ * @param triggerTime time in milliseconds since the start time
+ * that this object first triggers
+ * @param phaseDelayDuration number of milliseconds to wait after
+ * triggerTime before actually starting this alpha
+ * @param increasingAlphaDuration period of time during which alpha goes
+ * from zero to one
+ * @param increasingAlphaRampDuration period of time during which
+ * the alpha step size increases at the beginning of the
+ * increasingAlphaDuration and, correspondingly, decreases at the end
+ * of the increasingAlphaDuration. This value is clamped to half of
+ * increasingAlphaDuration. NOTE: a value of zero means that the alpha
+ * step size remains constant during the entire increasingAlphaDuration.
+ * @param alphaAtOneDuration period of time that alpha stays at one
+ * @param decreasingAlphaDuration period of time during which alpha goes
+ * from one to zero
+ * @param decreasingAlphaRampDuration period of time during which
+ * the alpha step size increases at the beginning of the
+ * decreasingAlphaDuration and, correspondingly, decreases at the end
+ * of the decreasingAlphaDuration. This value is clamped to half of
+ * decreasingAlphaDuration. NOTE: a value of zero means that the alpha
+ * step size remains constant during the entire decreasingAlphaDuration.
+ * @param alphaAtZeroDuration period of time that alpha stays at zero
+ */
+ public Alpha(int loopCount, int mode,
+ long triggerTime, long phaseDelayDuration,
+ long increasingAlphaDuration,
+ long increasingAlphaRampDuration,
+ long alphaAtOneDuration,
+ long decreasingAlphaDuration,
+ long decreasingAlphaRampDuration,
+ long alphaAtZeroDuration) {
+
+ this.loopCount = loopCount;
+ this.mode = mode;
+ this.triggerTime = (float) triggerTime * .001f;
+ phaseDelay = (float) phaseDelayDuration * .001f;
+
+ increasingAlpha = (float) increasingAlphaDuration * .001f;
+ alphaAtOne = (float)alphaAtOneDuration * .001f;
+ increasingAlphaRamp = increasingAlphaRampDuration;
+ incAlphaRampInternal = increasingAlphaRampDuration * .001f;
+ if (incAlphaRampInternal > (0.5f * increasingAlpha)) {
+ incAlphaRampInternal = 0.5f * increasingAlpha;
+ }
+
+ decreasingAlpha = (float)decreasingAlphaDuration * .001f;
+ alphaAtZero = (float)alphaAtZeroDuration * .001f;
+ decreasingAlphaRamp = decreasingAlphaRampDuration;
+ decAlphaRampInternal = decreasingAlphaRampDuration * .001f;
+ if (decAlphaRampInternal > (0.5f * decreasingAlpha)) {
+ decAlphaRampInternal = 0.5f * decreasingAlpha;
+ }
+ computeStopTime();
+ }
+
+
+ /**
+ * Constructs a new Alpha object that assumes that the mode is
+ * INCREASING_ENABLE.
+ *
+ * @param loopCount number of times to run this alpha; a value
+ * of -1 specifies that the alpha loops indefinitely.
+ * @param triggerTime time in milliseconds since the start time
+ * that this object first triggers
+ * @param phaseDelayDuration number of milliseconds to wait after
+ * triggerTime before actually starting this alpha
+ * @param increasingAlphaDuration period of time during which alpha goes
+ * from zero to one
+ * @param increasingAlphaRampDuration period of time during which
+ * the alpha step size increases at the beginning of the
+ * increasingAlphaDuration and, correspondingly, decreases at the end
+ * of the increasingAlphaDuration. This value is clamped to half of
+ * increasingAlphaDuration. NOTE: a value of zero means that the alpha
+ * step size remains constant during the entire increasingAlphaDuration.
+ * @param alphaAtOneDuration period of time that alpha stays at one
+ */
+
+ public Alpha(int loopCount,
+ long triggerTime, long phaseDelayDuration,
+ long increasingAlphaDuration,
+ long increasingAlphaRampDuration,
+ long alphaAtOneDuration) {
+ this(loopCount, INCREASING_ENABLE,
+ triggerTime, phaseDelayDuration,
+ increasingAlphaDuration, increasingAlphaRampDuration,
+ alphaAtOneDuration, 0, 0, 0);
+ }
+
+
+ /**
+ * This constructor takes only the loopCount and increasingAlphaDuration
+ * as parameters and assigns the default values to all of the other
+ * parameters.
+ * @param loopCount number of times to run this alpha; a value
+ * of -1 specifies that the alpha loops indefinitely
+ * @param increasingAlphaDuration period of time during which alpha goes
+ * from zero to one
+ */
+ public Alpha(int loopCount, long increasingAlphaDuration) {
+ // defaults
+ mode = INCREASING_ENABLE;
+ increasingAlpha = (float) increasingAlphaDuration * .001f;
+ this.loopCount = loopCount;
+
+ if (loopCount >= 0) {
+ stopTime = loopCount*increasingAlpha;
+ }
+ }
+
+
+ /**
+ * Pauses this alpha object. The current system time when this
+ * method is called will be used in place of the actual current
+ * time when calculating subsequent alpha values. This has the
+ * effect of freezing the interpolator at the time the method is
+ * called.
+ *
+ * @since Java 3D 1.3
+ */
+ public void pause() {
+ pause(System.currentTimeMillis());
+ }
+
+ /**
+ * Pauses this alpha object as of the specified time. The specified
+ * time will be used in place of the actual current time when
+ * calculating subsequent alpha values. This has the effect of freezing
+ * the interpolator at the specified time. Note that specifying a
+ * time in the future (that is, a time greater than
+ * System.currentTimeMillis()) will cause the alpha to immediately
+ * advance to that point before pausing. Similarly, specifying a
+ * time in the past (that is, a time less than
+ * System.currentTimeMillis()) will cause the alpha to immediately
+ * revert to that point before pausing.
+ *
+ * @param time the time at which to pause the alpha
+ *
+ * @exception IllegalArgumentException if time <= 0
+ *
+ * @since Java 3D 1.3
+ */
+ public void pause(long time) {
+ if (time <= 0L) {
+ throw new IllegalArgumentException(J3dI18N.getString("Alpha0"));
+ }
+
+ paused = true;
+ pauseTime = time;
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Resumes this alpha object. If the alpha
+ * object was paused, the difference between the current
+ * time and the pause time will be used to adjust the startTime of
+ * this alpha. The equation is as follows:
+ *
+ * <ul>
+ * <code>startTime += System.currentTimeMillis() - pauseTime</code>
+ * </ul>
+ *
+ * Since the alpha object is no longer paused, this has the effect
+ * of resuming the interpolator as of the current time. If the
+ * alpha object is not paused when this method is called, then this
+ * method does nothing--the start time is not adjusted in this case.
+ *
+ * @since Java 3D 1.3
+ */
+ public void resume() {
+ resume(System.currentTimeMillis());
+ }
+
+ /**
+ * Resumes this alpha object as of the specified time. If the alpha
+ * object was paused, the difference between the specified
+ * time and the pause time will be used to adjust the startTime of
+ * this alpha. The equation is as follows:
+ *
+ * <ul><code>startTime += time - pauseTime</code></ul>
+ *
+ * Since the alpha object is no longer paused, this has the effect
+ * of resuming the interpolator as of the specified time. If the
+ * alpha object is not paused when this method is called, then this
+ * method does nothing--the start time is not adjusted in this case.
+ *
+ * @param time the time at which to resume the alpha
+ *
+ * @exception IllegalArgumentException if time <= 0
+ *
+ * @since Java 3D 1.3
+ */
+ public void resume(long time) {
+ if (time <= 0L) {
+ throw new IllegalArgumentException(J3dI18N.getString("Alpha0"));
+ }
+
+ if (paused) {
+ long newStartTime = startTime + time - pauseTime;
+ paused = false;
+ pauseTime = 0L;
+ setStartTime(newStartTime);
+ }
+ }
+
+ /**
+ * Returns true if this alpha object is paused.
+ * @return true if this alpha object is paused, false otherwise
+ *
+ * @since Java 3D 1.3
+ */
+ public boolean isPaused() {
+ return paused;
+ }
+
+ /**
+ * Returns the time at which this alpha was paused.
+ * @return the pause time; returns 0 if this alpha is not paused
+ *
+ * @since Java 3D 1.3
+ */
+ public long getPauseTime() {
+ return pauseTime;
+ }
+
+
+ /**
+ * This method returns a value between 0.0 and 1.0 inclusive,
+ * based on the current time and the time-to-alpha parameters
+ * established for this alpha. If this alpha object is paused,
+ * the value will be based on the pause time rather than the
+ * current time.
+ * This method will return the starting alpha value if the alpha
+ * has not yet started (that is, if the current time is less
+ * than startTime + triggerTime + phaseDelayDuration). This
+ * method will return the ending alpha value if the alpha has
+ * finished (that is, if the loop count has expired).
+ *
+ * @return a value between 0.0 and 1.0 based on the current time
+ */
+ public float value() {
+ long currentTime = paused ? pauseTime : System.currentTimeMillis();
+ return this.value(currentTime);
+ }
+
+ /**
+ * This method returns a value between 0.0 and 1.0 inclusive,
+ * based on the specified time and the time-to-alpha parameters
+ * established for this alpha.
+ * This method will return the starting alpha value if the alpha
+ * has not yet started (that is, if the specified time is less
+ * than startTime + triggerTime + phaseDelayDuration). This
+ * method will return the ending alpha value if the alpha has
+ * finished (that is, if the loop count has expired).
+ *
+ * @param atTime The time for which we wish to compute alpha
+ * @return a value between 0.0 and 1.0 based on the specified time
+ */
+ public float value(long atTime) {
+ float interpolatorTime
+ = (float)(atTime - startTime) * .001f; // startTime is in millisec
+ float alpha, a1, a2, dt, alphaRampDuration;
+
+ // System.out.println("alpha mode: " + mode);
+
+ // If non-looping and before start
+ // if ((loopCount != -1) &&
+ // interpolatorTime <= ( triggerTime + phaseDelay)) {
+ //
+ // if (( mode & INCREASING_ENABLE ) == 0 &&
+ // ( mode & DECREASING_ENABLE) != 0)
+ // alpha = 1.0f;
+ // else
+ // alpha = 0.0f;
+ // return alpha;
+ // }
+
+
+ // Case of {constantly} moving forward, snap back, forward again
+ if (( mode & INCREASING_ENABLE ) != 0 &&
+ ( mode & DECREASING_ENABLE) == 0) {
+
+ if(interpolatorTime <= (triggerTime + phaseDelay))
+ return 0.0f;
+
+ if((loopCount != -1) && (interpolatorTime >= stopTime))
+ return 1.0f;
+
+ // Constant velocity case
+ if (incAlphaRampInternal == 0.0f) {
+
+ alpha = mfmod((interpolatorTime - triggerTime - phaseDelay) +
+ 6.0f*( increasingAlpha + alphaAtOne),
+ (increasingAlpha + alphaAtOne))/ increasingAlpha;
+
+ if ( alpha > 1.0f) alpha = 1.0f;
+ return alpha;
+ }
+
+ // Ramped velocity case
+ alphaRampDuration = incAlphaRampInternal;
+
+ dt = mfmod((interpolatorTime - triggerTime - phaseDelay) +
+ 6.0f*( increasingAlpha + alphaAtOne),
+ ( increasingAlpha + alphaAtOne));
+ if (dt >= increasingAlpha) { alpha = 1.0f; return alpha; }
+
+ // Original equation kept to help understand
+ // computation logic - simplification saves
+ // a multiply and an add
+ // a1 = 1.0f/(alphaRampDuration*alphaRampDuration +
+ // ( increasingAlpha - 2*alphaRampDuration)*
+ // alphaRampDuration);
+
+ a1 = 1.0f/(increasingAlpha * alphaRampDuration -
+ alphaRampDuration * alphaRampDuration);
+
+ if (dt < alphaRampDuration) {
+ alpha = 0.5f*a1*dt*dt;
+ } else if (dt < increasingAlpha - alphaRampDuration) {
+ alpha = 0.5f*a1*alphaRampDuration*
+ alphaRampDuration +
+ (dt - alphaRampDuration)*a1*
+ alphaRampDuration;
+ } else {
+ alpha = a1*alphaRampDuration*alphaRampDuration +
+ ( increasingAlpha - 2.0f*alphaRampDuration)*a1*
+ alphaRampDuration -
+ 0.5f*a1*( increasingAlpha - dt)*
+ ( increasingAlpha - dt);
+ }
+ return alpha;
+
+ } else
+
+
+ // Case of {constantly} moving backward, snap forward, backward
+ // again
+ if (( mode & INCREASING_ENABLE ) == 0 &&
+ ( mode & DECREASING_ENABLE) != 0) {
+
+ // If non-looping and past end
+ // if ((loopCount != -1)
+ // && (interpolatorTime
+ // >= (triggerTime + phaseDelay + decreasingAlpha))) {
+ // alpha = 0.0f;
+ // return alpha;
+ // }
+
+ if(interpolatorTime <= (triggerTime + phaseDelay))
+ return 1.0f;
+
+ if((loopCount != -1) && (interpolatorTime >= stopTime) )
+ return 0.0f;
+
+
+
+ // Constant velocity case
+ if (decAlphaRampInternal == 0.0f) {
+ alpha = mfmod((interpolatorTime - triggerTime -
+ phaseDelay) +
+ 6.0f*( decreasingAlpha + alphaAtZero),
+ (decreasingAlpha + alphaAtZero))/ decreasingAlpha;
+ if ( alpha > 1.0f) { alpha = 0.0f; return alpha; }
+ alpha = 1.0f - alpha;
+ return alpha;
+ }
+
+ // Ramped velocity case
+ alphaRampDuration = decAlphaRampInternal;
+
+ dt = mfmod((interpolatorTime - triggerTime - phaseDelay) +
+ 6.0f*( decreasingAlpha + alphaAtZero),
+ ( decreasingAlpha + alphaAtZero));
+ if (dt >= decreasingAlpha) { alpha = 0.0f; return alpha; }
+
+ // Original equation kept to help understand
+ // computation logic - simplification saves
+ // a multiply and an add
+ // a1 = 1.0f/(alphaRampDuration*alphaRampDuration +
+ // ( decreasingAlpha - 2*alphaRampDuration)*
+ // alphaRampDuration);
+
+ a1 = 1.0f/(decreasingAlpha * alphaRampDuration -
+ alphaRampDuration * alphaRampDuration);
+
+ if (dt < alphaRampDuration) {
+ alpha = 0.5f*a1*dt*dt;
+ } else if (dt < decreasingAlpha - alphaRampDuration) {
+ alpha = 0.5f*a1*alphaRampDuration*
+ alphaRampDuration +
+ (dt - alphaRampDuration)*a1*
+ alphaRampDuration;
+ } else {
+ alpha = a1*alphaRampDuration*alphaRampDuration +
+ ( decreasingAlpha - 2.0f*alphaRampDuration)*a1*
+ alphaRampDuration -
+ 0.5f*a1*( decreasingAlpha - dt)*
+ ( decreasingAlpha - dt);
+ }
+ alpha = 1.0f - alpha;
+ return alpha;
+
+ } else
+
+
+ // Case of {osscilating} increasing and decreasing alpha
+ if (( mode & INCREASING_ENABLE) != 0 &&
+ ( mode & DECREASING_ENABLE) != 0) {
+
+ // If non-looping and past end
+ // if ((loopCount != -1) &&
+ // (interpolatorTime >=
+ // (triggerTime + phaseDelay + increasingAlpha +
+ // alphaAtOne + decreasingAlpha))) {
+ // alpha = 0.0f;
+ // return alpha;
+ // }
+
+
+ // If non-looping and past end, we always end up at zero since
+ // decreasing alpha has been requested.
+ if(interpolatorTime <= (triggerTime + phaseDelay))
+ return 0.0f;
+
+ if( (loopCount != -1) && (interpolatorTime >= stopTime))
+ return 0.0f;
+
+ // Constant velocity case
+ if (incAlphaRampInternal == 0.0f
+ && decAlphaRampInternal == 0.0f) {
+ dt = mfmod(interpolatorTime - triggerTime - phaseDelay +
+ 6.0f*(increasingAlpha + alphaAtOne +
+ decreasingAlpha + alphaAtZero),
+ increasingAlpha + alphaAtOne +
+ decreasingAlpha + alphaAtZero);
+ alpha = dt / increasingAlpha;
+ if ( alpha < 1.0f) return alpha;
+ // sub all increasing alpha time
+ dt -= increasingAlpha;
+ if (dt < alphaAtOne) { alpha = 1.0f; return alpha; }
+ // sub out alpha @ 1 time
+ dt -= alphaAtOne;
+ alpha = dt/ decreasingAlpha;
+ if ( alpha < 1.0f) alpha = 1.0f - alpha;
+ else alpha = 0.0f;
+ return alpha;
+ }
+
+ // Ramped velocity case
+ alphaRampDuration = incAlphaRampInternal;
+
+ // work around for bug 4308308
+ if (alphaRampDuration == 0.0f)
+ alphaRampDuration = .00001f;
+
+ dt = mfmod(interpolatorTime - triggerTime - phaseDelay +
+ 6.0f*( increasingAlpha + alphaAtOne +
+ decreasingAlpha + alphaAtZero),
+ increasingAlpha + alphaAtOne +
+ decreasingAlpha + alphaAtZero);
+ if (dt <= increasingAlpha) {
+
+ // Original equation kept to help understand
+ // computation logic - simplification saves
+ // a multiply and an add
+ // a1 = 1.0f/(alphaRampDuration*alphaRampDuration +
+ // ( increasingAlpha - 2*alphaRampDuration)*
+ // alphaRampDuration);
+
+ a1 = 1.0f/(increasingAlpha * alphaRampDuration -
+ alphaRampDuration * alphaRampDuration);
+
+ if (dt < alphaRampDuration) {
+ alpha = 0.5f*a1*dt*dt;
+ } else if (dt < increasingAlpha - alphaRampDuration) {
+ alpha = 0.5f*a1*alphaRampDuration*
+ alphaRampDuration +
+ (dt - alphaRampDuration)*a1*
+ alphaRampDuration;
+ } else {
+ alpha = a1*alphaRampDuration*alphaRampDuration+
+ ( increasingAlpha - 2.0f*alphaRampDuration)*a1*
+ alphaRampDuration -
+ 0.5f*a1*( increasingAlpha - dt)*
+ ( increasingAlpha - dt);
+ }
+ return alpha;
+ }
+ else if (dt <= increasingAlpha + alphaAtOne) {
+ alpha = 1.0f; return alpha;
+ }
+ else if (dt >= increasingAlpha + alphaAtOne + decreasingAlpha) {
+ alpha = 0.0f; return alpha;
+ }
+ else {
+ dt -= increasingAlpha + alphaAtOne;
+
+ alphaRampDuration = decAlphaRampInternal;
+
+ // work around for bug 4308308
+ if (alphaRampDuration == 0.0f)
+ alphaRampDuration = .00001f;
+
+ // Original equation kept to help understand
+ // computation logic - simplification saves
+ // a multiply and an add
+ // a1 = 1.0f/(alphaRampDuration*alphaRampDuration +
+ // ( decreasingAlpha - 2*alphaRampDuration)*
+ // alphaRampDuration);
+
+ a1 = 1.0f/(decreasingAlpha * alphaRampDuration -
+ alphaRampDuration * alphaRampDuration);
+
+ if (dt < alphaRampDuration) {
+ alpha = 0.5f*a1*dt*dt;
+ } else if (dt < decreasingAlpha - alphaRampDuration) {
+ alpha = 0.5f*a1*alphaRampDuration*
+ alphaRampDuration +
+ (dt - alphaRampDuration)*a1*
+ alphaRampDuration;
+ } else {
+ alpha =
+ a1*alphaRampDuration*alphaRampDuration +
+ (decreasingAlpha - 2.0f*alphaRampDuration)*a1*
+ alphaRampDuration -
+ 0.5f*a1*( decreasingAlpha - dt)*
+ (decreasingAlpha - dt);
+ }
+ alpha = 1.0f - alpha;
+ return alpha;
+ }
+
+ }
+ return 0.0f;
+ }
+
+ float mfmod(float a, float b) {
+ float fm, ta = (a), tb = (b);
+ int fmint;
+ if (tb < 0.0f) tb = -tb;
+ if (ta < 0.0f) ta = -ta;
+
+ fmint =(int)( ta/tb);
+ fm = ta - (float)fmint * tb;
+
+ if ((a) < 0.0f) return ((b) - fm);
+ else return fm;
+ }
+
+ /**
+ * Retrieves this alpha's startTime, the base
+ * for all relative time specifications; the default value
+ * for startTime is the system start time.
+ * @return this alpha's startTime.
+ */
+ public long getStartTime() {
+ return this.startTime;
+ }
+
+ /**
+ * Sets this alpha's startTime to that specified in the argument;
+ * startTime sets the base (or zero) for all relative time
+ * computations; the default value for startTime is the system
+ * start time.
+ * @param startTime the new startTime value
+ */
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ // This is used for passive wakeupOnElapsedFrame in
+ // Interpolator to restart behavior after alpha.finished()
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's loopCount.
+ * @return this alpha's loopCount.
+ */
+ public int getLoopCount() {
+ return this.loopCount;
+ }
+
+ /**
+ * Set this alpha's loopCount to that specified in the argument.
+ * @param loopCount the new loopCount value
+ */
+ public void setLoopCount(int loopCount) {
+ this.loopCount = loopCount;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's mode.
+ * @return this alpha's mode: any combination of
+ * INCREASING_ENABLE and DECREASING_ENABLE
+ */
+ public int getMode() {
+ return this.mode;
+ }
+
+ /**
+ * Set this alpha's mode to that specified in the argument.
+ * @param mode indicates whether the increasing alpha parameters or
+ * the decreasing alpha parameters or both are active. This parameter
+ * accepts the following values, INCREASING_ENABLE or
+ * DECREASING_ENABLE, which may be ORed together to specify
+ * that both are active.
+ * The increasing alpha parameters are increasingAlphaDuration,
+ * increasingAlphaRampDuration, and alphaAtOneDuration.
+ * The decreasing alpha parameters are decreasingAlphaDuration,
+ * decreasingAlphaRampDuration, and alphaAtZeroDuration.
+ */
+ public void setMode(int mode) {
+ this.mode = mode;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's triggerTime.
+ * @return this alpha's triggerTime.
+ */
+ public long getTriggerTime() {
+ return (long) (this.triggerTime * 1000f);
+ }
+
+ /**
+ * Set this alpha's triggerTime to that specified in the argument.
+ * @param triggerTime the new triggerTime
+ */
+ public void setTriggerTime(long triggerTime) {
+ this.triggerTime = (float) triggerTime * .001f;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's phaseDelayDuration.
+ * @return this alpha's phaseDelayDuration.
+ */
+ public long getPhaseDelayDuration() {
+ return (long)(this.phaseDelay * 1000f);
+ }
+
+ /**
+ * Set this alpha's phaseDelayDuration to that specified in
+ * the argument.
+ * @param phaseDelayDuration the new phaseDelayDuration
+ */
+ public void setPhaseDelayDuration(long phaseDelayDuration) {
+ this.phaseDelay = (float) phaseDelayDuration * .001f;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's increasingAlphaDuration.
+ * @return this alpha's increasingAlphaDuration.
+ */
+ public long getIncreasingAlphaDuration() {
+ return (long)(this.increasingAlpha * 1000f);
+ }
+
+ /**
+ * Set this alpha's increasingAlphaDuration to that specified in
+ * the argument.
+ * @param increasingAlphaDuration the new increasingAlphaDuration
+ */
+ public void setIncreasingAlphaDuration(long increasingAlphaDuration) {
+ this.increasingAlpha = (float) increasingAlphaDuration * .001f;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's increasingAlphaRampDuration.
+ * @return this alpha's increasingAlphaRampDuration.
+ */
+ public long getIncreasingAlphaRampDuration() {
+ return increasingAlphaRamp;
+ }
+
+ /**
+ * Set this alpha's increasingAlphaRampDuration to that specified
+ * in the argument.
+ * @param increasingAlphaRampDuration the new increasingAlphaRampDuration
+ */
+ public void setIncreasingAlphaRampDuration(long increasingAlphaRampDuration) {
+ increasingAlphaRamp = increasingAlphaRampDuration;
+ incAlphaRampInternal = (float) increasingAlphaRampDuration * .001f;
+ if (incAlphaRampInternal > (0.5f * increasingAlpha)) {
+ incAlphaRampInternal = 0.5f * increasingAlpha;
+ }
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's alphaAtOneDuration.
+ * @return this alpha's alphaAtOneDuration.
+ */
+ public long getAlphaAtOneDuration() {
+ return (long)(this.alphaAtOne * 1000f);
+ }
+
+ /**
+ * Set this alpha object's alphaAtOneDuration to the specified
+ * value.
+ * @param alphaAtOneDuration the new alphaAtOneDuration
+ */
+ public void setAlphaAtOneDuration(long alphaAtOneDuration) {
+ this.alphaAtOne = (float) alphaAtOneDuration * .001f;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's decreasingAlphaDuration.
+ * @return this alpha's decreasingAlphaDuration.
+ */
+ public long getDecreasingAlphaDuration() {
+ return (long)(this.decreasingAlpha * 1000f);
+ }
+
+ /**
+ * Set this alpha's decreasingAlphaDuration to that specified in
+ * the argument.
+ * @param decreasingAlphaDuration the new decreasingAlphaDuration
+ */
+ public void setDecreasingAlphaDuration(long decreasingAlphaDuration) {
+ this.decreasingAlpha = (float) decreasingAlphaDuration * .001f;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's decreasingAlphaRampDuration.
+ * @return this alpha's decreasingAlphaRampDuration.
+ */
+ public long getDecreasingAlphaRampDuration() {
+ return decreasingAlphaRamp;
+ }
+
+ /**
+ * Set this alpha's decreasingAlphaRampDuration to that specified
+ * in the argument.
+ * @param decreasingAlphaRampDuration the new decreasingAlphaRampDuration
+ */
+ public void setDecreasingAlphaRampDuration(long decreasingAlphaRampDuration) {
+ decreasingAlphaRamp = decreasingAlphaRampDuration;
+ decAlphaRampInternal = (float) decreasingAlphaRampDuration * .001f;
+ if (decAlphaRampInternal > (0.5f * decreasingAlpha)) {
+ decAlphaRampInternal = 0.5f * decreasingAlpha;
+ }
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Retrieves this alpha's alphaAtZeroDuration.
+ * @return this alpha's alphaAtZeroDuration.
+ */
+ public long getAlphaAtZeroDuration() {
+ return (long)(this.alphaAtZero * 1000f);
+ }
+
+ /**
+ * Set this alpha object's alphaAtZeroDuration to the specified
+ * value.
+ * @param alphaAtZeroDuration the new alphaAtZeroDuration
+ */
+ public void setAlphaAtZeroDuration(long alphaAtZeroDuration) {
+ this.alphaAtZero = (float) alphaAtZeroDuration * .001f;
+ computeStopTime();
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Query to test if this alpha object is past its activity window,
+ * that is, if it has finished looping.
+ * @return true if no longer looping, false otherwise
+ */
+ public boolean finished() {
+ long currentTime = paused ? pauseTime : System.currentTimeMillis();
+ return ((loopCount != -1) &&
+ ((float)(currentTime - startTime) * .001f > stopTime));
+ }
+
+ final private void computeStopTime() {
+ if (loopCount >= 0) {
+ float sum = 0;
+ if (( mode & INCREASING_ENABLE ) != 0) {
+ sum = increasingAlpha+alphaAtOne;
+ }
+ if ((mode & DECREASING_ENABLE) != 0) {
+ sum += decreasingAlpha+alphaAtZero;
+ }
+ stopTime = this.triggerTime + phaseDelay + loopCount*sum;
+ } else {
+ stopTime = 0;
+ }
+ }
+
+ /**
+ * This internal method returns a clone of the Alpha
+ *
+ * @return a duplicate of this Alpha
+ */
+ Alpha cloneAlpha() {
+ Alpha a = new Alpha();
+ a.setStartTime(getStartTime());
+ a.setLoopCount(getLoopCount());
+ a.setMode(getMode());
+ a.setTriggerTime(getTriggerTime());
+ a.setPhaseDelayDuration(getPhaseDelayDuration());
+ a.setIncreasingAlphaDuration(getIncreasingAlphaDuration());
+ a.setIncreasingAlphaRampDuration(getIncreasingAlphaRampDuration());
+ a.setAlphaAtOneDuration(getAlphaAtOneDuration());
+ a.setDecreasingAlphaDuration(getDecreasingAlphaDuration());
+ a.setDecreasingAlphaRampDuration(getDecreasingAlphaRampDuration());
+ a.setAlphaAtZeroDuration(getAlphaAtZeroDuration());
+ return a;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/AlternateAppearance.java b/src/classes/share/javax/media/j3d/AlternateAppearance.java
new file mode 100644
index 0000000..d6b38ad
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AlternateAppearance.java
@@ -0,0 +1,594 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+
+
+/**
+ * The AlternateAppearance leaf node is used for overriding the
+ * Appearance component of selected nodes. It defines an Appearance
+ * component object and a region of influence in which this
+ * AlternateAppearance node is active. An AlternateAppearance node
+ * also contains a list of Group nodes that specifies the hierarchical
+ * scope of this AlternateAppearance. If the scope list is empty,
+ * then the AlternateAppearance node has universe scope: all nodes
+ * within the region of influence are affected by this
+ * AlternateAppearance node. If the scope list is non-empty, then
+ * only those Leaf nodes under the Group nodes in the scope list are
+ * affected by this AlternateAppearance node (subject to the
+ * influencing bounds).
+ *
+ * <p>
+ * An AlternateAppearance node affects Shape3D and Morph nodes by
+ * overriding their appearance component with the appearance
+ * component in this AlternateAppearance node. Only those Shape3D and
+ * Morph nodes that explicitly allow their appearance to be
+ * overridden are affected. The AlternateAppearance node has no
+ * effect on Shape3D and Morph nodes that do not allow their
+ * appearance to be overridden.
+ *
+ * <p>
+ * If the regions of influence of multiple AlternateAppearance nodes
+ * overlap, the Java 3D system will choose a single alternate
+ * appearance for those objects that lie in the intersection. This is
+ * done in an implementation-dependent manner, but in general, the
+ * AlternateAppearance node that is "closest" to the object is chosen.
+ *
+ * @since Java 3D 1.2
+ */
+
+public class AlternateAppearance extends Leaf {
+ /**
+ * Specifies that this AlternateAppearance node allows read access to its
+ * influencing bounds and bounds leaf information.
+ */
+ public static final int ALLOW_INFLUENCING_BOUNDS_READ =
+ CapabilityBits.ALTERNATE_APPEARANCE_ALLOW_INFLUENCING_BOUNDS_READ;
+
+ /**
+ * Specifies that this AlternateAppearance node allows write access to its
+ * influencing bounds and bounds leaf information.
+ */
+ public static final int ALLOW_INFLUENCING_BOUNDS_WRITE =
+ CapabilityBits.ALTERNATE_APPEARANCE_ALLOW_INFLUENCING_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this AlternateAppearance node allows read access to
+ * its appearance information.
+ */
+ public static final int ALLOW_APPEARANCE_READ =
+ CapabilityBits.ALTERNATE_APPEARANCE_ALLOW_APPEARANCE_READ;
+
+ /**
+ * Specifies that this AlternateAppearance node allows write access to
+ * its appearance information.
+ * information.
+ */
+ public static final int ALLOW_APPEARANCE_WRITE =
+ CapabilityBits.ALTERNATE_APPEARANCE_ALLOW_APPEARANCE_WRITE;
+
+ /**
+ * Specifies that this AlternateAppearance node allows read access
+ * to its scope information at runtime.
+ */
+ public static final int ALLOW_SCOPE_READ =
+ CapabilityBits.ALTERNATE_APPEARANCE_ALLOW_SCOPE_READ;
+
+ /**
+ * Specifies that this AlternateAppearance node allows write access
+ * to its scope information at runtime.
+ */
+ public static final int ALLOW_SCOPE_WRITE =
+ CapabilityBits.ALTERNATE_APPEARANCE_ALLOW_SCOPE_WRITE;
+
+
+ /**
+ * Constructs an AlternateAppearance node with default
+ * parameters. The default values are as follows:
+ *
+ * <ul>
+ * appearance : null<br>
+ * scope : empty (universe scope)<br>
+ * influencing bounds : null<br>
+ * influencing bounding leaf : null<br>
+ * </ul>
+ */
+ public AlternateAppearance() {
+ // Just use the defaults
+ }
+
+
+ /**
+ * Creates the retained mode AlternateAppearanceRetained object that this
+ * Alternate Appearance component object will point to.
+ */
+ void createRetained() {
+ this.retained = new AlternateAppearanceRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Constructs an AlternateAppearance node with the specified appearance.
+ * @param appearance the appearance that is used for those nodes affected
+ * by this AlternateAppearance node.
+ */
+ public AlternateAppearance(Appearance appearance) {
+ ((AlternateAppearanceRetained)retained).initAppearance(appearance);
+ }
+
+
+ /**
+ * Sets the appearance of this AlternateAppearance node.
+ * This appearance overrides the appearance in those Shape3D and
+ * Morph nodes affected by this AlternateAppearance node.
+ * @param appearance the new appearance.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAppearance(Appearance appearance) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPEARANCE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance0"));
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).setAppearance(appearance);
+ else
+ ((AlternateAppearanceRetained)this.retained).initAppearance(appearance);
+ }
+
+
+ /**
+ * Retrieves the appearance from this AlternateAppearance node.
+ * @return the current appearance.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Appearance getAppearance() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPEARANCE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance2"));
+
+ return ((AlternateAppearanceRetained)this.retained).getAppearance();
+
+ }
+
+ /**
+ * Sets the AlternateAppearance's influencing region to the specified
+ * bounds.
+ * This is used when the influencing bounding leaf is set to null.
+ * @param region the bounds that contains the AlternateAppearance's
+ * new influencing region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance3"));
+
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).setInfluencingBounds(region);
+ else
+ ((AlternateAppearanceRetained)this.retained).initInfluencingBounds(region);
+ }
+
+ /**
+ * Retrieves the AlternateAppearance node's influencing bounds.
+ * @return this AlternateAppearance's influencing bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getInfluencingBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance4"));
+
+
+ return ((AlternateAppearanceRetained)this.retained).getInfluencingBounds();
+ }
+
+
+ /**
+ * Sets the AlternateAppearance's influencing region to the specified
+ * bounding leaf.
+ * When set to a value other than null, this overrides the influencing
+ * bounds object.
+ * @param region the bounding leaf node used to specify the
+ * AlternateAppearance node's new influencing region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance3"));
+
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).setInfluencingBoundingLeaf(region);
+ else
+ ((AlternateAppearanceRetained)this.retained).initInfluencingBoundingLeaf(region);
+ }
+
+
+ /**
+ * Retrieves the AlternateAppearance node's influencing bounding leaf.
+ * @return this AlternateAppearance's influencing bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getInfluencingBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance4"));
+
+
+ return ((AlternateAppearanceRetained)this.retained).getInfluencingBoundingLeaf();
+ }
+
+
+ /**
+ * Replaces the node at the specified index in this
+ * AlternateAppearance node's
+ * list of scopes with the specified Group node.
+ * By default, AlternateAppearance nodes are scoped only by their
+ * influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be stored at the specified index.
+ * @param index the index of the Group node to be replaced.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void setScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance7"));
+
+
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).setScope(scope, index);
+ else
+ ((AlternateAppearanceRetained)this.retained).initScope(scope, index);
+ }
+
+
+ /**
+ * Retrieves the Group node at the specified index from
+ * this AlternateAppearance node's list of scopes.
+ * @param index the index of the Group node to be returned.
+ * @return the Group node at the specified index.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Group getScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance8"));
+
+
+ return ((AlternateAppearanceRetained)this.retained).getScope(index);
+ }
+
+
+ /**
+ * Inserts the specified Group node into this AlternateAppearance node's
+ * list of scopes at the specified index.
+ * By default, AlternateAppearance nodes are scoped only by their
+ * influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be inserted at the specified index.
+ * @param index the index at which the Group node is inserted.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void insertScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance9"));
+
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).insertScope(scope, index);
+ else
+ ((AlternateAppearanceRetained)this.retained).initInsertScope(scope, index);
+ }
+
+
+ /**
+ * Removes the node at the specified index from this AlternateAppearance
+ * node's
+ * list of scopes. If this operation causes the list of scopes to
+ * become empty, then this AlternateAppearance will have universe scope:
+ * all nodes
+ * within the region of influence will be affected by this
+ * AlternateAppearance node.
+ * @param index the index of the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the group node at the
+ * specified index is part of a compiled scene graph
+ */
+ public void removeScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance10"));
+
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).removeScope(index);
+ else
+ ((AlternateAppearanceRetained)this.retained).initRemoveScope(index);
+ }
+
+
+ /**
+ * Returns an enumeration of this AlternateAppearance node's list
+ * of scopes.
+ * @return an Enumeration object containing all nodes in this
+ * AlternateAppearance node's list of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Enumeration getAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance11"));
+
+
+ return (Enumeration) ((AlternateAppearanceRetained)this.retained).getAllScopes();
+ }
+
+
+ /**
+ * Appends the specified Group node to this AlternateAppearance node's
+ * list of scopes.
+ * By default, AlternateAppearance nodes are scoped only by their
+ * influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be appended.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void addScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance12"));
+
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).addScope(scope);
+ else
+ ((AlternateAppearanceRetained)this.retained).initAddScope(scope);
+ }
+
+
+ /**
+ * Returns the number of nodes in this AlternateAppearance node's list
+ * of scopes.
+ * If this number is 0, then the list of scopes is empty and this
+ * AlternateAppearance node has universe scope: all nodes within the
+ * region of
+ * influence are affected by this AlternateAppearance node.
+ * @return the number of nodes in this AlternateAppearance node's list
+ * of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int numScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance11"));
+
+
+ return ((AlternateAppearanceRetained)this.retained).numScopes();
+ }
+
+
+ /**
+ * Retrieves the index of the specified Group node in this
+ * AlternateAppearance node's list of scopes.
+ *
+ * @param scope the Group node to be looked up.
+ * @return the index of the specified Group node;
+ * returns -1 if the object is not in the list.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance8"));
+
+ return ((AlternateAppearanceRetained)this.retained).indexOfScope(scope);
+ }
+
+
+ /**
+ * Removes the specified Group node from this AlternateAppearance
+ * node's list of scopes. If the specified object is not in the
+ * list, the list is not modified. If this operation causes the
+ * list of scopes to become empty, then this AlternateAppearance
+ * will have universe scope: all nodes within the region of
+ * influence will be affected by this AlternateAppearance node.
+ *
+ * @param scope the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance10"));
+
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).removeScope(scope);
+ else
+ ((AlternateAppearanceRetained)this.retained).initRemoveScope(scope);
+ }
+
+
+ /**
+ * Removes all Group nodes from this AlternateAppearance node's
+ * list of scopes. The AlternateAppearance node will then have
+ * universe scope: all nodes within the region of influence will
+ * be affected by this AlternateAppearance node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if any group node in this
+ * node's list of scopes is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AlternateAppearance10"));
+ if (isLive())
+ ((AlternateAppearanceRetained)this.retained).removeAllScopes();
+ else
+ ((AlternateAppearanceRetained)this.retained).initRemoveAllScopes();
+ }
+
+
+ /**
+ * Copies all AlternateAppearance 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ AlternateAppearanceRetained attr = (AlternateAppearanceRetained)
+ originalNode.retained;
+ AlternateAppearanceRetained rt = (AlternateAppearanceRetained) retained;
+
+ rt.initAppearance((Appearance) getNodeComponent(
+ attr.getAppearance(),
+ forceDuplicate,
+ originalNode.nodeHashtable));
+
+ rt.initInfluencingBounds(attr.getInfluencingBounds());
+
+ Enumeration elm = attr.getAllScopes();
+ while (elm.hasMoreElements()) {
+ // this reference will set correctly in updateNodeReferences() callback
+ rt.initAddScope((Group) elm.nextElement());
+ }
+
+ // correct value will set in updateNodeReferences
+ rt.initInfluencingBoundingLeaf(attr.getInfluencingBoundingLeaf());
+
+ }
+
+ /**
+ * Callback used to allow a node to check if any nodes referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any node references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding Node in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * node is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+
+ AlternateAppearanceRetained rt = (AlternateAppearanceRetained)
+ retained;
+
+ BoundingLeaf bl = rt.getInfluencingBoundingLeaf();
+
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.initInfluencingBoundingLeaf((BoundingLeaf) o);
+ }
+
+ int num = rt.numScopes();
+ for (int i=0; i < num; i++) {
+ rt.initScope((Group) referenceTable.
+ getNewObjectReference(rt.getScope(i)), i);
+ }
+ }
+
+
+ /**
+ * 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) {
+ AlternateAppearance app = new AlternateAppearance();
+ app.duplicateNode(this, forceDuplicate);
+ return app;
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java b/src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java
new file mode 100644
index 0000000..692c4a2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java
@@ -0,0 +1,862 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+import javax.vecmath.*;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.ArrayList;
+
+
+class AlternateAppearanceRetained extends LeafRetained {
+
+
+ // Statics used when something in the alternate app changes
+ static final int APPEARANCE_CHANGED = 0x0001;
+ static final int SCOPE_CHANGED = 0x0002;
+ static final int BOUNDS_CHANGED = 0x0004;
+ static final int BOUNDINGLEAF_CHANGED = 0x0008;
+ static final int INIT_MIRROR = 0x0010; // setLive
+ static final int CLEAR_MIRROR = 0x0020; // clearLive
+
+
+ /**
+ * The Boundary object defining the lights's region of influence.
+ */
+ Bounds regionOfInfluence = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * Vector of GroupRetained nodes that scopes this alternate app .
+ */
+ Vector scopes = new Vector();
+
+ // This is true when this alternate app is referenced in an immediate mode context
+ boolean inImmCtx = false;
+
+ // Target threads to be notified when light changes
+ static final int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+ // Boolean to indicate if this object is scoped (only used for mirror objects
+ boolean isScoped = false;
+
+ // The object that contains the dynamic HashKey - a string type object
+ // Used in scoping
+ HashKey tempKey = new HashKey(250);
+
+ /**
+ * The transformed value of the applicationRegion.
+ */
+ Bounds region = null;
+
+ /**
+ * mirror Alternate appearance
+ */
+ AlternateAppearanceRetained mirrorAltApp = null;
+
+ /**
+ * Appearance for this object
+ */
+ AppearanceRetained appearance;
+
+ /**
+ * A reference to the scene graph alternateApp
+ */
+ AlternateAppearanceRetained sgAltApp = null;
+
+ /**
+ * Is true, if the mirror altapp is viewScoped
+ */
+ boolean isViewScoped = false;
+
+ AlternateAppearanceRetained() {
+ this.nodeType = NodeRetained.ALTERNATEAPPEARANCE;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ /**
+ * Initializes the appearance
+ */
+ void initAppearance(Appearance app) {
+ if (app != null)
+ appearance = (AppearanceRetained) app.retained;
+ else
+ appearance = null;
+ }
+
+
+ /**
+ * sets the appearance and send a message
+ */
+ void setAppearance(Appearance app) {
+ if (appearance != null)
+ synchronized(appearance.liveStateLock) {
+ appearance.clearLive(refCount);
+ }
+ initAppearance(app);
+ if (appearance != null) {
+ synchronized(appearance.liveStateLock) {
+ appearance.setLive(inBackgroundGroup, refCount);
+ }
+ }
+ // There is no need to clone the appearance's mirror
+ sendMessage(APPEARANCE_CHANGED,
+ (appearance != null ? appearance.mirror: null));
+ }
+
+
+
+ Appearance getAppearance() {
+ return (appearance == null ? null: (Appearance) appearance.source);
+ }
+
+
+ /**
+ * Set the alternate's region of influence.
+ */
+ void initInfluencingBounds(Bounds region) {
+ if (region != null) {
+ this.regionOfInfluence = (Bounds) region.clone();
+ } else {
+ this.regionOfInfluence = null;
+ }
+ }
+
+ /**
+ * Set the alternate's region of influence and send message
+ */
+ void setInfluencingBounds(Bounds region) {
+ initInfluencingBounds(region);
+ sendMessage(BOUNDS_CHANGED,
+ (region != null ? region.clone() : null));
+ }
+
+ /**
+ * Get the alternate's region of Influence.
+ */
+ Bounds getInfluencingBounds() {
+ return (regionOfInfluence != null ?
+ (Bounds) regionOfInfluence.clone() : null);
+ }
+
+ /**
+ * Set the alternate's region of influence to the specified Leaf node.
+ */
+ void initInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ } else {
+ boundingLeaf = null;
+ }
+ }
+
+ /**
+ * Set the alternate's region of influence to the specified Leaf node.
+ */
+ void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (boundingLeaf != null)
+ boundingLeaf.mirrorBoundingLeaf.removeUser(mirrorAltApp);
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorAltApp);
+ } else {
+ boundingLeaf = null;
+ }
+ sendMessage(BOUNDINGLEAF_CHANGED,
+ (boundingLeaf != null ?
+ boundingLeaf.mirrorBoundingLeaf : null));
+ }
+
+ /**
+ * Get the alternate's region of influence.
+ */
+ BoundingLeaf getInfluencingBoundingLeaf() {
+ return (boundingLeaf != null ?
+ (BoundingLeaf)boundingLeaf.source : null);
+ }
+
+
+
+ /**
+ * Replaces the specified scope with the scope provided.
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void initScope(Group scope, int index) {
+ scopes.setElementAt((GroupRetained)(scope.retained), index);
+
+ }
+
+ /**
+ * Replaces the specified scope with the scope provided.
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void setScope(Group scope, int index) {
+
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group;
+ ArrayList addScopeList = new ArrayList();
+ Object[] scopeInfo = new Object[3];
+
+ group = (GroupRetained) scopes.get(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedAltApp(mirrorAltApp, removeScopeList, tempKey);
+
+ group = (GroupRetained)scope.retained;
+ initScope(scope, index);
+ tempKey.reset();
+
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ group.addAllNodesForScopedAltApp(mirrorAltApp,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE:Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ Group getScope(int index) {
+ return (Group)(((GroupRetained)(scopes.elementAt(index))).source);
+ }
+
+
+ /**
+ * Inserts the specified scope at specified index.before the
+ * alt app is live
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void initInsertScope(Node scope, int index) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ scopes.insertElementAt((GroupRetained)(scope.retained), index);
+ group.setAltAppScope();
+ }
+
+ /**
+ * Inserts the specified scope at specified index and sends
+ * a message
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void insertScope(Node scope, int index) {
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scope.retained;
+
+ initInsertScope(scope, index);
+ group = (GroupRetained)scope.retained;
+ tempKey.reset();
+ group.addAllNodesForScopedAltApp(mirrorAltApp,addScopeList, tempKey);
+
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+
+
+ void initRemoveScope(int index) {
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+ scopes.removeElementAt(index);
+ group.removeAltAppScope();
+
+ }
+
+ void removeScope(int index) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+
+ tempKey.reset();
+ group.removeAllNodesForScopedAltApp(mirrorAltApp, removeScopeList, tempKey);
+
+ initRemoveScope(index);
+ scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ /**
+ * Removes the specified Group node from this node's list of scopes.
+ * Method is a no-op if the
+ * specified node is not found
+ * @param The Group node to be removed
+ */
+ void removeScope(Group scope) {
+ int ind = indexOfScope(scope);
+ if(ind >= 0)
+ removeScope(ind);
+ }
+
+ void initRemoveScope(Group scope) {
+ int ind = indexOfScope(scope);
+ if(ind >= 0)
+ initRemoveScope(ind);
+ }
+
+ void removeAllScopes() {
+ GroupRetained group;
+ ArrayList removeScopeList = new ArrayList();
+ int n = scopes.size();
+ for(int index = n-1; index >= 0; index--) {
+ group = (GroupRetained)scopes.elementAt(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedAltApp(mirrorAltApp, removeScopeList, tempKey);
+ initRemoveScope(index);
+ }
+ Object[] scopeInfo = new Object[3];
+ scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ void initRemoveAllScopes() {
+ int n = scopes.size();
+ for(int i = n-1; i >= 0; i--)
+ initRemoveScope(i);
+ }
+
+ /**
+ * Returns an enumeration object of the scoperen.
+ * @return an enumeration object of the scoperen
+ */
+ Enumeration getAllScopes() {
+ Enumeration elm = scopes.elements();
+ Vector v = new Vector(scopes.size());
+ while (elm.hasMoreElements()) {
+ v.add( ((GroupRetained) elm.nextElement()).source);
+ }
+ return v.elements();
+ }
+
+ /**
+ * Returns the index of the specified Group node in this node's list of scopes.
+ * @param scope the Group node whose index is needed
+ */
+ int indexOfScope(Group scope) {
+ if(scope != null)
+ return scopes.indexOf((GroupRetained)scope.retained);
+ else
+ return scopes.indexOf(null);
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes before
+ * the alt app is alive
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void initAddScope(Group scope) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ scopes.addElement((GroupRetained)(scope.retained));
+ group.setAltAppScope();
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes.
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void addScope(Group scope) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scope.retained;
+
+ initAddScope(scope);
+ tempKey.reset();
+ group.addAllNodesForScopedAltApp(mirrorAltApp,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+
+
+ /**
+ * Returns a count of this nodes' scopes.
+ * @return the number of scopes descendant from this node
+ */
+ int numScopes() {
+ return scopes.size();
+ }
+
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+ boolean isScoped() {
+ return (scopes != null);
+ }
+
+
+ void updateImmediateMirrorObject(Object[] objs) {
+ GroupRetained group;
+ Vector currentScopes;
+ int i, nscopes;
+ Transform3D trans;
+
+ int component = ((Integer)objs[1]).intValue();
+ if ((component & APPEARANCE_CHANGED) != 0) {
+ mirrorAltApp.appearance = (AppearanceRetained)objs[2];
+ }
+ if ((component & BOUNDS_CHANGED) != 0) {
+ mirrorAltApp.regionOfInfluence = (Bounds) objs[2];
+ if (mirrorAltApp.boundingLeaf == null) {
+ if (objs[2] != null) {
+ mirrorAltApp.region = (Bounds)mirrorAltApp.regionOfInfluence.copy(mirrorAltApp.region);
+ mirrorAltApp.region.transform(
+ mirrorAltApp.regionOfInfluence,
+ getCurrentLocalToVworld());
+ }
+ else {
+ mirrorAltApp.region = null;
+ }
+ }
+ }
+ else if ((component & BOUNDINGLEAF_CHANGED) != 0) {
+ mirrorAltApp.boundingLeaf = (BoundingLeafRetained)objs[2];
+ if (objs[2] != null) {
+ mirrorAltApp.region = (Bounds)mirrorAltApp.boundingLeaf.transformedRegion;
+ }
+ else {
+ if (mirrorAltApp.regionOfInfluence != null) {
+ mirrorAltApp.region = mirrorAltApp.regionOfInfluence.copy(mirrorAltApp.region);
+ mirrorAltApp.region.transform(
+ mirrorAltApp.regionOfInfluence,
+ getCurrentLocalToVworld());
+ }
+ else {
+ mirrorAltApp.region = null;
+ }
+
+ }
+ }
+ else if ((component & SCOPE_CHANGED) != 0) {
+ Object[] scopeList = (Object[])objs[2];
+ ArrayList addList = (ArrayList)scopeList[0];
+ ArrayList removeList = (ArrayList)scopeList[1];
+ boolean isScoped = ((Boolean)scopeList[2]).booleanValue();
+
+ if (addList != null) {
+ mirrorAltApp.isScoped = isScoped;
+ for (i = 0; i < addList.size(); i++) {
+ Shape3DRetained obj = ((GeometryAtom)addList.get(i)).source;
+ obj.addAltApp(mirrorAltApp);
+ }
+ }
+
+ if (removeList != null) {
+ mirrorAltApp.isScoped = isScoped;
+ for (i = 0; i < removeList.size(); i++) {
+ Shape3DRetained obj = ((GeometryAtom)removeList.get(i)).source;
+ obj.removeAltApp(mirrorAltApp);
+ }
+ }
+ }
+
+
+ }
+
+
+ /** Note: This routine will only be called on
+ * the mirror object - will update the object's
+ * cached region and transformed region
+ */
+
+ void updateBoundingLeaf() {
+ if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) {
+ region = boundingLeaf.transformedRegion;
+ } else {
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence, getCurrentLocalToVworld());
+ } else {
+ region = null;
+ }
+ }
+ }
+
+ void setLive(SetLiveState s) {
+ GroupRetained group;
+ Vector currentScopes;
+ int i, nscopes;
+ TransformGroupRetained[] tlist;
+
+ if (inImmCtx) {
+ throw new IllegalSharingException(J3dI18N.getString("AlternateAppearanceRetained13"));
+ }
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("AlternateAppearanceRetained15"));
+ }
+
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("AlternateAppearanceRetained16"));
+ }
+
+ super.doSetLive(s);
+
+ if (appearance != null) {
+ if (appearance.getInImmCtx()) {
+ throw new IllegalSharingException(J3dI18N.getString("AlternateAppearanceRetained14"));
+ }
+ synchronized(appearance.liveStateLock) {
+ appearance.setLive(inBackgroundGroup, s.refCount);
+ }
+ }
+
+ // Create the mirror object
+ // Initialization of the mirror object during the INSERT_NODE
+ // message (in updateMirrorObject)
+ if (mirrorAltApp == null) {
+ mirrorAltApp = (AlternateAppearanceRetained)this.clone();
+ // Assign the bounding leaf of this mirror object as null
+ // it will later be assigned to be the mirror of the alternate app
+ // bounding leaf object
+ mirrorAltApp.boundingLeaf = null;
+ mirrorAltApp.sgAltApp = this;
+ }
+ // If bounding leaf is not null, add the mirror object as a user
+ // so that any changes to the bounding leaf will be received
+ if (boundingLeaf != null) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorAltApp);
+ }
+
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(mirrorAltApp);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(mirrorAltApp);
+ }
+
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorAltApp,
+ Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorAltApp, Targets.ENV_TARGETS);
+ }
+ mirrorAltApp.switchState = (SwitchState)s.switchStates.get(0);
+
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+
+ // At the end make it live
+ super.markAsLive();
+
+ // Initialize the mirror object, this needs to be done, when
+ // renderBin is not accessing any of the fields
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.ALTERNATEAPPEARANCE_CHANGED;
+ createMessage.args[0] = this;
+ // a snapshot of all attributes that needs to be initialized
+ // in the mirror object
+ createMessage.args[1]= new Integer(INIT_MIRROR);
+ ArrayList addScopeList = new ArrayList();
+ for (i = 0; i < scopes.size(); i++) {
+ group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.addAllNodesForScopedAltApp(mirrorAltApp, addScopeList, tempKey);
+ }
+ Object[] scopeInfo = new Object[2];
+ scopeInfo[0] = ((scopes.size() > 0) ? Boolean.TRUE:Boolean.FALSE);
+ scopeInfo[1] = addScopeList;
+ createMessage.args[2] = scopeInfo;
+ if (appearance != null) {
+ createMessage.args[3] = appearance.mirror;
+ }
+ else {
+ createMessage.args[3] = null;
+ }
+ Object[] obj = new Object[2];
+ obj[0] = boundingLeaf;
+ obj[1] = (regionOfInfluence != null?regionOfInfluence.clone():null);
+ createMessage.args[4] = obj;
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+
+
+
+ }
+
+ /**
+ * This is called on the parent object
+ */
+ void initMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ Object[] scopeInfo = (Object[]) args[2];
+ Boolean scoped = (Boolean)scopeInfo[0];
+ ArrayList shapeList = (ArrayList)scopeInfo[1];
+ AppearanceRetained app = (AppearanceRetained)args[3];
+ BoundingLeafRetained bl=(BoundingLeafRetained)((Object[])args[4])[0];
+ Bounds bnds = (Bounds)((Object[])args[4])[1];
+
+ for (int i = 0; i < shapeList.size(); i++) {
+ shape = ((GeometryAtom)shapeList.get(i)).source;
+ shape.addAltApp(mirrorAltApp);
+ }
+ mirrorAltApp.isScoped = scoped.booleanValue();
+
+ if (app != null)
+ mirrorAltApp.appearance = app;
+
+ if (bl != null) {
+ mirrorAltApp.boundingLeaf = bl.mirrorBoundingLeaf;
+ mirrorAltApp.region = boundingLeaf.transformedRegion;
+ } else {
+ mirrorAltApp.boundingLeaf = null;
+ mirrorAltApp.region = null;
+ }
+
+ if (bnds != null) {
+ mirrorAltApp.regionOfInfluence = bnds;
+ if (mirrorAltApp.region == null) {
+ mirrorAltApp.region = (Bounds)regionOfInfluence.clone();
+ mirrorAltApp.region.transform(regionOfInfluence, getLastLocalToVworld());
+ }
+ }
+ else {
+ mirrorAltApp.regionOfInfluence = null;
+ }
+
+ }
+
+ void clearMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ ArrayList shapeList = (ArrayList)args[2];
+ ArrayList removeScopeList = new ArrayList();
+
+ for (int i = 0; i < shapeList.size(); i++) {
+ shape = ((GeometryAtom)shapeList.get(i)).source;
+ shape.removeAltApp(mirrorAltApp);
+ }
+ mirrorAltApp.isScoped = false;
+
+
+
+ }
+
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it removes itself to the list of alt app
+ */
+ void clearLive(SetLiveState s) {
+ int i, j;
+ GroupRetained group;
+
+ if (appearance != null) {
+ synchronized(appearance.liveStateLock) {
+ appearance.clearLive(s.refCount);
+ }
+ }
+
+ super.clearLive(s);
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+
+ // Remove this mirror light as users of the bounding leaf
+ if (mirrorAltApp.boundingLeaf != null)
+ mirrorAltApp.boundingLeaf.removeUser(mirrorAltApp);
+
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(mirrorAltApp);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(mirrorAltApp);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorAltApp,
+ Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorAltApp, Targets.ENV_TARGETS);
+ }
+
+
+ if (scopes.size() > 0) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.ALTERNATEAPPEARANCE_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(CLEAR_MIRROR);
+ ArrayList removeScopeList = new ArrayList();
+ for (i = 0; i < scopes.size(); i++) {
+ group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.removeAllNodesForScopedAltApp(mirrorAltApp, removeScopeList, tempKey);
+ }
+ createMessage.args[2] = removeScopeList;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+
+
+ void updateTransformChange() {
+ }
+
+ /**
+ * Called on mirror object
+ */
+ void updateImmediateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (boundingLeaf == null) {
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence,
+ sgAltApp.getCurrentLocalToVworld());
+ }
+
+ }
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.ALTERNATEAPPEARANCE_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ leafList.add(mirrorAltApp);
+ }
+
+
+ /**
+ * Copies all AlternateAppearance 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ throw new RuntimeException("method not implemented");
+
+// super.duplicateAttributes(originalNode, forceDuplicate);
+
+// AlternateAppearance alternate appearance = (AlternateAppearance) originalNode;
+
+// // TODO: clone appearance
+
+// setInfluencingBounds(alternate appearance.getInfluencingBounds());
+
+// Enumeration elm = alternate appearance.getAllScopes();
+// while (elm.hasMoreElements()) {
+// // this reference will set correctly in updateNodeReferences() callback
+// addScope((Group) elm.nextElement());
+// }
+
+// // this reference will set correctly in updateNodeReferences() callback
+// setInfluencingBoundingLeaf(alternate appearance.getInfluencingBoundingLeaf());
+ }
+
+// /**
+// * Callback used to allow a node to check if any nodes referenced
+// * by that node have been duplicated via a call to <code>cloneTree</code>.
+// * This method is called by <code>cloneTree</code> after all nodes in
+// * the sub-graph have been duplicated. The cloned Leaf node's method
+// * will be called and the Leaf node can then look up any node references
+// * by using the <code>getNewObjectReference</code> method found in the
+// * <code>NodeReferenceTable</code> object. If a match is found, a
+// * reference to the corresponding Node in the newly cloned sub-graph
+// * is returned. If no corresponding reference is found, either a
+// * DanglingReferenceException is thrown or a reference to the original
+// * node is returned depending on the value of the
+// * <code>allowDanglingReferences</code> parameter passed in the
+// * <code>cloneTree</code> call.
+// * <p>
+// * NOTE: Applications should <i>not</i> call this method directly.
+// * It should only be called by the cloneTree method.
+// *
+// * @param referenceTable a NodeReferenceTableObject that contains the
+// * <code>getNewObjectReference</code> method needed to search for
+// * new object instances.
+// * @see NodeReferenceTable
+// * @see Node#cloneTree
+// * @see DanglingReferenceException
+// */
+// public void updateNodeReferences(NodeReferenceTable referenceTable) {
+// throw new RuntimeException("method not implemented");
+//
+// Object o;
+//
+// BoundingLeaf bl = getInfluencingBoundingLeaf();
+// if (bl != null) {
+// o = referenceTable.getNewObjectReference(bl);
+// setInfluencingBoundingLeaf((BoundingLeaf) o);
+// }
+//
+// for (int i=0; i < numScopes(); i++) {
+// o = referenceTable.getNewObjectReference(getScope(i));
+// setScope((Group) o, i);
+// }
+// }
+
+}
diff --git a/src/classes/share/javax/media/j3d/AmbientLight.java b/src/classes/share/javax/media/j3d/AmbientLight.java
new file mode 100644
index 0000000..71106fd
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AmbientLight.java
@@ -0,0 +1,90 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+
+/**
+ * An ambient light source object. Ambient light is that light
+ * that seems to come from all directions. The AmbientLight object
+ * has the same attributes as a Light node, including color,
+ * influencing bounds, scopes, and
+ * a flag indicating whether this light source is on or off.
+ * Ambient reflections do not depend on the orientation or
+ * position of a surface.
+ * Ambient light has only an ambient reflection component.
+ * It does not have diffuse or specular reflection components.
+ * <p>
+ * For more information on Java 3D lighting, see the class description
+ * for Light.
+ * <p>
+ */
+
+public class AmbientLight extends Light {
+ /**
+ * Constructs and initializes an ambient light using default parameters.
+ */
+ public AmbientLight() {
+ }
+
+
+ /**
+ * Constructs and initializes an ambient light using the specified
+ * parameters.
+ * @param color the color of the light source.
+ */
+ public AmbientLight(Color3f color) {
+ super(color);
+ }
+
+
+ /**
+ * Constructs and initializes an ambient light using the specified
+ * parameters.
+ * @param lightOn flag indicating whether this light is on or off.
+ * @param color the color of the light source.
+ */
+ public AmbientLight(boolean lightOn, Color3f color) {
+ super(lightOn, color);
+ }
+
+ /**
+ * Creates the retained mode AmbientLightRetained object that this
+ * AmbientLight component object will point to.
+ */
+ void createRetained() {
+ this.retained = new AmbientLightRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * 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) {
+ AmbientLight a = new AmbientLight();
+ a.duplicateNode(this, forceDuplicate);
+ return a;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/AmbientLightRetained.java b/src/classes/share/javax/media/j3d/AmbientLightRetained.java
new file mode 100644
index 0000000..098566f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AmbientLightRetained.java
@@ -0,0 +1,39 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * An ambient light source object.
+ */
+
+class AmbientLightRetained extends LightRetained {
+
+ AmbientLightRetained() {
+ this.nodeType = NodeRetained.AMBIENTLIGHT;
+ lightType = 1;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ void setLive(SetLiveState s) {
+ super.setLive(s);
+ J3dMessage createMessage = super.initMessage(7);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void update(long ctx, int lightSlot, double scale) {
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Appearance.java b/src/classes/share/javax/media/j3d/Appearance.java
new file mode 100644
index 0000000..ed5f12b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Appearance.java
@@ -0,0 +1,927 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+
+/**
+ * The Appearance object defines all rendering state that can be set
+ * as a component object of a Shape3D node. The rendering state
+ * consists of the following:<p>
+ * <ul>
+ * <li>Coloring attributes - defines attributes used in color selection
+ * and shading. These attributes are defined in a ColoringAttributes
+ * object.</li><p>
+ *
+ * <li>Line attributes - defines attributes used to define lines, including
+ * the pattern, width, and whether antialiasing is to be used. These
+ * attributes are defined in a LineAttributes object.</li><p>
+ *
+ * <li>Point attributes - defines attributes used to define points,
+ * including the size and whether antialiasing is to be used. These
+ * attributes are defined in a PointAttributes object.</li><p>
+ *
+ * <li>Polygon attributes - defines the attributes used to define
+ * polygons, including culling, rasterization mode (filled, lines,
+ * or points), constant offset, offset factor, and whether back
+ * back facing normals are flipped. These attributes are defined
+ * in a PolygonAttributes object.</li><p>
+ *
+ * <li>Rendering attributes - defines rendering operations,
+ * including the alpha test function and test value, the raster
+ * operation, whether vertex colors are ignored, whether invisible
+ * objects are rendered, and whether the depth buffer is enabled.
+ * These attributes are defined in a RenderingAttributes
+ * object.</li><p>
+ *
+ * <li>Transparency attributes - defines the attributes that affect
+ * transparency of the object, such as the transparency mode
+ * (blended, screen-door), blending function (used in transparency
+ * and antialiasing operations), and a blend value that defines
+ * the amount of transparency to be applied to this Appearance
+ * component object.</li><p>
+ *
+ * <li>Material - defines the appearance of an object under illumination,
+ * such as the ambient color, diffuse color, specular color, emissive
+ * color, and shininess. These attributes are defined in a Material
+ * object.</li><p>
+ *
+ * <li>Texture - defines the texture image and filtering
+ * parameters used when texture mapping is enabled. These attributes
+ * are defined in a Texture object.</li><p>
+ *
+ * <li>Texture attributes - defines the attributes that apply to
+ * texture mapping, such as the texture mode, texture transform,
+ * blend color, and perspective correction mode. These attributes
+ * are defined in a TextureAttributes object.</li><p>
+ *
+ * <li>Texture coordinate generation - defines the attributes
+ * that apply to texture coordinate generation, such as whether
+ * texture coordinate generation is enabled, coordinate format
+ * (2D or 3D coordinates), coordinate generation mode (object
+ * linear, eye linear, or spherical reflection mapping), and the
+ * R, S, and T coordinate plane equations. These attributes
+ * are defined in a TexCoordGeneration object.</li><p>
+ *
+ * <li>Texture unit state - array that defines texture state for each
+ * of <i>N</i> separate texture units. This allows multiple textures
+ * to be applied to geometry. Each TextureUnitState object contains a
+ * Texture object, TextureAttributes, and TexCoordGeneration object
+ * for one texture unit. If the length of the texture unit state
+ * array is greater than 0, then the array is used for all texture
+ * state; the individual Texture, TextureAttributes, and
+ * TexCoordGeneration objects in this Appearance object are not used
+ * and and must not be set by an application. If the length of the
+ * texture unit state array is 0, the multi-texture is disabled and
+ * the Texture, TextureAttributes, and TexCoordGeneration objects
+ * in the Appearance object are used. If the application sets the
+ * existing Texture, TextureAttributes, and TexCoordGeneration
+ * objects to non-null values, they effectively define the state
+ * for texture unit 0. If the TextureUnitState array is set to a
+ * non-null, non-empty array, the individual TextureUnitState
+ * objects define the state for texture units 0 through <i>n</i>
+ * -1. If both the old and new values are set, an exception is thrown.
+ *
+ * </li>
+ * </ul>
+ *
+ * @see ColoringAttributes
+ * @see LineAttributes
+ * @see PointAttributes
+ * @see PolygonAttributes
+ * @see RenderingAttributes
+ * @see TransparencyAttributes
+ * @see Material
+ * @see Texture
+ * @see TextureAttributes
+ * @see TexCoordGeneration
+ * @see TextureUnitState
+ */
+public class Appearance extends NodeComponent {
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its coloringAttributes component
+ * information.
+ */
+ public static final int
+ ALLOW_COLORING_ATTRIBUTES_READ = CapabilityBits.APPEARANCE_ALLOW_COLORING_ATTRIBUTES_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its coloringAttributes component
+ * information.
+ */
+ public static final int
+ ALLOW_COLORING_ATTRIBUTES_WRITE = CapabilityBits.APPEARANCE_ALLOW_COLORING_ATTRIBUTES_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its transparency component
+ * information.
+ */
+ public static final int
+ ALLOW_TRANSPARENCY_ATTRIBUTES_READ = CapabilityBits.APPEARANCE_ALLOW_TRANSPARENCY_ATTRIBUTES_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its transparency component
+ * information.
+ */
+ public static final int
+ ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE = CapabilityBits.APPEARANCE_ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its rendering/rasterization component
+ * information.
+ */
+ public static final int
+ ALLOW_RENDERING_ATTRIBUTES_READ = CapabilityBits.APPEARANCE_ALLOW_RENDERING_ATTRIBUTES_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its rendering/rasterization component
+ * information.
+ */
+ public static final int
+ ALLOW_RENDERING_ATTRIBUTES_WRITE = CapabilityBits.APPEARANCE_ALLOW_RENDERING_ATTRIBUTES_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its polygon component
+ * information.
+ */
+ public static final int
+ ALLOW_POLYGON_ATTRIBUTES_READ = CapabilityBits.APPEARANCE_ALLOW_POLYGON_ATTRIBUTES_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its polygon component
+ * information.
+ */
+ public static final int
+ ALLOW_POLYGON_ATTRIBUTES_WRITE = CapabilityBits.APPEARANCE_ALLOW_POLYGON_ATTRIBUTES_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its line component
+ * information.
+ */
+ public static final int
+ ALLOW_LINE_ATTRIBUTES_READ = CapabilityBits.APPEARANCE_ALLOW_LINE_ATTRIBUTES_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its line component
+ * information.
+ */
+ public static final int
+ ALLOW_LINE_ATTRIBUTES_WRITE = CapabilityBits.APPEARANCE_ALLOW_LINE_ATTRIBUTES_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its point component
+ * information.
+ */
+ public static final int
+ ALLOW_POINT_ATTRIBUTES_READ = CapabilityBits.APPEARANCE_ALLOW_POINT_ATTRIBUTES_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its point component
+ * information.
+ */
+ public static final int
+ ALLOW_POINT_ATTRIBUTES_WRITE = CapabilityBits.APPEARANCE_ALLOW_POINT_ATTRIBUTES_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its material component information.
+ */
+ public static final int
+ ALLOW_MATERIAL_READ = CapabilityBits.APPEARANCE_ALLOW_MATERIAL_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its material component information.
+ */
+ public static final int
+ ALLOW_MATERIAL_WRITE = CapabilityBits.APPEARANCE_ALLOW_MATERIAL_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its texture component information.
+ */
+ public static final int
+ ALLOW_TEXTURE_READ = CapabilityBits.APPEARANCE_ALLOW_TEXTURE_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its texture component information.
+ */
+ public static final int
+ ALLOW_TEXTURE_WRITE = CapabilityBits.APPEARANCE_ALLOW_TEXTURE_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its textureAttributes component
+ * information.
+ */
+ public static final int
+ ALLOW_TEXTURE_ATTRIBUTES_READ = CapabilityBits.APPEARANCE_ALLOW_TEXTURE_ATTRIBUTES_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its textureAttributes component
+ * information.
+ */
+ public static final int
+ ALLOW_TEXTURE_ATTRIBUTES_WRITE = CapabilityBits.APPEARANCE_ALLOW_TEXTURE_ATTRIBUTES_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its texture coordinate generation component
+ * information.
+ */
+ public static final int
+ ALLOW_TEXGEN_READ = CapabilityBits.APPEARANCE_ALLOW_TEXGEN_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its texture coordinate generation component
+ * information.
+ */
+ public static final int
+ ALLOW_TEXGEN_WRITE = CapabilityBits.APPEARANCE_ALLOW_TEXGEN_WRITE;
+
+ /**
+ * Specifies that this Appearance object
+ * allows reading its texture unit state component
+ * information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_TEXTURE_UNIT_STATE_READ =
+ CapabilityBits.APPEARANCE_ALLOW_TEXTURE_UNIT_STATE_READ;
+
+ /**
+ * Specifies that this Appearance object
+ * allows writing its texture unit state component
+ * information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_TEXTURE_UNIT_STATE_WRITE =
+ CapabilityBits.APPEARANCE_ALLOW_TEXTURE_UNIT_STATE_WRITE;
+
+
+ /**
+ * Constructs an Appearance component object using defaults for all
+ * state variables. All component object references are initialized
+ * to null.
+ */
+ public Appearance() {
+ // Just use default values
+ }
+
+ /**
+ * Creates the retained mode AppearanceRetained object that this
+ * Appearance component object will point to.
+ */
+ void createRetained() {
+ this.retained = new AppearanceRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Sets the material object to the specified object.
+ * Setting it to null disables lighting.
+ * @param material object that specifies the desired material
+ * properties
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setMaterial(Material material) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MATERIAL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance0"));
+ ((AppearanceRetained)this.retained).setMaterial(material);
+ }
+
+ /**
+ * Retrieves the current material object.
+ * @return the material object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Material getMaterial() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MATERIAL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance1"));
+ return ((AppearanceRetained)this.retained).getMaterial();
+ }
+
+ /**
+ * Sets the coloringAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage.
+ * @param coloringAttributes object that specifies the desired
+ * coloringAttributes parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setColoringAttributes(ColoringAttributes coloringAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLORING_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance6"));
+ ((AppearanceRetained)this.retained).setColoringAttributes(coloringAttributes);
+ }
+
+ /**
+ * Retrieves the current coloringAttributes object.
+ * @return the coloringAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public ColoringAttributes getColoringAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLORING_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance7"));
+ return ((AppearanceRetained)this.retained).getColoringAttributes();
+ }
+
+ /**
+ * Sets the transparencyAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage.
+ * @param transparencyAttributes object that specifies the desired
+ * transparencyAttributes parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setTransparencyAttributes(TransparencyAttributes transparencyAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance8"));
+ ((AppearanceRetained)this.retained).setTransparencyAttributes(transparencyAttributes);
+ }
+
+ /**
+ * Retrieves the current transparencyAttributes object.
+ * @return the transparencyAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public TransparencyAttributes getTransparencyAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TRANSPARENCY_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance9"));
+ return ((AppearanceRetained)this.retained).getTransparencyAttributes();
+ }
+
+ /**
+ * Sets the renderingAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage.
+ * @param renderingAttributes object that specifies the desired
+ * renderingAttributes parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setRenderingAttributes(RenderingAttributes renderingAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_RENDERING_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance10"));
+ ((AppearanceRetained)this.retained).setRenderingAttributes(renderingAttributes);
+ }
+
+ /**
+ * Retrieves the current renderingAttributes object.
+ * @return the renderingAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public RenderingAttributes getRenderingAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_RENDERING_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance11"));
+ return ((AppearanceRetained)this.retained).getRenderingAttributes();
+ }
+
+ /**
+ * Sets the polygonAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage.
+ * @param polygonAttributes object that specifies the desired
+ * polygonAttributes parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPolygonAttributes(PolygonAttributes polygonAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_POLYGON_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance12"));
+ ((AppearanceRetained)this.retained).setPolygonAttributes(polygonAttributes);
+ }
+
+ /**
+ * Retrieves the current polygonAttributes object.
+ * @return the polygonAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public PolygonAttributes getPolygonAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_POLYGON_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance13"));
+ return ((AppearanceRetained)this.retained).getPolygonAttributes();
+ }
+
+ /**
+ * Sets the lineAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage.
+ * @param lineAttributes object that specifies the desired
+ * lineAttributes parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setLineAttributes(LineAttributes lineAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_LINE_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance14"));
+ ((AppearanceRetained)this.retained).setLineAttributes(lineAttributes);
+ }
+
+ /**
+ * Retrieves the current lineAttributes object.
+ * @return the lineAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public LineAttributes getLineAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_LINE_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance15"));
+ return ((AppearanceRetained)this.retained).getLineAttributes();
+ }
+
+ /**
+ * Sets the pointAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage.
+ * @param pointAttributes object that specifies the desired
+ * pointAttributes parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPointAttributes(PointAttributes pointAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_POINT_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance16"));
+ ((AppearanceRetained)this.retained).setPointAttributes(pointAttributes);
+ }
+
+ /**
+ * Retrieves the current pointAttributes object.
+ * @return the pointAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public PointAttributes getPointAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_POINT_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance17"));
+ return ((AppearanceRetained)this.retained).getPointAttributes();
+ }
+
+ /**
+ * Sets the texture object to the specified object.
+ * Setting it to null disables texture mapping.
+ *
+ * <p>
+ * Applications must not set individual texture component objects
+ * (texture, textureAttributes, or texCoordGeneration) and
+ * the texture unit state array in the same Appearance object.
+ * Doing so will result in an exception being thrown.
+ *
+ * @param texture object that specifies the desired texture
+ * map and texture parameters
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the specified texture
+ * object is non-null and the texture unit state array in this
+ * appearance object is already non-null.
+ */
+ public void setTexture(Texture texture) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance2"));
+ ((AppearanceRetained)this.retained).setTexture(texture);
+ }
+
+ /**
+ * Retrieves the current texture object.
+ * @return the texture object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Texture getTexture() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance3"));
+ return ((AppearanceRetained)this.retained).getTexture();
+ }
+
+ /**
+ * Sets the textureAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage.
+ *
+ * <p>
+ * Applications must not set individual texture component objects
+ * (texture, textureAttributes, or texCoordGeneration) and
+ * the texture unit state array in the same Appearance object.
+ * Doing so will result in an exception being thrown.
+ *
+ * @param textureAttributes object that specifies the desired
+ * textureAttributes map and textureAttributes parameters
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the specified textureAttributes
+ * object is non-null and the texture unit state array in this
+ * appearance object is already non-null.
+ */
+ public void setTextureAttributes(TextureAttributes textureAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance4"));
+ ((AppearanceRetained)this.retained).setTextureAttributes(textureAttributes);
+ }
+
+ /**
+ * Retrieves the current textureAttributes object.
+ * @return the textureAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public TextureAttributes getTextureAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance5"));
+ return ((AppearanceRetained)this.retained).getTextureAttributes();
+ }
+
+ /**
+ * Sets the texCoordGeneration object to the specified object.
+ * Setting it to null disables texture coordinate generation.
+ *
+ * <p>
+ * Applications must not set individual texture component objects
+ * (texture, textureAttributes, or texCoordGeneration) and
+ * the texture unit state array in the same Appearance object.
+ * Doing so will result in an exception being thrown.
+ *
+ * @param texCoordGeneration object that specifies the texture coordinate
+ * generation parameters
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the specified texCoordGeneration
+ * object is non-null and the texture unit state array in this
+ * appearance object is already non-null.
+ */
+ public void setTexCoordGeneration(TexCoordGeneration texCoordGeneration) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXGEN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance18"));
+ ((AppearanceRetained)this.retained).setTexCoordGeneration(texCoordGeneration);
+ }
+
+ /**
+ * Retrieves the current texCoordGeneration object.
+ * @return the texCoordGeneration object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public TexCoordGeneration getTexCoordGeneration() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXGEN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance19"));
+ return ((AppearanceRetained)this.retained).getTexCoordGeneration();
+ }
+
+ /**
+ * Sets the texture unit state array for this appearance object to the
+ * specified array. A shallow copy of the array of references to
+ * the TextureUnitState objects is made. If the specified array
+ * is null or if the length of the array is 0, multi-texture is
+ * disabled. Within the array, a null TextureUnitState element
+ * disables the corresponding texture unit.
+ *
+ * <p>
+ * Applications must not set individual texture component objects
+ * (texture, textureAttributes, or texCoordGeneration) and
+ * the texture unit state array in the same Appearance object.
+ * Doing so will result in an exception being thrown.
+ *
+ * @param stateArray array of TextureUnitState objects that
+ * specify the desired texture state for each unit. The length of
+ * this array specifies the maximum number of texture units that
+ * will be used by this appearance object. The texture units are
+ * numbered from <code>0</code> through
+ * <code>stateArray.length-1</code>.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the specified array is
+ * non-null and any of the texture object, textureAttributes
+ * object, or texCoordGeneration object in this appearance object
+ * is already non-null.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureUnitState(TextureUnitState[] stateArray) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_UNIT_STATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance20"));
+
+ ((AppearanceRetained)this.retained).setTextureUnitState(stateArray);
+ }
+
+ /**
+ * Sets the texture unit state object at the specified index
+ * within the texture unit state array to the specified object.
+ * If the specified object is null, the corresponding texture unit
+ * is disabled. The index must be within the range
+ * <code>[0,&nbsp;stateArray.length-1]</code>.
+ *
+ * @param index the array index of the object to be set
+ *
+ * @param state new texture unit state object
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception NullPointerException if the texture unit state array is
+ * null.
+ * @exception ArrayIndexOutOfBoundsException if <code>index >=
+ * stateArray.length</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureUnitState(int index, TextureUnitState state) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_UNIT_STATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance20"));
+
+ ((AppearanceRetained)this.retained).setTextureUnitState(index, state);
+ }
+
+ /**
+ * Retrieves the array of texture unit state objects from this
+ * Appearance object. A shallow copy of the array of references to
+ * the TextureUnitState objects is returned.
+ *
+ * @return the array of texture unit state objects
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public TextureUnitState[] getTextureUnitState() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_UNIT_STATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance21"));
+
+ return ((AppearanceRetained)this.retained).getTextureUnitState();
+ }
+
+ /**
+ * Retrieves the texture unit state object at the specified
+ * index within the texture unit state array. The index must be
+ * within the range <code>[0,&nbsp;stateArray.length-1]</code>.
+ *
+ * @param index the array index of the object to be retrieved
+ *
+ * @return the texture unit state object at the specified index
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public TextureUnitState getTextureUnitState(int index) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_UNIT_STATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance21"));
+
+ return ((AppearanceRetained)this.retained).getTextureUnitState(index);
+ }
+
+ /**
+ * Retrieves the length of the texture unit state array from
+ * this appearance object. The length of this array specifies the
+ * maximum number of texture units that will be used by this
+ * appearance object. If the array is null, a count of 0 is
+ * returned.
+ *
+ * @return the length of the texture unit state array
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getTextureUnitCount() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TEXTURE_UNIT_STATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Appearance21"));
+
+ return ((AppearanceRetained)this.retained).getTextureUnitCount();
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ Appearance a = new Appearance();
+ a.duplicateNodeComponent(this);
+ return a;
+ }
+
+ /**
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @deprecated replaced with duplicateNodeComponent(
+ * NodeComponent originalNodeComponent, boolean forceDuplicate)
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+ checkDuplicateNodeComponent(originalNodeComponent);
+ }
+
+ /**
+ * Copies all Appearance information from
+ * <code>originalNodeComponent</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 originalNodeComponent 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#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ Hashtable hashtable = originalNodeComponent.nodeHashtable;
+
+ AppearanceRetained app = (AppearanceRetained) originalNodeComponent.retained;
+
+ AppearanceRetained rt = (AppearanceRetained) retained;
+
+ rt.setMaterial((Material) getNodeComponent(app.getMaterial(),
+ forceDuplicate,
+ hashtable));
+
+ rt.setColoringAttributes((ColoringAttributes) getNodeComponent(
+ app.getColoringAttributes(),
+ forceDuplicate,
+ hashtable));
+
+
+ rt.setTransparencyAttributes((TransparencyAttributes) getNodeComponent(
+ app.getTransparencyAttributes(),
+ forceDuplicate,
+ hashtable));
+
+
+ rt.setRenderingAttributes((RenderingAttributes) getNodeComponent(
+ app.getRenderingAttributes(),
+ forceDuplicate,
+ hashtable));
+
+
+ rt.setPolygonAttributes((PolygonAttributes) getNodeComponent(
+ app.getPolygonAttributes(),
+ forceDuplicate,
+ hashtable));
+
+
+ rt.setLineAttributes((LineAttributes) getNodeComponent(
+ app.getLineAttributes(),
+ forceDuplicate,
+ hashtable));
+
+
+ rt.setPointAttributes((PointAttributes) getNodeComponent(
+ app.getPointAttributes(),
+ forceDuplicate,
+ hashtable));
+
+ rt.setTexture((Texture) getNodeComponent(app.getTexture(),
+ forceDuplicate,
+ hashtable));
+
+ rt.setTextureAttributes((TextureAttributes) getNodeComponent(
+ app.getTextureAttributes(),
+ forceDuplicate,
+ hashtable));
+
+ rt.setTexCoordGeneration((TexCoordGeneration) getNodeComponent(
+ app.getTexCoordGeneration(),
+ forceDuplicate,
+ hashtable));
+
+ TextureUnitState state[] = app.getTextureUnitState();
+ if (state != null) {
+ rt.setTextureUnitState(state);
+ for (int i=0; i < state.length; i++) {
+ rt.setTextureUnitState(i, (TextureUnitState)
+ getNodeComponent(state[i],
+ forceDuplicate,
+ hashtable));
+ }
+ }
+
+ }
+
+ /**
+ * This function is called from getNodeComponent() to see if any of
+ * the sub-NodeComponents duplicateOnCloneTree flag is true.
+ * If it is the case, current NodeComponent needs to
+ * duplicate also even though current duplicateOnCloneTree flag is false.
+ * This should be overwrite by NodeComponent which contains sub-NodeComponent.
+ */
+ boolean duplicateChild() {
+ if (getDuplicateOnCloneTree())
+ return true;
+
+ AppearanceRetained rt = (AppearanceRetained) retained;
+
+ NodeComponent nc;
+
+ nc = rt.getMaterial();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getColoringAttributes();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getTransparencyAttributes();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getPolygonAttributes();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getLineAttributes();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getPointAttributes();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getTexture();
+ if ((nc != null) && nc.duplicateChild())
+ return true;
+
+ nc = rt.getTextureAttributes();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getTexCoordGeneration();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ // TODO: TextureUnitState
+
+ return false;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/AppearanceRetained.java b/src/classes/share/javax/media/j3d/AppearanceRetained.java
new file mode 100644
index 0000000..a5afcee
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AppearanceRetained.java
@@ -0,0 +1,1391 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+import java.util.BitSet;
+import java.util.ArrayList;
+
+
+/**
+ * The Appearance object defines all rendering state that can be set
+ * as a component object of a Shape3D node.
+ */
+class AppearanceRetained extends NodeComponentRetained {
+
+ //
+ // State variables: these should all be initialized to approproate
+ // Java 3D defaults.
+ //
+
+ // Material object used when lighting is enabled
+ MaterialRetained material = null;
+
+ // Texture object used to apply a texture map to an object
+ TextureRetained texture = null;
+
+ // Texture coordinate generation object
+ TexCoordGenerationRetained texCoordGeneration = null;
+
+ // Texture Attributes bundle object
+ TextureAttributesRetained textureAttributes = null;
+
+ TextureUnitStateRetained texUnitState[] = null;
+
+ // Coloring Attributes bundle object
+ ColoringAttributesRetained coloringAttributes = null;
+
+ // Transparency Attributes bundle object
+ TransparencyAttributesRetained transparencyAttributes = null;
+
+ // Rendering Attributes bundle object
+ RenderingAttributesRetained renderingAttributes = null;
+
+ // Polygon Attributes bundle object
+ PolygonAttributesRetained polygonAttributes = null;
+
+ // Line Attributes bundle object
+ LineAttributesRetained lineAttributes = null;
+
+ // Point Attributes bundle object
+ PointAttributesRetained pointAttributes = null;
+
+
+ // Lock used for synchronization of live state
+ Object liveStateLock = new Object();
+
+ // NOTE: Consider grouping random state into common objects
+
+ // Cache used during compilation. If map == compState, then
+ // mapAppearance can be used for this appearance
+ CompileState map = null;
+ AppearanceRetained mapAppearance = null;
+
+ static final int MATERIAL = 0x0001;
+ static final int TEXTURE = 0x0002;
+ static final int TEXCOORD_GEN = 0x0004;
+ static final int TEXTURE_ATTR = 0x0008;
+ static final int COLOR = 0x0010;
+ static final int TRANSPARENCY = 0x0020;
+ static final int RENDERING = 0x0040;
+ static final int POLYGON = 0x0080;
+ static final int LINE = 0x0100;
+ static final int POINT = 0x0200;
+ static final int TEXTURE_UNIT_STATE = 0x0400;
+
+ static final int ALL_COMPONENTS = (MATERIAL|TEXTURE|TEXCOORD_GEN|TEXTURE_ATTR|COLOR|TRANSPARENCY|
+ RENDERING|POLYGON|LINE|POINT|TEXTURE_UNIT_STATE);
+
+ static final int ALL_SOLE_USERS = 0;
+
+ // A pointer to the scene graph appearance object
+ AppearanceRetained sgApp = null;
+
+ // The object level hashcode for this appearance
+ // int objHashCode = super.hashCode();
+
+ /**
+ * Set the material object to the specified object.
+ * @param material object that specifies the desired material
+ * @exception IllegalSharingException
+ * properties
+ */
+ void setMaterial(Material material) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.material != null) {
+ this.material.clearLive(refCount);
+ this.material.removeMirrorUsers(this);
+ }
+ if (material != null) {
+ ((MaterialRetained)material.retained).setLive(inBackgroundGroup, refCount);
+ // If appearance is live, then copy all the users of this
+ // appaearance as users of this material
+ ((MaterialRetained)material.retained).copyMirrorUsers(this);
+ }
+ sendMessage(MATERIAL,
+ (material != null ?
+ ((MaterialRetained)material.retained).mirror : null), true);
+ }
+ if (material == null) {
+ this.material = null;
+ } else {
+ this.material = (MaterialRetained)material.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current material object.
+ * @return the material object
+ */
+ Material getMaterial() {
+ return (material == null ? null : (Material)material.source);
+ }
+
+ /**
+ * Sets the texture object to the specified object.
+ * @param texture object that specifies the desired texture
+ * map and texture parameters
+ */
+ void setTexture(Texture texture) {
+
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.texture != null) {
+ this.texture.clearLive(refCount);
+ this.texture.removeMirrorUsers(this);
+ }
+
+ if (texture != null) {
+ ((TextureRetained)texture.retained).setLive(inBackgroundGroup, refCount);
+ ((TextureRetained)texture.retained).copyMirrorUsers(this);
+ }
+ sendMessage(TEXTURE,
+ (texture != null ?
+ ((TextureRetained)texture.retained).mirror : null), true);
+
+ }
+
+
+ if (texture == null) {
+ this.texture = null;
+ } else {
+ this.texture = (TextureRetained)texture.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current texture object.
+ * @return the texture object
+ */
+ Texture getTexture() {
+ return (texture == null ? null : (Texture)texture.source);
+ }
+
+ /**
+ * Sets the textureAttrbutes object to the specified object.
+ * @param textureAttributes object that specifies the desired texture
+ * attributes
+ */
+ void setTextureAttributes(TextureAttributes textureAttributes) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.textureAttributes != null) {
+ this.textureAttributes.clearLive(refCount);
+ this.textureAttributes.removeMirrorUsers(this);
+ }
+
+ if (textureAttributes != null) {
+ ((TextureAttributesRetained)textureAttributes.retained).setLive(inBackgroundGroup, refCount);
+ ((TextureAttributesRetained)textureAttributes.retained).copyMirrorUsers(this);
+ }
+ sendMessage(TEXTURE_ATTR,
+ (textureAttributes != null ?
+ ((TextureAttributesRetained)textureAttributes.retained).mirror:
+ null), true);
+
+ }
+
+
+ if (textureAttributes == null) {
+ this.textureAttributes = null;
+ } else {
+ this.textureAttributes = (TextureAttributesRetained)textureAttributes.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current textureAttributes object.
+ * @return the textureAttributes object
+ */
+ TextureAttributes getTextureAttributes() {
+ return (textureAttributes == null ? null :
+ (TextureAttributes)textureAttributes.source);
+ }
+
+ /**
+ * Sets the coloringAttrbutes object to the specified object.
+ * @param coloringAttributes object that specifies the desired texture
+ * attributes
+ */
+ void setColoringAttributes(ColoringAttributes coloringAttributes) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.coloringAttributes != null) {
+ this.coloringAttributes.clearLive(refCount);
+ this.coloringAttributes.removeMirrorUsers(this);
+ }
+
+ if (coloringAttributes != null) {
+ ((ColoringAttributesRetained)coloringAttributes.retained).setLive(inBackgroundGroup, refCount);
+ ((ColoringAttributesRetained)coloringAttributes.retained).copyMirrorUsers(this);
+ }
+ sendMessage(COLOR,
+ (coloringAttributes != null ?
+ ((ColoringAttributesRetained)coloringAttributes.retained).mirror:
+ null), true);
+ }
+
+
+ if (coloringAttributes == null) {
+ this.coloringAttributes = null;
+ } else {
+ this.coloringAttributes = (ColoringAttributesRetained)coloringAttributes.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current coloringAttributes object.
+ * @return the coloringAttributes object
+ */
+ ColoringAttributes getColoringAttributes() {
+ return (coloringAttributes == null ? null :
+ (ColoringAttributes)coloringAttributes.source);
+ }
+
+ /**
+ * Sets the transparencyAttrbutes object to the specified object.
+ * @param transparencyAttributes object that specifies the desired texture
+ * attributes
+ */
+ void setTransparencyAttributes(TransparencyAttributes transparencyAttributes) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.transparencyAttributes != null) {
+ this.transparencyAttributes.clearLive(refCount);
+ this.transparencyAttributes.removeMirrorUsers(this);
+ }
+
+ if (transparencyAttributes != null) {
+ ((TransparencyAttributesRetained)transparencyAttributes.retained).setLive(inBackgroundGroup, refCount);
+ ((TransparencyAttributesRetained)transparencyAttributes.retained).copyMirrorUsers(this);
+ }
+
+ sendMessage(TRANSPARENCY,
+ (transparencyAttributes != null ?
+ ((TransparencyAttributesRetained)transparencyAttributes.retained).mirror: null), true);
+ }
+
+
+ if (transparencyAttributes == null) {
+ this.transparencyAttributes = null;
+ } else {
+ this.transparencyAttributes = (TransparencyAttributesRetained)transparencyAttributes.retained;
+
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current transparencyAttributes object.
+ * @return the transparencyAttributes object
+ */
+ TransparencyAttributes getTransparencyAttributes() {
+ return (transparencyAttributes == null ? null :
+ (TransparencyAttributes)transparencyAttributes.source);
+ }
+
+ /**
+ * Sets the renderingAttrbutes object to the specified object.
+ * @param renderingAttributes object that specifies the desired texture
+ * attributes
+ */
+ void setRenderingAttributes(RenderingAttributes renderingAttributes) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+ if (this.renderingAttributes != null) {
+ this.renderingAttributes.clearLive(refCount);
+ this.renderingAttributes.removeMirrorUsers(this);
+ }
+
+ if (renderingAttributes != null) {
+ ((RenderingAttributesRetained)renderingAttributes.retained).setLive(inBackgroundGroup, refCount);
+ ((RenderingAttributesRetained)renderingAttributes.retained).copyMirrorUsers(this);
+ }
+ Object m = null;
+ boolean v = true;
+ if (renderingAttributes != null) {
+ m = ((RenderingAttributesRetained)renderingAttributes.retained).mirror;
+ v = ((RenderingAttributesRetained)renderingAttributes.retained).visible;
+ }
+ sendMessage(RENDERING,m, v);
+ // Also need to send a message to GeometryStructure.
+ sendRenderingAttributesChangedMessage( v);
+ }
+ if (renderingAttributes == null) {
+ this.renderingAttributes = null;
+ } else {
+ this.renderingAttributes = (RenderingAttributesRetained)renderingAttributes.retained;
+
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current renderingAttributes object.
+ * @return the renderingAttributes object
+ */
+ RenderingAttributes getRenderingAttributes() {
+ if (renderingAttributes == null)
+ return null;
+
+ return (RenderingAttributes)renderingAttributes.source;
+ }
+
+ /**
+ * Sets the polygonAttrbutes object to the specified object.
+ * @param polygonAttributes object that specifies the desired texture
+ * attributes
+ */
+ void setPolygonAttributes(PolygonAttributes polygonAttributes) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+ if (this.polygonAttributes != null) {
+ this.polygonAttributes.clearLive(refCount);
+ this.polygonAttributes.removeMirrorUsers(this);
+ }
+
+ if (polygonAttributes != null) {
+ ((PolygonAttributesRetained)polygonAttributes.retained).setLive(inBackgroundGroup, refCount);
+ ((PolygonAttributesRetained)polygonAttributes.retained).copyMirrorUsers(this);
+ }
+ sendMessage(POLYGON,
+ (polygonAttributes != null ?
+ ((PolygonAttributesRetained)polygonAttributes.retained).mirror :
+ null), true);
+
+ }
+
+ if (polygonAttributes == null) {
+ this.polygonAttributes = null;
+ } else {
+ this.polygonAttributes = (PolygonAttributesRetained)polygonAttributes.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current polygonAttributes object.
+ * @return the polygonAttributes object
+ */
+ PolygonAttributes getPolygonAttributes() {
+ return (polygonAttributes == null ? null:
+ (PolygonAttributes)polygonAttributes.source);
+ }
+
+ /**
+ * Sets the lineAttrbutes object to the specified object.
+ * @param lineAttributes object that specifies the desired texture
+ * attributes
+ */
+ void setLineAttributes(LineAttributes lineAttributes) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.lineAttributes != null) {
+ this.lineAttributes.clearLive(refCount);
+ this.lineAttributes.removeMirrorUsers(this);
+ }
+
+ if (lineAttributes != null) {
+ ((LineAttributesRetained)lineAttributes.retained).setLive(inBackgroundGroup, refCount);
+ ((LineAttributesRetained)lineAttributes.retained).copyMirrorUsers(this);
+ }
+ sendMessage(LINE,
+ (lineAttributes != null ?
+ ((LineAttributesRetained)lineAttributes.retained).mirror: null), true);
+ }
+
+
+ if (lineAttributes == null) {
+ this.lineAttributes = null;
+ } else {
+ this.lineAttributes = (LineAttributesRetained)lineAttributes.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current lineAttributes object.
+ * @return the lineAttributes object
+ */
+ LineAttributes getLineAttributes() {
+ return (lineAttributes == null ? null :
+ (LineAttributes)lineAttributes.source);
+ }
+
+ /**
+ * Sets the pointAttrbutes object to the specified object.
+ * @param pointAttributes object that specifies the desired texture
+ * attributes
+ */
+ void setPointAttributes(PointAttributes pointAttributes) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.pointAttributes != null) {
+ this.pointAttributes.clearLive(refCount);
+ this.pointAttributes.removeMirrorUsers(this);
+ }
+ if (pointAttributes != null) {
+ ((PointAttributesRetained)pointAttributes.retained).setLive(inBackgroundGroup, refCount);
+ ((PointAttributesRetained)pointAttributes.retained).copyMirrorUsers(this);
+ }
+ sendMessage(POINT,
+ (pointAttributes != null ?
+ ((PointAttributesRetained)pointAttributes.retained).mirror:
+ null), true);
+ }
+
+
+ if (pointAttributes == null) {
+ this.pointAttributes = null;
+ } else {
+ this.pointAttributes = (PointAttributesRetained)pointAttributes.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current pointAttributes object.
+ * @return the pointAttributes object
+ */
+ PointAttributes getPointAttributes() {
+ return (pointAttributes == null? null : (PointAttributes)pointAttributes.source);
+ }
+
+ /**
+ * Sets the texCoordGeneration object to the specified object.
+ * @param texCoordGeneration object that specifies the texture coordinate
+ * generation parameters
+ */
+ void setTexCoordGeneration(TexCoordGeneration texGen) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ if (this.texCoordGeneration != null) {
+ this.texCoordGeneration.clearLive(refCount);
+ this.texCoordGeneration.removeMirrorUsers(this);
+ }
+
+ if (texGen != null) {
+ ((TexCoordGenerationRetained)texGen.retained).setLive(inBackgroundGroup, refCount);
+ ((TexCoordGenerationRetained)texGen.retained).copyMirrorUsers(this);
+ }
+ sendMessage(TEXCOORD_GEN,
+ (texGen != null ?
+ ((TexCoordGenerationRetained)texGen.retained).mirror : null), true);
+ }
+
+ if (texGen == null) {
+ this.texCoordGeneration = null;
+ } else {
+ this.texCoordGeneration = (TexCoordGenerationRetained)texGen.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current texCoordGeneration object.
+ * @return the texCoordGeneration object
+ */
+ TexCoordGeneration getTexCoordGeneration() {
+ return (texCoordGeneration == null ? null :
+ (TexCoordGeneration)texCoordGeneration.source);
+ }
+
+
+ /**
+ * Sets the texture unit state array to the specified array.
+ * @param textureUnitState array that specifies the texture unit state
+ */
+ void setTextureUnitState(TextureUnitState[] stateArray) {
+
+ int i;
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ // remove the existing texture unit states from this appearance
+ if (this.texUnitState != null) {
+ for (i = 0; i < this.texUnitState.length; i++) {
+ if (this.texUnitState[i] != null) {
+ this.texUnitState[i].clearLive(refCount);
+ this.texUnitState[i].removeMirrorUsers(this);
+ }
+ }
+ }
+
+ // add the specified texture unit states to this appearance
+ // also make a copy of the array of references to the units
+ if (stateArray != null) {
+
+ Object [] args = new Object[2];
+
+ // -1 index means the entire array is to be set
+ args[0] = new Integer(-1);
+
+ // make a copy of the array for the message,
+ TextureUnitStateRetained mirrorStateArray[] =
+ new TextureUnitStateRetained[stateArray.length];
+
+ args[1] = (Object) mirrorStateArray;
+
+ for (i = 0; i < stateArray.length; i++) {
+ TextureUnitState tu = stateArray[i];
+ if (tu != null) {
+ ((TextureUnitStateRetained)tu.retained).setLive(
+ inBackgroundGroup, refCount);
+ ((TextureUnitStateRetained)tu.retained).copyMirrorUsers(
+ this);
+ mirrorStateArray[i] = (TextureUnitStateRetained)
+ ((TextureUnitStateRetained)tu.retained).mirror;
+ } else {
+ mirrorStateArray[i] = null;
+ }
+ }
+ sendMessage(TEXTURE_UNIT_STATE, args, true);
+
+ } else {
+ sendMessage(TEXTURE_UNIT_STATE, null, true);
+ }
+ }
+
+ // assign the retained copy of the texture unit state to the
+ // appearance
+ if (stateArray == null) {
+ this.texUnitState = null;
+ } else {
+
+ // make another copy of the array for the retained object
+ // itself if it doesn't have a copy or the array size is
+ // not the same
+ if ((this.texUnitState == null) ||
+ (this.texUnitState.length != stateArray.length)) {
+ this.texUnitState = new TextureUnitStateRetained[
+ stateArray.length];
+ }
+ for (i = 0; i < stateArray.length; i++) {
+ if (stateArray[i] != null) {
+ this.texUnitState[i] =
+ (TextureUnitStateRetained)stateArray[i].retained;
+ } else {
+ this.texUnitState[i] = null;
+ }
+ }
+ }
+ }
+ }
+
+ void setTextureUnitState(int index, TextureUnitState state) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+
+ // remove the existing texture unit states from this appearance
+ // Note: Let Java throw an exception if texUnitState is null
+ // or index is >= texUnitState.length.
+ if (this.texUnitState[index] != null) {
+ this.texUnitState[index].clearLive(refCount);
+ this.texUnitState[index].removeMirrorUsers(this);
+ }
+
+ // add the specified texture unit states to this appearance
+ // also make a copy of the array of references to the units
+ Object args[] = new Object[2];
+ args[0] = new Integer(index);
+
+ if (state != null) {
+ ((TextureUnitStateRetained)state.retained).setLive(
+ inBackgroundGroup, refCount);
+ ((TextureUnitStateRetained)state.retained).copyMirrorUsers(this);
+ args[1] = ((TextureUnitStateRetained)state.retained).mirror;
+ sendMessage(TEXTURE_UNIT_STATE, args, true);
+ } else {
+ args[1] = null;
+ sendMessage(TEXTURE_UNIT_STATE, args, true);
+ }
+ }
+
+ // assign the retained copy of the texture unit state to the
+ // appearance
+ if (state != null) {
+ this.texUnitState[index] = (TextureUnitStateRetained)state.retained;
+ } else {
+ this.texUnitState[index] = null;
+ }
+ }
+ }
+
+
+
+ /**
+ * Retrieves the array of texture unit state objects from this
+ * Appearance object. A shallow copy of the array of references to
+ * the TextureUnitState objects is returned.
+ *
+ */
+ TextureUnitState[] getTextureUnitState() {
+ if (texUnitState == null) {
+ return null;
+ } else {
+ TextureUnitState tus[] =
+ new TextureUnitState[texUnitState.length];
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null) {
+ tus[i] = (TextureUnitState) texUnitState[i].source;
+ } else {
+ tus[i] = null;
+ }
+ }
+ return tus;
+ }
+ }
+
+ /**
+ * Retrieves the texture unit state object at the specified
+ * index within the texture unit state array.
+ */
+ TextureUnitState getTextureUnitState(int index) {
+
+ // let Java throw an exception if texUnitState == null or
+ // index is >= length
+ if (texUnitState[index] != null)
+ return (TextureUnitState)texUnitState[index].source;
+ else
+ return null;
+ }
+
+
+ /**
+ * Retrieves the length of the texture unit state array from
+ * this appearance object. The length of this array specifies the
+ * maximum number of texture units that will be used by this
+ * appearance object. If the array is null, a count of 0 is
+ * returned.
+ */
+
+ int getTextureUnitCount() {
+ if (texUnitState == null)
+ return 0;
+ else
+ return texUnitState.length;
+ }
+
+
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // we can't check isStatic() since it sub-NodeComponent
+ // create a new one, we should create a
+ // new AppearanceRetained() even though isStatic() = true.
+ // For simplicity, always create a retained side.
+ mirror = new AppearanceRetained();
+ }
+ initMirrorObject();
+ }
+
+ /**
+ * This routine updates the mirror appearance for this appearance.
+ * It also calls the update method for each node component if it
+ * is not null.
+ */
+ synchronized void initMirrorObject() {
+
+ AppearanceRetained mirrorApp = (AppearanceRetained)mirror;
+
+ mirrorApp.source = source;
+ mirrorApp.sgApp = this;
+ if (material != null) {
+ mirrorApp.material = (MaterialRetained)material.mirror;
+ } else {
+ mirrorApp.material = null;
+ }
+
+ if (texture != null) {
+ mirrorApp.texture = (TextureRetained)texture.mirror;
+ } else {
+ mirrorApp.texture = null;
+ }
+ if (texCoordGeneration != null) {
+ mirrorApp.texCoordGeneration = (TexCoordGenerationRetained)texCoordGeneration.mirror;
+ } else {
+ mirrorApp.texCoordGeneration = null;
+ }
+
+ if (textureAttributes != null) {
+ mirrorApp.textureAttributes = (TextureAttributesRetained)textureAttributes.mirror;
+ } else {
+ mirrorApp.textureAttributes = null;
+ }
+
+ // TextureUnitState supercedes the single texture interface
+ if (texUnitState != null && texUnitState.length > 0) {
+ mirrorApp.texUnitState =
+ new TextureUnitStateRetained[texUnitState.length];
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null) {
+ mirrorApp.texUnitState[i] =
+ (TextureUnitStateRetained)texUnitState[i].mirror;
+ }
+ }
+ } else if (mirrorApp.texture != null ||
+ mirrorApp.textureAttributes != null ||
+ mirrorApp.texCoordGeneration != null) {
+
+ mirrorApp.texUnitState = new TextureUnitStateRetained[1];
+ mirrorApp.texUnitState[0] = new TextureUnitStateRetained();
+ mirrorApp.texUnitState[0].set(
+ mirrorApp.texture,
+ mirrorApp.textureAttributes,
+ mirrorApp.texCoordGeneration);
+ }
+
+ if (coloringAttributes != null) {
+ mirrorApp.coloringAttributes = (ColoringAttributesRetained)coloringAttributes.mirror;
+ } else {
+ mirrorApp.coloringAttributes = null;
+ }
+ if (transparencyAttributes != null) {
+ mirrorApp.transparencyAttributes = (TransparencyAttributesRetained)transparencyAttributes.mirror;
+ } else {
+ mirrorApp.transparencyAttributes = null;
+ }
+
+ if (renderingAttributes != null) {
+ mirrorApp.renderingAttributes = (RenderingAttributesRetained)renderingAttributes.mirror;
+ } else {
+ mirrorApp.renderingAttributes = null;
+ }
+
+ if (polygonAttributes != null) {
+ mirrorApp.polygonAttributes = (PolygonAttributesRetained)polygonAttributes.mirror;
+ } else {
+ mirrorApp.polygonAttributes = null;
+ }
+
+ if (lineAttributes != null) {
+ mirrorApp.lineAttributes = (LineAttributesRetained)lineAttributes.mirror;
+ } else {
+ mirrorApp.lineAttributes = null;
+ }
+
+ if (pointAttributes != null) {
+ mirrorApp.pointAttributes = (PointAttributesRetained)pointAttributes.mirror;
+ } else {
+ mirrorApp.pointAttributes = null;
+ }
+ }
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+ AppearanceRetained mirrorApp = (AppearanceRetained)mirror;
+ if ((component & MATERIAL) != 0) {
+ mirrorApp.material = (MaterialRetained)value;
+ }
+ else if ((component & TEXTURE) != 0) {
+ if (mirrorApp.texUnitState == null) {
+ mirrorApp.texUnitState = new TextureUnitStateRetained[1];
+ mirrorApp.texUnitState[0] = new TextureUnitStateRetained();
+ }
+ mirrorApp.texUnitState[0].texture = (TextureRetained)value;
+ }
+ else if ((component & TEXCOORD_GEN) != 0) {
+ if (mirrorApp.texUnitState == null) {
+ mirrorApp.texUnitState = new TextureUnitStateRetained[1];
+ mirrorApp.texUnitState[0] = new TextureUnitStateRetained();
+ }
+ mirrorApp.texUnitState[0].texGen = (TexCoordGenerationRetained)value;
+ }
+ else if ((component & TEXTURE_ATTR) != 0) {
+ if (mirrorApp.texUnitState == null) {
+ mirrorApp.texUnitState = new TextureUnitStateRetained[1];
+ mirrorApp.texUnitState[0] = new TextureUnitStateRetained();
+ }
+ mirrorApp.texUnitState[0].texAttrs = (TextureAttributesRetained)value;
+ }
+ else if ((component & TEXTURE_UNIT_STATE) != 0) {
+ Object [] args = (Object [])value;
+
+ if (args == null) {
+ mirrorApp.texUnitState = null;
+ } else {
+ int index = ((Integer)args[0]).intValue();
+ if (index == -1) {
+ mirrorApp.texUnitState =
+ (TextureUnitStateRetained [])args[1];
+ } else {
+ mirrorApp.texUnitState[index] =
+ (TextureUnitStateRetained)args[1];
+ }
+ }
+ }
+ else if ((component & COLOR) != 0) {
+ mirrorApp.coloringAttributes = (ColoringAttributesRetained)value;
+ }
+ else if ((component & TRANSPARENCY) != 0) {
+ mirrorApp.transparencyAttributes = (TransparencyAttributesRetained)value;
+ }
+ else if ((component & RENDERING) != 0) {
+ mirrorApp.renderingAttributes = (RenderingAttributesRetained)value;
+ }
+ else if ((component & POLYGON) != 0) {
+ mirrorApp.polygonAttributes = (PolygonAttributesRetained)value;
+ }
+ else if ((component & LINE) != 0) {
+ mirrorApp.lineAttributes = (LineAttributesRetained)value;
+ }
+ else if ((component & POINT) != 0) {
+ mirrorApp.pointAttributes = (PointAttributesRetained)value;
+ }
+
+ }
+
+ /**
+ * This setLive routine first calls the superclass's method, then
+ * it adds itself to the list of lights
+ */
+ void setLive(boolean backgroundGroup, int refCount) {
+
+ if (material != null) {
+
+ material.setLive(backgroundGroup, refCount);
+ }
+
+ if (texture != null) {
+
+ texture.setLive(backgroundGroup, refCount);
+ }
+
+ if (texCoordGeneration != null) {
+
+ texCoordGeneration.setLive(backgroundGroup, refCount);
+ }
+
+ if (textureAttributes != null) {
+
+ textureAttributes.setLive(backgroundGroup, refCount);
+ }
+
+ if (texUnitState != null) {
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null)
+ texUnitState[i].setLive(backgroundGroup, refCount);
+ }
+ }
+
+
+ if (coloringAttributes != null) {
+ coloringAttributes.setLive(backgroundGroup, refCount);
+ }
+
+ if (transparencyAttributes != null) {
+ transparencyAttributes.setLive(backgroundGroup, refCount);
+ }
+
+ if (renderingAttributes != null) {
+ renderingAttributes.setLive(backgroundGroup, refCount);
+ }
+
+ if (polygonAttributes != null) {
+ polygonAttributes.setLive(backgroundGroup, refCount);
+ }
+
+ if (lineAttributes != null) {
+ lineAttributes.setLive(backgroundGroup, refCount);
+ }
+
+ if (pointAttributes != null) {
+ pointAttributes.setLive(backgroundGroup, refCount);
+ }
+
+
+ // Increment the reference count and initialize the appearance
+ // mirror object
+ super.doSetLive(backgroundGroup, refCount);
+ super.markAsLive();
+ }
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it removes itself to the list of lights
+ */
+ void clearLive(int refCount) {
+ super.clearLive(refCount);
+
+ if (texture != null) {
+ texture.clearLive(refCount);
+ }
+
+ if (texCoordGeneration != null) {
+ texCoordGeneration.clearLive(refCount);
+ }
+
+ if (textureAttributes != null) {
+ textureAttributes.clearLive(refCount);
+ }
+
+ if (texUnitState != null) {
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null)
+ texUnitState[i].clearLive(refCount);
+ }
+ }
+
+ if (coloringAttributes != null) {
+ coloringAttributes.clearLive(refCount);
+ }
+
+ if (transparencyAttributes != null) {
+ transparencyAttributes.clearLive(refCount);
+ }
+
+ if (renderingAttributes != null) {
+ renderingAttributes.clearLive(refCount);
+ }
+
+ if (polygonAttributes != null) {
+ polygonAttributes.clearLive(refCount);
+ }
+
+ if (lineAttributes != null) {
+ lineAttributes.clearLive(refCount);
+ }
+
+ if (pointAttributes != null) {
+ pointAttributes.clearLive(refCount);
+ }
+
+ if (material != null) {
+ material.clearLive(refCount);
+ }
+ }
+
+
+ boolean isStatic() {
+ boolean flag;
+
+ flag = (source.capabilityBitsEmpty() &&
+ ((texture == null) ||
+ texture.source.capabilityBitsEmpty()) &&
+ ((texCoordGeneration == null) ||
+ texCoordGeneration.source.capabilityBitsEmpty()) &&
+ ((textureAttributes == null) ||
+ textureAttributes.source.capabilityBitsEmpty()) &&
+ ((coloringAttributes == null) ||
+ coloringAttributes.source.capabilityBitsEmpty()) &&
+ ((transparencyAttributes == null) ||
+ transparencyAttributes.source.capabilityBitsEmpty()) &&
+ ((renderingAttributes == null) ||
+ renderingAttributes.source.capabilityBitsEmpty()) &&
+ ((polygonAttributes == null) ||
+ polygonAttributes.source.capabilityBitsEmpty()) &&
+ ((lineAttributes == null) ||
+ lineAttributes.source.capabilityBitsEmpty()) &&
+ ((pointAttributes == null) ||
+ pointAttributes.source.capabilityBitsEmpty()) &&
+ ((material == null) ||
+ material.source.capabilityBitsEmpty()));
+
+ if (!flag)
+ return flag;
+
+ if (texUnitState != null) {
+ for (int i = 0; i < texUnitState.length && flag; i++) {
+ if (texUnitState[i] != null) {
+ flag = flag && texUnitState[i].isStatic();
+ }
+ }
+ }
+
+ return flag;
+ }
+ /*
+ // Simply pass along to the NodeComponents
+ void compile(CompileState compState) {
+ setCompiled();
+
+ if (texture != null) {
+ texture.compile(compState);
+ }
+
+ if (texCoordGeneration != null) {
+ texCoordGeneration.compile(compState);
+ }
+
+ if (textureAttributes != null) {
+ textureAttributes.compile(compState);
+ }
+
+ if (texUnitState != null) {
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null)
+ texUnitState[i].compile(compState);
+ }
+ }
+
+ if (coloringAttributes != null) {
+ coloringAttributes.compile(compState);
+ }
+
+ if (transparencyAttributes != null) {
+ transparencyAttributes.compile(compState);
+ }
+
+ if (renderingAttributes != null) {
+ renderingAttributes.compile(compState);
+ }
+
+ if (polygonAttributes != null) {
+ polygonAttributes.compile(compState);
+ }
+
+ if (lineAttributes != null) {
+ lineAttributes.compile(compState);
+ }
+
+ if (pointAttributes != null) {
+ pointAttributes.compile(compState);
+ }
+
+ if (material != null) {
+ material.compile(compState);
+ }
+ }
+ */
+
+ /**
+ * Returns the hashcode for this object.
+ * hashcode should be constant for object but same for two objects
+ * if .equals() is true. For an appearance (where .equals() is going
+ * to use the values in the appearance), the only way to have a
+ * constant value is for all appearances to have the same hashcode, so
+ * we use the hashcode of the class obj.
+ *
+ * Since hashCode is only used by AppearanceMap (at present) we may be
+ * able to improve efficency by calcing a hashCode from the values.
+ */
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+ public boolean equals(Object obj) {
+ return ((obj instanceof AppearanceRetained) &&
+ equals((AppearanceRetained) obj));
+ }
+
+ boolean equals(AppearanceRetained app) {
+ boolean flag;
+
+ flag = (app == this) ||
+ ((app != null) &&
+ (((material == app.material) ||
+ ((material != null) && material.equivalent(app.material))) &&
+ ((texture == app.texture) ||
+ ((texture != null) && texture.equals(app.texture))) &&
+ ((renderingAttributes == app.renderingAttributes) ||
+ ((renderingAttributes != null) &&
+ renderingAttributes.equivalent(
+ app.renderingAttributes))) &&
+ ((polygonAttributes == app.polygonAttributes) ||
+ ((polygonAttributes != null) &&
+ polygonAttributes.equivalent(app.polygonAttributes))) &&
+ ((texCoordGeneration == app.texCoordGeneration) ||
+ ((texCoordGeneration != null) &&
+ texCoordGeneration.equivalent(app.texCoordGeneration))) &&
+ ((textureAttributes == app.textureAttributes) ||
+ ((textureAttributes != null) &&
+ textureAttributes.equivalent(app.textureAttributes))) &&
+ ((coloringAttributes == app.coloringAttributes) ||
+ ((coloringAttributes != null) &&
+ coloringAttributes.equivalent(app.coloringAttributes))) &&
+ ((transparencyAttributes == app.transparencyAttributes) ||
+ ((transparencyAttributes != null) &&
+ transparencyAttributes.equivalent(
+ app.transparencyAttributes))) &&
+ ((lineAttributes == app.lineAttributes) ||
+ ((lineAttributes != null) &&
+ lineAttributes.equivalent(app.lineAttributes))) &&
+ ((pointAttributes == app.pointAttributes) ||
+ ((pointAttributes != null) &&
+ pointAttributes.equivalent(app.pointAttributes)))));
+
+ if (!flag)
+ return (flag);
+
+ if (texUnitState == app.texUnitState)
+ return (flag);
+
+ if (texUnitState == null || app.texUnitState == null ||
+ texUnitState.length != app.texUnitState.length)
+ return (false);
+
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] == app.texUnitState[i])
+ continue;
+
+ if (texUnitState[i] == null || app.texUnitState[i] == null ||
+ !texUnitState[i].equals(app.texUnitState[i]))
+ return (false);
+ }
+ return (true);
+ }
+
+
+
+
+ synchronized void addAMirrorUser(Shape3DRetained shape) {
+
+ super.addAMirrorUser(shape);
+ if (material != null)
+ material.addAMirrorUser(shape);
+
+ if (texture != null)
+ texture.addAMirrorUser(shape);
+ if (texCoordGeneration != null)
+ texCoordGeneration.addAMirrorUser(shape);
+ if (textureAttributes != null)
+ textureAttributes.addAMirrorUser(shape);
+
+ if (texUnitState != null) {
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null)
+ texUnitState[i].addAMirrorUser(shape);
+ }
+ }
+
+ if (coloringAttributes != null)
+ coloringAttributes.addAMirrorUser(shape);
+ if (transparencyAttributes != null)
+ transparencyAttributes.addAMirrorUser(shape);
+ if (renderingAttributes != null)
+ renderingAttributes.addAMirrorUser(shape);
+ if (polygonAttributes != null)
+ polygonAttributes.addAMirrorUser(shape);
+ if (lineAttributes != null)
+ lineAttributes.addAMirrorUser(shape);
+ if (pointAttributes != null)
+ pointAttributes.addAMirrorUser(shape);
+ }
+
+ synchronized void removeAMirrorUser(Shape3DRetained shape) {
+ super.removeAMirrorUser(shape);
+ if (material != null)
+ material.removeAMirrorUser(shape);
+ if (texture != null)
+ texture.removeAMirrorUser(shape);
+ if (texCoordGeneration != null)
+ texCoordGeneration.removeAMirrorUser(shape);
+ if (textureAttributes != null)
+ textureAttributes.removeAMirrorUser(shape);
+
+ if (texUnitState != null) {
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null)
+ texUnitState[i].removeAMirrorUser(shape);
+ }
+ }
+
+ if (coloringAttributes != null)
+ coloringAttributes.removeAMirrorUser(shape);
+ if (transparencyAttributes != null)
+ transparencyAttributes.removeAMirrorUser(shape);
+ if (renderingAttributes != null)
+ renderingAttributes.removeAMirrorUser(shape);
+ if (polygonAttributes != null)
+ polygonAttributes.removeAMirrorUser(shape);
+ if (lineAttributes != null)
+ lineAttributes.removeAMirrorUser(shape);
+ if (pointAttributes != null)
+ pointAttributes.removeAMirrorUser(shape);
+ }
+
+ // 3rd argument used only when Rendering Attr comp changes
+ final void sendMessage(int attrMask, Object attr, boolean visible) {
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.APPEARANCE_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.APPEARANCE_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+ // Send the value itself, since Geometry Structure cannot rely on the
+ // mirror (which may be updated lazily)
+ if (attrMask == RENDERING) {
+ if (attr != null) {
+ createMessage.args[4] = visible?Boolean.TRUE:Boolean.FALSE;
+ }
+ else {
+ createMessage.args[4] = Boolean.TRUE;
+ }
+ }
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+
+
+ final void sendRenderingAttributesChangedMessage(boolean visible) {
+
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_GEOMETRY;
+ createMessage.type = J3dMessage.RENDERINGATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1] = null; // Sync with RenderingAttrRetained sendMessage
+ createMessage.args[2]= visible?Boolean.TRUE:Boolean.FALSE;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ boolean isOpaque(int geoType) {
+ TransparencyAttributesRetained ta;
+ int i;
+
+ ta = transparencyAttributes;
+ if (ta != null &&
+ ta.transparencyMode != TransparencyAttributes.NONE &&
+ (VirtualUniverse.mc.isD3D() ||
+ (!VirtualUniverse.mc.isD3D() &&
+ (ta.transparencyMode !=
+ TransparencyAttributes.SCREEN_DOOR)))) {
+ return(false);
+ }
+
+ switch (geoType) {
+ case GeometryRetained.GEO_TYPE_POINT_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET:
+ if ((pointAttributes != null) &&
+ pointAttributes.pointAntialiasing) {
+ return (false);
+ }
+ break;
+ case GeometryRetained.GEO_TYPE_LINE_SET:
+ case GeometryRetained.GEO_TYPE_LINE_STRIP_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ if ((lineAttributes != null) &&
+ lineAttributes.lineAntialiasing) {
+ return (false);
+ }
+ break;
+ case GeometryRetained.GEO_TYPE_RASTER:
+ case GeometryRetained.GEO_TYPE_COMPRESSED:
+ break;
+ default:
+ if (polygonAttributes != null) {
+ if((polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT) &&
+ (pointAttributes != null) &&
+ pointAttributes.pointAntialiasing) {
+ return (false);
+ } else if ((polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE) &&
+ (lineAttributes != null) &&
+ lineAttributes.lineAntialiasing) {
+ return (false);
+ }
+ }
+ break;
+ }
+
+ return(true);
+ }
+
+ void handleFrequencyChange(int bit) {
+ int mask = 0;
+ if (bit == Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE)
+ mask = COLOR;
+ else if(bit == Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE)
+ mask = TRANSPARENCY;
+ else if(bit == Appearance.ALLOW_RENDERING_ATTRIBUTES_WRITE)
+ mask = RENDERING;
+ else if (bit == Appearance.ALLOW_POLYGON_ATTRIBUTES_WRITE)
+ mask = POLYGON;
+ else if (bit == Appearance.ALLOW_LINE_ATTRIBUTES_WRITE)
+ mask = LINE;
+ else if (bit == Appearance.ALLOW_POINT_ATTRIBUTES_WRITE)
+ mask = POINT;
+ else if (bit == Appearance.ALLOW_MATERIAL_WRITE)
+ mask = MATERIAL;
+ else if (bit == Appearance.ALLOW_TEXTURE_WRITE)
+ mask = TEXTURE;
+ else if (bit == Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE)
+ mask = TEXTURE_ATTR;
+ else if (bit == Appearance.ALLOW_TEXGEN_WRITE)
+ mask = TEXCOORD_GEN;
+ else if (bit == Appearance.ALLOW_TEXTURE_UNIT_STATE_WRITE)
+ mask = TEXTURE_UNIT_STATE;
+
+ if (mask != 0)
+ setFrequencyChangeMask(bit, mask);
+ }
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/AssertionFailureException.java b/src/classes/share/javax/media/j3d/AssertionFailureException.java
new file mode 100644
index 0000000..8d8cbda
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AssertionFailureException.java
@@ -0,0 +1,35 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates an assertion failure.
+ */
+
+class AssertionFailureException extends RuntimeException {
+
+ /**
+ * Create the exception object with default values.
+ */
+ AssertionFailureException() {
+ }
+
+ /**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ AssertionFailureException(String str) {
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/AttributeBin.java b/src/classes/share/javax/media/j3d/AttributeBin.java
new file mode 100644
index 0000000..c686d27
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AttributeBin.java
@@ -0,0 +1,475 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+/**
+ * The AttributeBin manages a collection of TextureBin objects.
+ * All objects in the AttributeBin share the same RenderingAttributes
+ */
+
+class AttributeBin extends Object implements ObjectUpdate {
+
+ /**
+ * The RenderingAttributes for this AttributeBin
+ */
+ RenderingAttributesRetained definingRenderingAttributes = null;
+
+ /**
+ * The EnvirionmentSet that this AttributeBin resides
+ */
+ EnvironmentSet environmentSet = null;
+
+ /**
+ * The references to the next and previous AttributeBins in the
+ * list.
+ */
+ AttributeBin next = null;
+ AttributeBin prev = null;
+
+ /**
+ * The list of TextureBins in this AttributeBin
+ */
+ TextureBin textureBinList = null;
+
+
+ /**
+ * The list of TextureBins to be added for the next frame
+ */
+ ArrayList addTextureBins = new ArrayList();
+
+ /**
+ * List of TextureBins to be added next frame
+ */
+ ArrayList addTBs = new ArrayList();
+
+
+ /**
+ * If the RenderingAttribute component of the appearance will be changed
+ * frequently, then confine it to a separate bin
+ */
+ boolean soleUser = false;
+ AppearanceRetained app = null;
+
+ /**
+ * List of TextureBins to be removeded next frame
+ */
+ ArrayList removeTBs = new ArrayList();
+
+ int onUpdateList = 0;
+ static int ON_OBJ_UPDATE_LIST = 0x1;
+ static int ON_CHANGED_FREQUENT_UPDATE_LIST = 0x2;
+
+ // Cache it outside, to avoid the "if" check in renderMethod
+ // for whether the definingRendering attrs is non-null;
+ boolean ignoreVertexColors = false;
+
+ // TODO: use definingMaterial etc. instead of these
+ // when sole user is completely implement
+ RenderingAttributesRetained renderingAttrs;
+
+ int numEditingTextureBins = 0;
+
+
+
+ AttributeBin(AppearanceRetained app, RenderingAttributesRetained renderingAttributes, RenderBin rBin) {
+ reset(app, renderingAttributes, rBin);
+ }
+
+ void reset(AppearanceRetained app, RenderingAttributesRetained renderingAttributes, RenderBin rBin) {
+ prev = null;
+ next = null;
+ textureBinList = null;
+ onUpdateList = 0;
+ numEditingTextureBins = 0;
+ renderingAttrs = renderingAttributes;
+
+ if (app != null) {
+ soleUser = ((app.changedFrequent & AppearanceRetained.RENDERING) != 0);
+ }
+ else {
+ soleUser = false;
+ }
+ // System.out.println("soleUser = "+soleUser+" renderingAttributes ="+renderingAttributes);
+ // Set the appearance only for soleUser case
+ if (soleUser)
+ this.app = app;
+ else
+ app = null;
+
+ if (renderingAttributes != null) {
+ if (renderingAttributes.changedFrequent != 0) {
+ definingRenderingAttributes = renderingAttributes;
+ if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) == 0 ) {
+ rBin.aBinUpdateList.add(this);
+ onUpdateList |= AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
+ }
+ }
+ else {
+ if (definingRenderingAttributes != null) {
+ definingRenderingAttributes.set(renderingAttributes);
+ }
+ else {
+ definingRenderingAttributes = (RenderingAttributesRetained)renderingAttributes.clone();
+ }
+ }
+ ignoreVertexColors = definingRenderingAttributes.ignoreVertexColors;
+ } else {
+ definingRenderingAttributes = null;
+ ignoreVertexColors = false;
+ }
+ }
+
+
+ /**
+ * This tests if the given attributes match this AttributeBin
+ */
+ boolean equals(RenderingAttributesRetained renderingAttributes, RenderAtom ra) {
+
+ // If the any reference to the appearance components that is cached renderMolecule
+ // can change frequently, make a separate bin
+ // If the any reference to the appearance components that is cached renderMolecule
+ // can change frequently, make a separate bin
+ if (soleUser || (ra.geometryAtom.source.appearance != null &&
+ ((ra.geometryAtom.source.appearance.changedFrequent & AppearanceRetained.RENDERING) != 0))) {
+ if (app == (Object)ra.geometryAtom.source.appearance) {
+
+ // if this AttributeBin is currently on a zombie state,
+ // we'll need to put it on the update list to reevaluate
+ // the state, because while it is on a zombie state,
+ // rendering attributes reference could have been changed.
+ // Example, application could have detached an appearance,
+ // made changes to the reference, and then
+ // reattached the appearance. In this case, the rendering
+ // attributes reference change would not have reflected to
+ // the AttributeBin
+
+ if (numEditingTextureBins == 0) {
+ if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) == 0) {
+ environmentSet.renderBin.aBinUpdateList.add(this);
+ onUpdateList |=
+ AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
+ }
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+
+ }
+ // Either a changedFrequent or a null case
+ // and the incoming one is not equal or null
+ // then return;
+ // This check also handles null == null case
+ if (definingRenderingAttributes != null) {
+ if ((this.definingRenderingAttributes.changedFrequent != 0) ||
+ (renderingAttributes !=null && renderingAttributes.changedFrequent != 0))
+ if (definingRenderingAttributes == renderingAttributes) {
+ if (definingRenderingAttributes.compChanged != 0) {
+ if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) == 0 ) {
+ environmentSet.renderBin.aBinUpdateList.add(this);
+ onUpdateList |= AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
+ }
+ }
+ }
+ else {
+ return false;
+ }
+ else if (!definingRenderingAttributes.equivalent(renderingAttributes)) {
+ return false;
+ }
+ }
+ else if (renderingAttributes != null) {
+ return false;
+ }
+
+
+
+ return (true);
+ }
+
+
+ public void updateObject() {
+ TextureBin t;
+ int i;
+
+ if (addTBs.size() > 0) {
+ t = (TextureBin)addTBs.get(0);
+ if (textureBinList == null) {
+ textureBinList = t;
+
+ }
+ else {
+ // Look for a TextureBin that has the same texture
+ insertTextureBin(t);
+ }
+ for (i = 1; i < addTBs.size() ; i++) {
+ t = (TextureBin)addTBs.get(i);
+ // Look for a TextureBin that has the same texture
+ insertTextureBin(t);
+
+ }
+ }
+ addTBs.clear();
+ onUpdateList &= ~ON_OBJ_UPDATE_LIST;
+ }
+
+ void insertTextureBin(TextureBin t) {
+ TextureBin tb;
+ int i;
+ TextureRetained texture = null;
+
+ if (t.texUnitState != null && t.texUnitState.length > 0) {
+ if (t.texUnitState[0] != null) {
+ texture = t.texUnitState[0].texture;
+ }
+ }
+
+ // use the texture in the first texture unit as the sorting criteria
+ if (texture != null) {
+ tb = textureBinList;
+ while (tb != null) {
+ if (tb.texUnitState == null || tb.texUnitState[0] == null ||
+ tb.texUnitState[0].texture != texture) {
+ tb = tb.next;
+ } else {
+ // put it here
+ t.next = tb;
+ t.prev = tb.prev;
+ if (tb.prev == null) {
+ textureBinList = t;
+ }
+ else {
+ tb.prev.next = t;
+ }
+ tb.prev = t;
+ return;
+ }
+ }
+ }
+ // Just put it up front
+ t.prev = null;
+ t.next = textureBinList;
+ textureBinList.prev = t;
+ textureBinList = t;
+
+ t.tbFlag &= ~TextureBin.RESORT;
+ }
+
+
+ /**
+ * reInsert textureBin if the first texture is different from
+ * the previous bin and different from the next bin
+ */
+ void reInsertTextureBin(TextureBin tb) {
+
+ TextureRetained texture = null,
+ prevTexture = null,
+ nextTexture = null;
+
+ if (tb.texUnitState != null && tb.texUnitState[0] != null) {
+ texture = tb.texUnitState[0].texture;
+ }
+
+ if (tb.prev != null && tb.prev.texUnitState != null) {
+ prevTexture = tb.prev.texUnitState[0].texture;
+ }
+
+ if (texture != prevTexture) {
+ if (tb.next != null && tb.next.texUnitState != null) {
+ nextTexture = tb.next.texUnitState[0].texture;
+ }
+ if (texture != nextTexture) {
+ if (tb.prev != null && tb.next != null) {
+ tb.prev.next = tb.next;
+ tb.next.prev = tb.prev;
+ insertTextureBin(tb);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Adds the given TextureBin to this AttributeBin.
+ */
+ void addTextureBin(TextureBin t, RenderBin rb, RenderAtom ra) {
+ int i;
+ t.attributeBin = this;
+ AppearanceRetained raApp = ra.geometryAtom.source.appearance;
+ RenderingAttributesRetained rAttrs =
+ (raApp == null)? null : raApp.renderingAttributes;
+ if (!soleUser && renderingAttrs != rAttrs) {
+ // no longer sole user
+ renderingAttrs = definingRenderingAttributes;
+ }
+ addTBs.add(t);
+ if ((onUpdateList & ON_OBJ_UPDATE_LIST) == 0) {
+ onUpdateList |= ON_OBJ_UPDATE_LIST;
+ rb.objUpdateList.add(this);
+ }
+
+ }
+
+ /**
+ * Removes the given TextureBin from this AttributeBin.
+ */
+ void removeTextureBin(TextureBin t) {
+
+ int i;
+ TextureRetained tex;
+
+ t.attributeBin = null;
+ // If the TextureBin being remove is contained in addTBs, then
+ // remove the TextureBin from the addList
+ if (addTBs.contains(t)) {
+ addTBs.remove(addTBs.indexOf(t));
+ }
+ else {
+ if (t.prev == null) { // At the head of the list
+ textureBinList = t.next;
+ if (t.next != null) {
+ t.next.prev = null;
+ }
+ } else { // In the middle or at the end.
+ t.prev.next = t.next;
+ if (t.next != null) {
+ t.next.prev = t.prev;
+ }
+ }
+ }
+ t.prev = null;
+ t.next = null;
+
+ t.clear();
+
+ environmentSet.renderBin.textureBinFreelist.add(t);
+
+ if (textureBinList == null && addTBs.size() == 0 ) {
+ // Note: Removal of this attributebin as a user of the rendering
+ // atttrs is done during removeRenderAtom() in RenderMolecule.java
+ environmentSet.removeAttributeBin(this);
+ }
+ }
+
+ /**
+ * Renders this AttributeBin
+ */
+ void render(Canvas3D cv) {
+
+ TextureBin t;
+
+ boolean visible = (definingRenderingAttributes == null ||
+ definingRenderingAttributes.visible);
+
+ if ( (environmentSet.renderBin.view.viewCache.visibilityPolicy
+ == View.VISIBILITY_DRAW_VISIBLE && !visible) ||
+ (environmentSet.renderBin.view.viewCache.visibilityPolicy
+ == View.VISIBILITY_DRAW_INVISIBLE && visible)) {
+ return;
+ }
+
+
+ // include this AttributeBin to the to-be-updated list in Canvas
+ cv.setStateToUpdate(Canvas3D.ATTRIBUTEBIN_BIT, this);
+
+ t = textureBinList;
+ while (t != null) {
+ t.render(cv);
+ t = t.next;
+ }
+ }
+
+
+ void updateAttributes(Canvas3D cv) {
+ if ((cv.canvasDirty & Canvas3D.ATTRIBUTEBIN_DIRTY) != 0) {
+ // Update Attribute Bundles
+ if (definingRenderingAttributes == null) {
+ cv.resetRenderingAttributes(cv.ctx,
+ cv.depthBufferWriteEnableOverride,
+ cv.depthBufferEnableOverride);
+ } else {
+ definingRenderingAttributes.updateNative(
+ cv.ctx,
+ cv.depthBufferWriteEnableOverride,
+ cv.depthBufferEnableOverride);
+ }
+ cv.renderingAttrs = renderingAttrs;
+ }
+
+ else if (cv.renderingAttrs != renderingAttrs &&
+ cv.attributeBin != this) {
+ // Update Attribute Bundles
+ if (definingRenderingAttributes == null) {
+ cv.resetRenderingAttributes(
+ cv.ctx,
+ cv.depthBufferWriteEnableOverride,
+ cv.depthBufferEnableOverride);
+ } else {
+ definingRenderingAttributes.updateNative(
+ cv.ctx,
+ cv.depthBufferWriteEnableOverride,
+ cv.depthBufferEnableOverride);
+ }
+ cv.renderingAttrs = renderingAttrs;
+ }
+ cv.attributeBin = this;
+ cv.canvasDirty &= ~Canvas3D.ATTRIBUTEBIN_DIRTY;
+ }
+
+ void updateNodeComponent() {
+ // May be in the freelist already (due to freq bit changing)
+ // if so, don't update anything
+ if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) != 0) {
+ if (soleUser) {
+ boolean cloned = definingRenderingAttributes != null && definingRenderingAttributes != renderingAttrs;
+ renderingAttrs = app.renderingAttributes;
+
+ if (renderingAttrs == null) {
+ definingRenderingAttributes = null;
+ ignoreVertexColors = false;
+ }
+ else {
+ if (renderingAttrs.changedFrequent != 0) {
+ definingRenderingAttributes = renderingAttrs;
+ }
+ else {
+ if (cloned) {
+ definingRenderingAttributes.set(renderingAttrs);
+ }
+ else {
+ definingRenderingAttributes = (RenderingAttributesRetained)renderingAttrs.clone();
+ }
+ }
+ ignoreVertexColors = definingRenderingAttributes.ignoreVertexColors;
+ }
+ }
+ else {
+ ignoreVertexColors = definingRenderingAttributes.ignoreVertexColors;
+ }
+ }
+
+ onUpdateList &= ~ON_CHANGED_FREQUENT_UPDATE_LIST;
+ }
+
+ void incrActiveTextureBin() {
+ numEditingTextureBins++;
+ }
+
+ void decrActiveTextureBin() {
+ numEditingTextureBins--;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/AudioDevice.java b/src/classes/share/javax/media/j3d/AudioDevice.java
new file mode 100644
index 0000000..314c7de
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AudioDevice.java
@@ -0,0 +1,257 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The AudioDevice Class defines and encapsulates the
+ * audio device's basic information and characteristics.
+ * <P>
+ * A Java3D application running on a particular machine could have one of
+ * several options available to it for playing the audio image created by the
+ * sound renderer. Perhaps the machine Java3D is executing on has more than
+ * one sound card (e.g., one that is a Wave Table Synthesis card and the other
+ * with accelerated sound spatialization hardware). Furthermore, suppose there
+ * are Java3D audio device drivers that execute Java3D audio methods on each of
+ * these specific cards. In such a case the application would have at least two
+ * audio device drivers through which the audio could be produced. For such a
+ * case the Java3D application must choose the audio device driver with which
+ * sound rendering is to be performed. Once this audio device is chosen, the
+ * application can additionally select the type of audio playback type the
+ * rendered sound image is to be output on. The playback device (headphones or
+ * speaker(s)) is physically connected to the port the selected device driver
+ * outputs to.
+ *<P>
+ * AudioDevice Interface
+ *<P>
+ *<UL> The selection of this device driver is done through methods in the
+ * PhysicalEnvironment object - see PhysicalEnvironment class.
+ * The application would query how many audio devices are available. For
+ * each device, the user can get the AudioDevice object that describes it
+ * and query its characteristics. Once a decision is made about which of
+ * the available audio devices to use for a PhysicalEnvironment, the
+ * particular device is set into this PhysicalEnvironment's fields. Each
+ * PhysicalEnvironment object may use only a single audio device.
+ *<P>
+ * The AudioDevice object interface specifies an abstract input device
+ * that creators of Java3D class libraries would implement for a particular
+ * device. Java3D's uses several methods to interact with specific devices.
+ * Since all audio devices implement this consistent interface, the user
+ * could have a portable means of initialize, set particular audio device
+ * elements and query generic characteristics for any audio device.
+ *<P>
+ *Initialization
+ *<P><UL>
+ * Each audio device driver must be initialized.
+ * The chosen device driver should be initialized before any Java3D
+ * Sound methods are executed because the implementation of the Sound
+ * methods, in general, are potentially device driver dependent.</UL>
+ *<P>
+ * Audio Playback Type
+ *<P><UL>
+ * These methods set and retrieve the audio playback type used to output
+ * the analog audio from rendering Java3D Sound nodes.
+ * The audio playback type specifies that playback will be through:
+ * stereo headphones, a monaural speaker, or a pair of speakers.
+ * For the stereo speakers, it is assumed that the two output speakers are
+ * equally distant from the listener, both at same angle from the head
+ * axis (thus oriented symmetrically about the listener), and at the same
+ * elevation.
+ * The type of playback chosen affects the sound image generated.
+ * Cross-talk cancellation is applied to the audio image if playback over
+ * stereo speakers is selected.</UL>
+ *<P>
+ * Distance to Speaker
+ *<P><UL>
+ * These methods set and retrieve the distance in meters from the center
+ * ear (the midpoint between the left and right ears) and one of the
+ * speakers in the listener's environment. For monaural speaker playback,
+ * a typical distance from the listener to the speaker in a workstation
+ * cabinet is 0.76 meters. For stereo speakers placed at the sides of the
+ * display, this might be 0.82 meters.</UL>
+ *<P>
+ * Angular Offset of Speakers
+ *<P><UL>
+ * These methods set and retrieve the angle in radians between the vectors
+ * from the center ear to each of the speaker transducers and the vectors
+ * from the center ear parallel to the head coordinate's Z axis. Speakers
+ * placed at the sides of the computer display typically range between
+ * 0.28 to 0.35 radians (between 10 and 20 degrees).</UL>
+ *<P>
+ * Device Driver Specific Data
+ *<P><UL>
+ * While the sound image created for final output to the playback system
+ * is either only mono or stereo (for this version of Java3D) most device
+ * driver implementations will mix the left and right image signals
+ * generated for each rendered sound source before outputting the final
+ * playback image. Each sound source will use N input channels of this
+ * internal mixer. Each implemented Java3D audio device driver will have
+ * its own limitations and driver-specific characteristics. These include
+ * channel availability and usage (during rendering). Methods for
+ * querying these device-driver specific characteristics are provided.</UL></UL>
+ *<P>
+ * Instantiating and Registering a New Device
+ *<P>
+ *<UL> A browser or applications developer must instantiate whatever system-
+ * specific audio devices that he or she needs and that exist on the system.
+ * This device information typically exists in a site configuration file.
+ * The browser or application will instantiate the physical environment as
+ * requested by the end-user.
+ *<P>
+ * The API for instantiating devices is site-specific, but it consists of
+ * a device object with a constructor and at least all of the methods
+ * specified in the AudioDevice interface.
+ *<P>
+ * Once instantiated, the browser or application must register the device
+ * with the Java3D sound scheduler by associating this device with a
+ * PhysicalEnvironment. The setAudioDevice method introduces new devices
+ * to the Java3D environment and the allAudioDevices method produces an
+ * enumeration that allows examining all available devices within a Java3D
+ * environment. See PhysicalEnvironment class for more details.</UL>
+ * <P>
+ * General Rules for calling AudioDevice methods:
+ * It is illegal for an application to call any non-query AudioDevice method
+ * if the AudioDevice is created then explicitly assigned to a
+ * PhysicalEnvironment using PhysicalEnvironment.setAudioDevice();
+ * When either PhysicalEnvironment.setAudioDevice() is called - including
+ * when implicitly called by SimpleUniverse.getViewer().createAudioDevice()
+ * - the Core creates a SoundScheduler thread which makes calls to
+ * the AudioDevice.
+ * <P>
+ * If an application creates it's own instance of an AudioDevice and
+ * initializes it directly, rather than using PhysicalEnvironment.
+ * setAudioDevice(), that application may make <i>any</i> AudioDevice3D methods calls
+ * without fear of the Java 3D Core also trying to control the AudioDevice.
+ * Under this condition it is safe to call AudioDevice non-query methods.
+ */
+
+public interface AudioDevice {
+
+ /** *************
+ *
+ * Constants
+ *
+ ****************/
+ /**
+ * Audio Playback Types
+ *
+ * Types of audio output device Java3D sound is played over:
+ * Headphones, MONO_SPEAKER, STEREO_SPEAKERS
+ */
+ /**
+ * Choosing Headphones as the audio playback type
+ * specifies that the audio playback will be through stereo headphones.
+ */
+ public static final int HEADPHONES = 0;
+
+ /**
+ * Choosing a
+ * single near-field monoaural speaker
+ * as the audio playback type
+ * specifies that the audio playback will be through a single speaker
+ * some supplied distance away from the listener.
+ */
+ public static final int MONO_SPEAKER = 1;
+
+ /**
+ * Choosing a
+ * two near-field stereo speakers
+ * as the audio playback type
+ * specifies that the audio playback will be through stereo speakers
+ * some supplied distance away from, and at some given angle to
+ * the listener.
+ */
+ public static final int STEREO_SPEAKERS = 2;
+
+ /**
+ * Initialize the audio device.
+ * Exactly what occurs during initialization is implementation dependent.
+ * This method provides explicit control by the user over when this
+ * initialization occurs.
+ * Initialization must be initiated before any other AudioDevice
+ * methods are called.
+ * @return true if initialization was successful without errors
+ */
+ public abstract boolean initialize();
+
+ /**
+ * Code to close the device and release resources.
+ * @return true if close of device was successful without errors
+ */
+ public abstract boolean close();
+
+ /**
+ * Set Type of Audio Playback physical transducer(s) sound is output to.
+ * Valid types are HEADPHONES, MONO_SPEAKER, STEREO_SPEAKERS
+ * @param type audio playback type
+ */
+ public abstract void setAudioPlaybackType(int type);
+
+ /**
+ * Get Type of Audio Playback Output Device.
+ * @return audio playback type
+ */
+ public abstract int getAudioPlaybackType();
+
+ /**
+ * Set Distance from interaural mid-point between Ears to a Speaker.
+ * @param distance from interaural midpoint between the ears to closest speaker
+ */
+ public abstract void setCenterEarToSpeaker(float distance);
+
+ /**
+ * Get Distance from interaural mid-point between Ears to a Speaker.
+ * @return distance from interaural midpoint between the ears to closest speaker
+ */
+ public abstract float getCenterEarToSpeaker();
+
+ /**
+ * Set Angle Offset (in radians) To Speaker.
+ * @param angle in radians from head Z axis and vector from center ear to speaker
+ */
+ public abstract void setAngleOffsetToSpeaker(float angle);
+
+ /**
+ * Get Angle Offset (in radians) To Speaker.
+ * @return angle in radians from head Z axis and vector from center ear to speaker
+ */
+ public abstract float getAngleOffsetToSpeaker();
+
+ /**
+ * Query total number of channels available for sound rendering
+ * for this audio device. This returns the maximum number of channels
+ * available for Java3D sound rendering for all sound sources.
+ * @return total number of channels that can be used for this audio device
+ */
+ public abstract int getTotalChannels();
+
+ /**
+ * Query number of channels currently available for use.
+ * During rendering, when sound nodes are playing, this method returns the
+ * number of channels still available to Java3D for rendering additional
+ * sound nodes.
+ * @return total number of channels current available
+ */
+ public abstract int getChannelsAvailable();
+
+ /**
+ * Query number of channels that are used, or would be used to render
+ * a particular sound node. This method returns the number of channels
+ * needed to render a particular Sound node. The return value is the same
+ * no matter if the Sound is currently active and enabled (being played) or
+ * is inactive.
+ * @return number of channels a particular Sound node is using or would used
+ * if enabled and activated (rendered).
+ */
+ public abstract int getChannelsUsedForSound(Sound node);
+}
diff --git a/src/classes/share/javax/media/j3d/AudioDevice3D.java b/src/classes/share/javax/media/j3d/AudioDevice3D.java
new file mode 100644
index 0000000..4ae2fcc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AudioDevice3D.java
@@ -0,0 +1,515 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+
+/**
+ * The AudioDevice3D class defines a 3D audio device that is used to set
+ * sound and aural attributes.
+ *<P>
+ * After the application chooses the AudioDevice3D that Java3D sound
+ * is to be rendered on, the Java 3D Sound Scheduler will call these
+ * methods for all active sounds to render them on the audio device.
+ *<P>
+ * The intent is for this interface to be implemented by AudioDevice Driver
+ * developers using a software or hardware sound engine of their choice.
+ *<P>
+ * Methods in this interface provide the Java3D Core a generic way to
+ * set and query the audio device the application has chosen audio rendering
+ * to be performed on. Methods in this interface include:
+ * <UL>
+ * Set up and clear the sound as a sample on the device.
+ * <P>
+ * Start, stop, pause, unpause, mute, and unmute of sample on the device.
+ * <P>
+ * Set parameters for each sample corresponding to the fields in the
+ * Sound node.
+ * <P>
+ * Set the current active aural parameters that affect all positional samples.
+ * </UL>
+ * <P>
+ * Sound Types
+ * <P>
+ * Sound types match the Sound node classes defined for Java 3D core
+ * for BackgroundSound, PointSound, and ConeSound. The type of sound
+ * a sample is loaded as determines which methods affect it.
+ *
+ * <P>
+ * Sound Data Types
+ * <P>
+ * Samples can be processed as streaming or buffered data.
+ * Fully spatializing sound sources may require data to be buffered.
+ *
+ */
+
+public interface AudioDevice3D extends AudioDevice {
+
+ /**
+ * Specifies the sound type as background sound.
+ */
+ public static final int BACKGROUND_SOUND = 1;
+
+ /**
+ * Specifies the sound type as point sound.
+ */
+ public static final int POINT_SOUND = 2;
+
+ /**
+ * Specifies the sound type as cone sound.
+ */
+
+ public static final int CONE_SOUND = 3;
+
+ /**
+ * Sound data specified as Streaming is not copied by the AudioDevice
+ * driver implementation. It is up to the application to ensure that
+ * this data is continuously accessible during sound rendering.
+ * Furthermore, full sound spatialization may not be possible, for
+ * all AudioDevice3D implementations on unbuffered sound data.
+ */
+ public static final int STREAMING_AUDIO_DATA = 1;
+ /**
+ * Sound data specified as Buffered is copied by the AudioDevice
+ * driver implementation.
+ */
+ public static final int BUFFERED_AUDIO_DATA = 2;
+
+
+ /**
+ * Accepts a reference to the current View.
+ * Passes reference to current View Object. The PhysicalEnvironment
+ * parameters (with playback type and speaker placement), and the
+ * PhysicalBody parameters (position/orientation of ears)
+ * can be obtained from this object, and the transformations to/from
+ * ViewPlatform coordinate (space the listener's head is in) and
+ * Virtual World coordinates (space sounds are in).
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param reference the current View
+ */
+ public abstract void setView(View reference);
+
+ /**
+ * Accepts a reference to the MediaContainer
+ * which contains a reference to sound data and information about the
+ * type of data it is. A "sound type" input parameter denotes if the
+ * Java 3D sound associated with this sample is a Background, Point, or
+ * Cone Sound node.
+ * Depending on the type of MediaContainer the sound data is and on the
+ * implementation of the AudioDevice used, sound data preparation could
+ * consist of opening, attaching, or loading sound data into the device.
+ * Unless the cached flag is true, this sound data should NOT be copied,
+ * if possible, into host or device memory.
+ *<P>
+ * Once this preparation is complete for the sound sample, an AudioDevice
+ * specific index, used to reference the sample in future method calls,
+ * is returned. All the rest of the methods described below require
+ * this index as a parameter.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param soundType defines the type of Sound Node: Background, Point, and
+ * Cone
+ * @param soundData reference to MediaContainer sound data and cached flag
+ * @return device specific sample index used for referencing this sound
+ */
+ public abstract int prepareSound(int soundType, MediaContainer soundData);
+
+ /**
+ * Requests that the AudioDevice free all
+ * resources associated with sample with index id.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ */
+ public abstract void clearSound(int index);
+
+ /**
+ * Returns the duration in milliseconds of the sound sample,
+ * if this information can be determined.
+ * For non-cached
+ * streams, this method returns Sound.DURATION_UNKNOWN.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @return sound duration in milliseconds if this can be determined,
+ * otherwise (for non-cached streams) Sound.DURATION_UNKNOWN is returned
+ */
+ public abstract long getSampleDuration(int index);
+
+ /**
+ *
+ * Retrieves the number of channels (on executing audio device) that
+ * this sound is using, if it is playing, or is expected to use
+ * if it were begun to be played. This form of this method takes the
+ * sound's current state (including whether it is muted or unmuted)
+ * into account.
+ *<P>
+ * For some AudioDevice3D implementations:
+ *<UL>
+ * Muted sound take channels up on the systems mixer (because they're
+ * rendered as samples playing with gain zero.
+ *<P>
+ * A single sound could be rendered using multiple samples, each taking
+ * up mixer channels.
+ *</UL>
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @return number of channels used by sound if it were playing
+ */
+ public abstract int getNumberOfChannelsUsed(int index);
+
+ /**
+ *
+ * Retrieves the number of channels (on executing audio device) that
+ * this sound is using, if it is playing, or is projected to use if
+ * it were to be started playing. Rather than using the actual current
+ * muted/unmuted state of the sound, the muted parameter is used in
+ * making the determination.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param muted flag to use as the current muted state ignoring current
+ * mute state
+ * @return number of channels used by sound if it were playing
+ */
+ public abstract int getNumberOfChannelsUsed(int index, boolean muted);
+
+ /**
+ * Begins a sound playing on the AudioDevice.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @return flag denoting if sample was started; 1 if true, 0 if false
+ */
+ public abstract int startSample(int index);
+
+ /**
+ * Returns the system time of when the sound
+ * was last "started". Note that this start time will be as accurate
+ * as the AudioDevice implementation can make it - but that it is not
+ * guaranteed to be exact.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @return system time in milliseconds of the last time sound was started
+ */
+ public abstract long getStartTime(int index);
+
+ /**
+ * Stops the sound on the AudioDevice.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * associated with sound data to be played
+ * @return flag denoting if sample was stopped; 1 if true, 0 if false
+ */
+ public abstract int stopSample(int index);
+
+ /**
+ * Sets the overall gain scale factor applied to data associated with this
+ * source to increase or decrease its overall amplitude.
+ * The gain scale factor value passed into this method is the combined value
+ * of the Sound node's Initial Gain and the current AuralAttribute Gain
+ * scale factors.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param scaleFactor amplitude (gain) scale factor
+ */
+ public abstract void setSampleGain(int index, float scaleFactor);
+
+ /**
+ * Sets a sound's loop count.
+ * A full description of this parameter and how it is used is in
+ * the documentation for Sound.setLoop.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param count number of times sound is looped during play
+ * @see Sound#setLoop
+ */
+ public abstract void setLoop(int index, int count);
+
+ /**
+ * Passes a reference to the concatenated transformation to be applied to
+ * local sound position and direction parameters.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param trans transformation matrix applied to local coordinate parameters
+ */
+ public abstract void setVworldXfrm(int index, Transform3D trans);
+
+
+ /**
+ * Sets this sound's location (in Local coordinates) from specified
+ * Point. The form of the position parameter matches those of the PointSound
+ * method of the same name.
+ * A full description of this parameter and how it is used is in
+ * the documentation for PointSound class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param position location of Point or Cone Sound in Virtual World
+ * coordinates
+ * @see PointSound#setPosition(float x, float y, float z)
+ * @see PointSound#setPosition(Point3f position)
+ */
+ public abstract void setPosition(int index, Point3d position);
+
+ /**
+ * Sets this sound's distance gain elliptical attenuation (not
+ * including filter cutoff frequency) by defining corresponding
+ * arrays containing distances from the sound's origin and gain
+ * scale factors applied to all active positional sounds.
+ * Gain scale factor is applied to sound based on the distance
+ * the listener
+ * is from sound source.
+ * These attenuation parameters are ignored for BackgroundSound nodes.
+ * The back attenuation parameter is ignored for PointSound nodes.
+ * <P>
+ * The form of the attenuation parameters match that of the ConeSound method
+ * of the same name.
+ * A full description of this parameter and how it is used is in
+ * the documentation for ConeSound class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param frontDistance defines an array of distance along positive axis
+ * through which ellipses pass
+ * @param frontAttenuationScaleFactor gain scale factors
+ * @param backDistance defines an array of distance along the negative axis
+ * through which ellipses pass
+ * @param backAttenuationScaleFactor gain scale factors
+ * @see ConeSound#setDistanceGain(float[] frontDistance, float[] frontGain,
+ * float[] backDistance, float[] backGain)
+ * @see ConeSound#setDistanceGain(Point2f[] frontAttenuation,
+ * Point2f[] backAttenuation)
+ */
+ public abstract void setDistanceGain(int index,
+ double[] frontDistance, float[] frontAttenuationScaleFactor,
+ double[] backDistance, float[] backAttenuationScaleFactor);
+ /**
+ * Sets this sound's direction from the local coordinate vector provided.
+ * The form of the direction parameter matches that of the ConeSound method
+ * of the same name.
+ * A full description of this parameter and how it is used is in
+ * the documentation for the ConeSound class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param direction the new direction vector in local coordinates
+ * @see ConeSound#setDirection(float x, float y, float z)
+ * @see ConeSound#setDirection(Vector3f direction)
+ */
+ public abstract void setDirection(int index, Vector3d direction);
+
+ /**
+ * Sets this sound's angular gain attenuation (including filter)
+ * by defining corresponding arrays containing angular offsets from
+ * the sound's axis, gain scale factors, and frequency cutoff applied
+ * to all active directional sounds.
+ * Gain scale factor is applied to sound based on the angle between the
+ * sound's axis and the ray from the sound source origin to the listener.
+ * The form of the attenuation parameter matches that of the ConeSound
+ * method of the same name.
+ * A full description of this parameter and how it is used is in
+ * the documentation for the ConeSound class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ * @param filterType describes type (if any) of filtering defined by attenuation
+ * @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.
+ * @see ConeSound#setAngularAttenuation(float[] distance, float[] gain,
+ * float[] filter)
+ * @see ConeSound#setAngularAttenuation(Point3f[] attenuation)
+ * @see ConeSound#setAngularAttenuation(Point2f[] attenuation)
+ */
+ public abstract void setAngularAttenuation(int index, int filterType,
+ double[] angle, float[] attenuationScaleFactor, float[] filterCutoff);
+
+ /**
+ * Changes the speed of sound factor.
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param rolloff atmospheric gain scale factor (changing speed of sound)
+ * @see AuralAttributes#setRolloff
+ */
+ public abstract void setRolloff(float rolloff);
+
+ /**
+ * Sets the Reflective Coefficient scale factor applied to distinct
+ * low-order early reflections of sound off the surfaces in the region
+ * defined by the current listening region.
+ * <P>
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param coefficient reflection/absorption factor applied to reverb
+ * @see AuralAttributes#setReflectionCoefficient
+ */
+ public abstract void setReflectionCoefficient(float coefficient);
+
+ /**
+ * Sets the reverberation delay time.
+ * In this form, while reverberation is being rendered, the parameter
+ * specifies the delay time between each order of late reflections
+ * explicitly given in milliseconds.
+ * A value for delay time of 0.0 disables
+ * reverberation.
+ * <P>
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param reverbDelay time between each order of late reflection
+ * @see AuralAttributes#setReverbDelay(float reverbDelay)
+ */
+ public abstract void setReverbDelay(float reverbDelay);
+
+ /**
+ * Sets the reverberation order of reflections.
+ * The reverbOrder parameter specifies the number of times reflections are added to
+ * reverberation being calculated. A value of -1 specifies an unbounded
+ * number of reverberations.
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param reverbOrder number of times reflections added to reverb signal
+ * @see AuralAttributes#setReverbOrder
+ */
+ public abstract void setReverbOrder(int reverbOrder);
+
+ /**
+ * Sets Distance Filter corresponding arrays containing distances and
+ * frequency cutoff applied to all active positional sounds.
+ * Gain scale factor is applied to sound based on the distance the listener
+ * is from the sound source.
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param filterType denotes the type of filtering to be applied
+ * @param distance array of offset distances from sound origin
+ * @param filterCutoff array of frequency cutoff
+ * @see AuralAttributes#setDistanceFilter(float[] distance,
+ * float[] frequencyCutoff)
+ * @see AuralAttributes#setDistanceFilter(Point2f[] attenuation)
+ */
+ public abstract void setDistanceFilter(int filterType,
+ double[] distance, float[] filterCutoff);
+
+ /**
+ * Specifies a scale factor applied to the frequency (or
+ * wavelength). A value less than 1.0 will result of slowing the playback
+ * rate of the sample. A value greater than 1.0 will increase the playback
+ * rate.
+ * This parameter is also used to expand or contract the usual
+ * frequency shift applied to the sound source due to Doppler effect
+ * calculations. Valid values are >= 0.0.
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param frequencyScaleFactor factor applied to change of frequency
+ * @see AuralAttributes#setFrequencyScaleFactor
+ */
+ public abstract void setFrequencyScaleFactor(float frequencyScaleFactor);
+
+ /**
+ * Sets the Velocity scale factor applied during Doppler Effect calculation.
+ * This parameter specifies a scale factor applied to the velocity of sound
+ * relative to the listener's position and movement in relation to the sound's
+ * position and movement. This scale factor is multipled by the calculated
+ * velocity portion of the Doppler effect equation used during sound rendering.
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param velocityScaleFactor applied to velocity of sound in relation
+ * to listener
+ * @see AuralAttributes#setVelocityScaleFactor
+ */
+ public abstract void setVelocityScaleFactor(float velocityScaleFactor);
+
+ /**
+ * Makes the sample 'play silently'.
+ * This method implements (as efficiently as possible) the muting
+ * of a playing sound sample. Ideally this is implemented by
+ * stopping a sample and freeing channel resources (rather than
+ * just setting the gain of the sample to zero).
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ */
+ public abstract void muteSample(int index);
+
+ /**
+ * Makes a silently playing sample audible.
+ * In the ideal, this restarts a muted sample by offset from the
+ * beginning by the number of milliseconds since the time the sample
+ * began playing (rather than setting gain to current non-zero gain).
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ */
+ public abstract void unmuteSample(int index);
+
+ /**
+ * Temporarily stops a cached sample from playing without resetting the
+ * sample's current pointer back to the beginning of the sound data so
+ * that it can be unpaused at a later time from the same location in the
+ * sample when the pause was initiated. Pausing a streaming, non-cached
+ * sound sample will be treated as a mute.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ */
+ public abstract void pauseSample(int index);
+
+ /**
+ * Restarts the paused sample from the location in the sample where
+ * paused.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ */
+ public abstract void unpauseSample(int index);
+
+ /**
+ *
+ * Explicitly updates a Sample.
+ * This method is called when a Sound is to be explicitly updated.
+ * It is only called when all a sounds parameters are known to have
+ * been passed to the audio device. In this way, an implementation
+ * can choose to perform lazy-evaluation of a sample, rather than
+ * updating the rendering state of the sample after every individual
+ * parameter changed.
+ * This method can be left as a null method if the implementor so chooses.
+ * <P>
+ * This method should only be called by Java3D Core and NOT by any application.
+ * @param index device specific reference number to device driver sample
+ */
+ public abstract void updateSample(int index);
+
+}
diff --git a/src/classes/share/javax/media/j3d/AudioDevice3DL2.java b/src/classes/share/javax/media/j3d/AudioDevice3DL2.java
new file mode 100644
index 0000000..2ba4c6a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AudioDevice3DL2.java
@@ -0,0 +1,305 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * Extends AudioDevice3D to include reverb and environmental audio parameters
+ * that are defined in the MIDI Manufactures' Association Interactive Audio
+ * Special Interest Group (MMA IASIG) Level 2 Specification.
+ *<P>
+ * The reverberation methods of AudioDevice3DL2 interface augment the
+ * reverberation methods defined in AudioDevice3D.
+ *<P>
+ * The intent is for this interface to be implemented by AudioDevice Driver
+ * developers using a software or hardware sound engine of their choice.
+ *<P>
+ * Methods in this interface provide the Java3D Core a generic way to
+ * set and query the audio device the application has chosen audio rendering
+ * to be performed on.
+ *<P>
+ * The non-query methods of this interface should only be called by
+ * an application if the AudioDevice instance
+ * is not referenced by any PhysicalEnvironment
+ * explicitly with .setAudioDevice() or implicitly through Universe
+ * utility method in which case these are called by Core Java 3D
+ * Sound classes and Sound Scheduler thread(s).
+ *<P>
+ * After the application chooses the AudioDevice3DL2 implementation
+ * that Java3D sound is to be rendered on, the Java 3D Sound Scheduler
+ * will call these methods for all active sounds to render them on the
+ * audio device.
+ *<P>
+ * The AudioDevice3DL2 methods should not be call by any application if the
+ * audio device is associated with a Physical Environment and thus used by
+ * Java3D Core.
+ *<P>
+ * Filtering for this extended AudioDevice interface is defined uniformly as
+ * a simple low-pass filter defined by a cutoff frequency. This will allow the
+ * same type of high-frequency attenuation that the MMA IASIG Level 2 filtering
+ * model with its 'reference frequency' and 'attenuation ratio' parameters
+ * affords. Use of a cutoff frequency is consistent with the filtering type
+ * for distance and angular attenuation for ConeSound and AuralAttributes.
+ * The filter methods will likely be overloaded in some future extension of this
+ * interface.
+ *
+ * @see Sound
+ * @see AuralAttributes
+ * @see AudioDevice3D
+ * @since Java 3D 1.3
+ */
+
+public interface AudioDevice3DL2 extends AudioDevice3D {
+
+ /**
+ * Pause audio device engine (thread/server) without closing the device.
+ * Causes all cached sounds to be paused and all streaming sounds to be
+ * stopped.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * This method will be implicitly called when View (associated with this
+ * device) is deactivated.
+ */
+ public abstract void pause();
+
+ /**
+ * Resumes audio device engine (if previously paused) without reinitializing
+ * the device.
+ * Causes all paused cached sounds to be resumed and all streaming sounds
+ * restarted.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * This method will be implicitly called when View (associated with this
+ * device) is actived.
+ */
+ public abstract void resume();
+
+ /**
+ * Set overall gain control of all sounds playing on the audio device.
+ * Default: 1.0f = no attenuation.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * @param scaleFactor scale factor applied to calculated amplitudes for
+ * all sounds playing on this device
+ */
+ public abstract void setGain(float scaleFactor);
+
+ /**
+ * 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.
+ * Default: 1.0f = original sample rate is unchanged
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * @param sampleId device specific reference number to device driver sample
+ * @param scaleFactor non-negative factor applied to calculated
+ * amplitudes for all sounds playing on this device
+ */
+ public abstract void setRateScaleFactor(int sampleId, float scaleFactor);
+
+
+ /**
+ * 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.
+ * A coefficient value of 0 disables reverberation.
+ * Valid values of parameters range from 0.0 to 1.0.
+ * Default: 0.0f.
+ * <P>
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * @param coefficient late reflection attenuation factor
+ * @see AuralAttributes#setReverbCoefficient
+ */
+ public abstract void setReverbCoefficient(float coefficient);
+
+
+ /**
+ * 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.
+ * Valid values are non-negative floats.
+ * There may be limitations imposed by the device on how small or large this
+ * value can be made.
+ * A value of 0.0 would result in early reflections being added as soon as
+ * possible after the sound begins.
+ * Default = 20.0 milliseconds.
+ * <P>
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * @param reflectionDelay time between each order of early reflection
+ * @see AuralAttributes#setReflectionDelay
+ */
+ public abstract void setReflectionDelay(float reflectionDelay);
+
+ /**
+ * Set reverb decay time.
+ * Defines the reverberation decay curve.
+ * Default: 1000.0 milliseconds.
+ * <P>
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * @param time decay time in milliseconds
+ * @see AuralAttributes#setDecayTime
+ */
+ public abstract void setDecayTime(float time);
+
+ /**
+ * 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.
+ * Default: 1.0 decay is uniform for all frequencies.
+ * <P>
+ * There is no corresponding Core AuralAttributes method at this time.
+ * Until high frequency attenuation is supported by new Core API,
+ * this will be set by the Core with the value 1.0.
+ * It is highly recommended that this method should NOT be
+ * called by any application if the audio device is associated with
+ * a Physical Environment used by Java3D Core.
+ * @param frequencyCutoff value of frequencies in Hertz above which a
+ * low-pass filter is applied.
+ * @see AuralAttributes#setDecayFilter
+ */
+ public abstract void setDecayFilter(float frequencyCutoff);
+
+ /**
+ * 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.
+ * Default: 1.0f - maximum diffusion on device.
+ * <P>
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * @param diffusion percentage expressed within the range of 0.0 and 1.0
+ * @see AuralAttributes#setDiffusion
+ */
+ public abstract void setDiffusion(float diffusion);
+
+ /**
+ * 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.
+ * Default: 1.0f - maximum density on device.
+ * <P>
+ * A full description of this parameter and how it is used is in
+ * the documentation for the AuralAttributes class.
+ * <P>
+ * This method should NOT be called by any application if the audio device
+ * is associated with a Physical Environment used by Java3D Core.
+ * @param density reverb density expressed as a percentage,
+ * within the range of 0.0 and 1.0
+ * @see AuralAttributes#setDensity
+ */
+ public abstract void setDensity(float density);
+
+
+ /**
+ * 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.
+ * Default: 1.0 - gain is not attenuated; obstruction is not occurring.
+ * <P>
+ * There is no corresponding Core AuralAttributes method at this time.
+ * Even so, this method should NOT be called by any application if the
+ * audio device is associated with a Physical Environment used by Java3D
+ * Core.
+ * @param sampleId device specific reference number to device driver sample
+ * @param scaleFactor non-negative factor applied to direct sound gain
+ */
+ public abstract void setObstructionGain(int sampleId, float scaleFactor);
+
+ /**
+ * 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.
+ * Default: 1.0 - filtering is uniform for all frequencies.
+ * <P>
+ * There is no corresponding Core AuralAttributes method at this time.
+ * Until high frequency attenuation is supported by new Core API
+ * this will be set by the Core with the value 1.0.
+ * It is highly recommended that this method should NOT be
+ * called by any application if the audio device is associated with
+ * a Physical Environment used by Java3D Core.
+ * @param frequencyCutoff value of frequencies in Hertz above which a
+ * low-pass filter is applied.
+ */
+
+ public abstract void setObstructionFilter(int sampleId, float frequencyCutoff);
+
+ /**
+ * 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.
+ * Default: 1.0 - gain is not attenuated; occlusion is not occurring.
+ * <P>
+ * There is no corresponding Core AuralAttributes method at this time.
+ * Even so, this method should NOT be called by any application if the
+ * audio device is associated with a Physical Environment used by Java3D
+ * Core.
+ * @param sampleId device specific reference number to device driver sample
+ * @param scaleFactor non-negative factor applied to direct sound gain
+ */
+ public abstract void setOcclusionGain(int sampleId, float scaleFactor);
+
+ /**
+ * 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.
+ * Default: 1.0 - filtering is uniform for all frequencies.
+ * <P>
+ * There is no corresponding Core AuralAttributes method at this time.
+ * Until high frequency attenuation is supported by new Core API
+ * this will be set by the Core with the value 1.0.
+ * It is highly recommended that this method should NOT be
+ * called by any application if the audio device is associated with
+ * a Physical Environment used by Java3D Core.
+ * @param frequencyCutoff value of frequencies in Hertz above which a
+ * low-pass filter is applied.
+ */
+ public abstract void setOcclusionFilter(int sampleId, float frequencyCutoff);
+}
diff --git a/src/classes/share/javax/media/j3d/AudioDeviceEnumerator.java b/src/classes/share/javax/media/j3d/AudioDeviceEnumerator.java
new file mode 100644
index 0000000..79b2b57
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AudioDeviceEnumerator.java
@@ -0,0 +1,67 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * The class that enumerates all AudioDevices defined in the environment
+ *
+ * An AudioDeviceEnumerator generates the audio devices defined with the
+ * execution environment of the currently running Java 3D application.
+ */
+
+class AudioDeviceEnumerator implements Enumeration {
+
+ boolean endOfList; // NOTE: list length always equals one or zero
+ AudioDevice device;
+
+ AudioDeviceEnumerator(PhysicalEnvironment physicalEnvironment) {
+ device = physicalEnvironment.getAudioDevice();
+ if(device == null)
+ endOfList = true;
+ else
+ endOfList = false;
+ }
+
+ void reset() {
+ if(device != null)
+ endOfList = false;
+ }
+
+
+ /**
+ * Query that tells whether the enumerator has more elements
+ * @return true if the enumerator has more elements, false otherwise
+ */
+ public boolean hasMoreElements() {
+ if(endOfList == false)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Return the next element in the enumerators
+ * @return the next element in this enumerator
+ */
+ public Object nextElement() {
+ if (this.hasMoreElements()) {
+ endOfList = true;
+ return ((Object) device);
+ } else {
+ throw new NoSuchElementException(J3dI18N.getString("AudioDeviceEnumerator0"));
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/AuralAttributes.java b/src/classes/share/javax/media/j3d/AuralAttributes.java
new file mode 100644
index 0000000..dde9881
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AuralAttributes.java
@@ -0,0 +1,1261 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Point2f;
+
+/**
+ * The AuralAttributes object is a component object of a Soundscape node that
+ * defines environmental audio parameters that affect sound rendering. These
+ * attributes include gain scale factor, atmospheric rolloff, and parameters
+ * controlling reverberation, distance frequency filtering, and velocity-based
+ * Doppler effect.
+ *<P>
+ * Attribute Gain
+ * <P><UL>
+ * Scale factor applied to all sound's amplitude active within this region.
+ * This factor attenuates both direct and reflected/reverbered amplitudes.
+ * Valid values are >= 0.0
+ * </UL>
+ *<P>
+ * Attribute Gain Rolloff
+ * <P><UL>
+ * Rolloff scale factor is used to model atmospheric changes from normal
+ * speed of sound. The base value, 0.344 meters/millisecond is used
+ * to approximate the speed of sound through air at room temperature,
+ * is multipled by this scale factor whenever the speed of sound is
+ * applied during spatialization calculations.
+ * Valid values are >= 0.0. Values > 1.0 increase the speed of sound,
+ * while values < 1.0 decrease its speed. A value of zero makes sound
+ * silent (but it continues to play).
+ * </UL>
+ *<P>
+ * Auralization <P>
+ *<UL>
+ * Auralization is the environmental modeling of sound iteratively
+ * reflecting off the surfaces of the bounded region the listener is in.
+ * Auralization components include
+ * early, distinct, low-order reflections and later, dense,
+ * higher-order reflections referred to as reverberation.
+ * These reflections are attenuated relative to the direct, unreflected
+ * sound. The difference in gain between direct and reflected sound
+ * gives the listener a sense of the surface material and
+ * the relative distance of the sound.
+ * The delay between the start of the direct sound and start of
+ * reverberation (as detected by the listener),
+ * as well as the length of time reverberation is audible as it
+ * exponentially decays, give the listener a sense of the size of the
+ * listening space.
+ * <P>
+ * In Java3D's model for auralization there are several parameters
+ * that approximate sound reflection and reverberation for a particular
+ * listening space:
+ * <UL>Reflection Coefficient <UL>Gain attenuation of the initial
+ * reflections across all frequencies.</UL></UL>
+ * <UL>(Early) Reflection Delay <UL>The time it takes for the first
+ * low-order reflected sound to reach the listener.</UL></UL>
+ * <UL>Reverb Coefficient <UL>Gain attenuation of the late reflections
+ * (referred to as 'reverb') across all frequencies.</UL></UL>
+ * <UL>Reverb Delay <UL>The time it takes for reverbered sound
+ * to reach the listener.</UL></UL>
+ * <UL>Decay Time <UL>Describes the reverb decay curve by defining the
+ * length of time reverb takes to decay to effective zero.
+ * </UL></UL>
+ * <UL>Decay Filter <UL>High-frequencies of the late reverberation
+ * can be attenuated at a different rate. </UL></UL>
+ * <UL>Density <UL>Modal density (spectral coloration) of
+ * reverberation.</UL></UL>
+ * <UL>Diffusion <UL>Echo dispersement of reverberation.</UL></UL>
+ * <UL>Reverb Bounds <UL>Approximates the volume of the listening space.
+ * If specified, it defines the reverberation delay.</UL></UL>
+ * <UL>Reverb Order <UL>Optionally limits the amount of times during
+ * reverb calculation that a sound is recursively reflected off the
+ * bounding region.</UL></UL>
+ * <P>
+ * Reflection Coefficient
+ * <P><UL>
+ * The reflection coefficient is an amplitude scale factor used to
+ * approximate the average reflective or absorptive characteristics
+ * for early reflections
+ * of the composite surfaces in the region the listener is in.
+ * This scale factor is applied to the sound's amplitude regardless of the
+ * sound's position.
+ * The range of valid values is 0.0 to 1.0.
+ * A value of 1.0 denotes that reflections are unattenuated -
+ * the amplitude of reflected sound waves are not decreased.
+ * A value of 0.0 represents full absorption of reflections
+ * by the surfaces in the listening space (no reflections occur
+ * thus reverberation is disabled).
+ * </UL>
+ * <P>
+ * Reflection Delay
+ * <P><UL>
+ * The early reflection delay time (in milliseconds) can be explicitly
+ * set. Well-defined values are floats > 0.0.
+ * A value of 0.0 results in reverberation being added as soon as
+ * possible after the sound begins.
+ * </UL>
+ * <P>
+ * Reverberation Coefficient
+ * <P><UL>
+ * The reverb coefficient is an amplitude scale factor used to
+ * approximate the average reflective or absorptive characteristics
+ * of late reflections.
+ * A value of 0.0 represents full absorption of reflections
+ * by the surfaces in the listening space (no reflections occur
+ * thus reverberation is disabled).
+ * </UL>
+ * <P>
+ * Reverberation Delay
+ * <P><UL>
+ * The reverb delay time (in milliseconds) is set either explicitly,
+ * or implicitly by supplying a reverb bounds volume (from which the
+ * delay time can be calculated). Well-defined values are floats > 0.0.
+ * A value of 0.0 results in reverberation being added as soon as
+ * possible after the sound begins. Reverb delay, as calculated from non-
+ * null reverb bounds, takes precedence over explicitly set delay time.
+ * </UL>
+ * <P>
+ * Reverberation Bounds
+ * <P><UL>
+ * The reverb bounding region defines the overall size of space
+ * that reverberation is calculated for.
+ * This optional bounds does not have to be the same as the application
+ * region of the Soundscape node referencing this AuralAttributes object.
+ * If this bounding region is specified then reverb decay and delay are
+ * internally calculated from this bounds.
+ * </UL>
+ * <P>
+ * Reverberation Order
+ * <P><UL>
+ * The reverb order is a hint that can be used during reverberation
+ * to limit the number of late reflections required in calculation of
+ * reverb decay.
+ * All positive values can be interpreted during reverb rendering
+ * as the maximum order of reflections to be calculated.
+ * A non-positive value signifies that no limit is placed on the order of
+ * reflections calculated during reverberation rendering.
+ * In the case where reverb order is not limited, reverb decay is defined
+ * strictly by the Reverberation Decay Time parameter.
+ * </UL>
+ * <P>
+ * Decay Time
+ * <P><UL>
+ * The reverberation decay time explicitly defines the length of time in
+ * milliseconds it takes for the amplitude of late reflections to
+ * exponentally decrease to effective zero.
+ * In the case where reverb delay is set non-positive
+ * the renderer will perform the shortest reverberation decay
+ * possible.
+ * If ReverbOrder is set, this parameter is clamped by the reverb
+ * time calculated as time = reverb Delay * reverb Order.
+ * If ReverbOrder is 0, the decay time parameter is not clamped.
+ * </UL>
+ * <P>
+ * Decay Filter
+ * <P><UL>
+ * The reverberation decay filter defines how frequencies above a given
+ * value are attenuated by the listening space. This allows for modelling
+ * materials on surfaces that absorb high frequencies at a faster rate
+ * than low frequencies.
+ * </UL>
+ * <P>
+ * Reverberation Diffusion
+ * <P><UL>
+ * The reverberation diffusion explicitly defines echo dispersement
+ * (sometimes refered to as echo density). The value for diffusion
+ * is proportional to the number of echos per second heard in late
+ * reverberation, especially noticable at the tail of the reverberation
+ * decay. The greater the diffusion the more 'natural' the reverberation
+ * decay sounds. Reducing diffusion makes the decay sound hollow as
+ * produced in a small highly reflecive space (such as a bathroom).
+ * </UL>
+ * <P>
+ * Reverberation Density
+ * <P><UL>
+ * The reverberation density explicitly defines modal reverb density
+ * The value for this modal density is proportional to the number of
+ * resonances heard in late reverberation perceived as spectral
+ * coloration. The greater the density, the smoother, less grainy the
+ * later reverberation decay.
+ * </UL>
+ *</UL>
+ *<P>
+ * Distance Filter
+ * <P><UL>
+ * This parameter specifies a (distance, filter) attenuation pairs array.
+ * If this is not set, no distance filtering is performed (equivalent to
+ * using a distance filter of Sound.NO_FILTER for all distances). Currently,
+ * this filter is a low-pass cutoff frequency. This array of pairs defines
+ * a piece-wise linear slope for range of values. This attenuation array is
+ * similar to the PointSound node's distanceAttenuation pair array, except
+ * paired with distances in this list are frequency values. Using these
+ * pairs, distance-based low-pass frequency filtering can be applied during
+ * sound rendering. Distances, specified in the local coordinate system in
+ * meters, must be > 0. Frequencies (in Hz) must be > 0.
+ *<P>
+ * If the distance from the listener to the sound source is less than the
+ * first distance in the array, the first filter is applied to the sound
+ * source. This creates a spherical region around the listener within
+ * which a sound is uniformly attenuated by the first filter in the array.
+ * If the distance from the listener to the sound source is greater than
+ * the last distance in the array, the last filter is applied to the sound
+ * source.
+ * <P>
+ * Distance elements in these array of pairs is a monotonically-increasing
+ * set of floating point numbers measured from the location of the sound
+ * source. FrequencyCutoff elements in this list of pairs can be any
+ * positive float. While for most applications this list of values will
+ * usually be monotonically-decreasing, they do not have to be.
+ * <P>
+ * The getDistanceFilterLength method returns the length of the distance filter
+ * arrays. Arrays passed into getDistanceFilter methods should all be at
+ * least this size.</UL>
+ * </UL><P>
+ * Doppler Effect Model
+ * <P><UL>
+ * Doppler effect can be used to create a greater sense of movement of
+ * sound sources, and can help reduce front-back localization errors.
+ * The frequency of sound waves emanating from the source are raised or
+ * lowered based on the speed of the source in relation to the listener,
+ * and several AuralAttribute parameters.
+ * <P>
+ * The FrequencyScaleFactor can be used to increase or reduce the change
+ * of frequency associated with normal Doppler calculation, or to shift
+ * the pitch of the sound directly if Doppler effect is disabled.
+ * Values must be > zero for sounds to be heard. If the value is zero,
+ * sounds affected by this AuralAttribute object are paused.
+ * <P>
+ * To simulate Doppler effect, the relative velocity (change in
+ * distance in the local coordinate system between the sound source and
+ * the listener over time, in meters per second) is calculated. This
+ * calculated velocity is multipled by the given VelocityScaleFactor.
+ * Values must be >= zero. If is a scale factor value of zero is given,
+ * Doppler effect is not calculated or applied to sound.</UL></UL>
+ */
+public class AuralAttributes extends NodeComponent {
+
+ /**
+ *
+ * Constants
+ *
+ * These flags, when enabled using the setCapability method, allow an
+ * application to invoke methods that read or write its parameters.
+ *
+ */
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's attribute gain scale factor information.
+ */
+ public static final int
+ ALLOW_ATTRIBUTE_GAIN_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_ATTRIBUTE_GAIN_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's attribute gain scale factor information.
+ */
+ public static final int
+ ALLOW_ATTRIBUTE_GAIN_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_ATTRIBUTE_GAIN_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's atmospheric rolloff.
+ */
+ public static final int
+ ALLOW_ROLLOFF_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_ROLLOFF_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's atmospheric rolloff.
+ */
+ public static final int
+ ALLOW_ROLLOFF_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_ROLLOFF_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reflection coefficient.
+ */
+ public static final int
+ ALLOW_REFLECTION_COEFFICIENT_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REFLECTION_COEFFICIENT_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reflection coefficient.
+ */
+ public static final int
+ ALLOW_REFLECTION_COEFFICIENT_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REFLECTION_COEFFICIENT_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reflection delay information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_REFLECTION_DELAY_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REFLECTION_DELAY_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reflection delay information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_REFLECTION_DELAY_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REFLECTION_DELAY_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reverb coefficient.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_REVERB_COEFFICIENT_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REVERB_COEFFICIENT_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reverb coefficient.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_REVERB_COEFFICIENT_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REVERB_COEFFICIENT_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reverberation delay information.
+ */
+ public static final int
+ ALLOW_REVERB_DELAY_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REVERB_DELAY_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reverberation delay information.
+ */
+ public static final int
+ ALLOW_REVERB_DELAY_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REVERB_DELAY_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reverb order (feedback loop) information.
+ */
+ public static final int
+ ALLOW_REVERB_ORDER_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REVERB_ORDER_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reverb order (feedback loop) information.
+ */
+ public static final int
+ ALLOW_REVERB_ORDER_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_REVERB_ORDER_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reverb decay time information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DECAY_TIME_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DECAY_TIME_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reverb decay time information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DECAY_TIME_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DECAY_TIME_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reverb decay filter information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DECAY_FILTER_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DECAY_FILTER_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reverb decay filter information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DECAY_FILTER_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DECAY_FILTER_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reverb diffusion information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DIFFUSION_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DIFFUSION_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reverb diffusion information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DIFFUSION_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DIFFUSION_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's reverb density information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DENSITY_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DENSITY_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's reverb density information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DENSITY_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DENSITY_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's frequency cutoff information.
+ */
+ public static final int
+ ALLOW_DISTANCE_FILTER_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DISTANCE_FILTER_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's frequency cutoff information.
+ */
+ public static final int
+ ALLOW_DISTANCE_FILTER_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_DISTANCE_FILTER_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's frequency scale factor information.
+ */
+ public static final int
+ ALLOW_FREQUENCY_SCALE_FACTOR_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_FREQUENCY_SCALE_FACTOR_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's frequency scale factor information.
+ */
+ public static final int
+ ALLOW_FREQUENCY_SCALE_FACTOR_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_FREQUENCY_SCALE_FACTOR_WRITE;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the reading of it's velocity scale factor information.
+ */
+ public static final int
+ ALLOW_VELOCITY_SCALE_FACTOR_READ = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_VELOCITY_SCALE_FACTOR_READ;
+
+ /**
+ * For AuralAttributes component objects, specifies that this object
+ * allows the writing of it's velocity scale factor information.
+ */
+ public static final int
+ ALLOW_VELOCITY_SCALE_FACTOR_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_VELOCITY_SCALE_FACTOR_WRITE;
+
+ /** *****************
+ *
+ * Constructors
+ *
+ * ******************/
+ /**
+ * Constructs and initializes a new AuralAttributes object using default
+ * parameters. The following default values are used:
+ * <ul>
+ * attribute gain: 1.0<br>
+ * rolloff: 1.0<br>
+ * reflection coeff: 0.0<br>
+ * reflection delay: 20.0<br>
+ * reverb coeff: 1.0<br>
+ * reverb delay: 40.0<br>
+ * decay time: 1000.0<br>
+ * decay filter: 5000.0<>
+ * diffusion: 1.0<br>
+ * density: 1.0<br>
+ * reverb bounds: null<br>
+ * reverb order: 0<br>
+ * distance filtering: null (no filtering performed)<br>
+ * frequency scale factor: 1.0<br>
+ * velocity scale factor: 0.0<br>
+ * </ul>
+ */
+ public AuralAttributes() {
+ // Just use default values
+ }
+
+ /**
+ * Constructs and initializes a new AuralAttributes object using specified
+ * parameters including an array of Point2f for the distanceFilter.
+ * @param gain amplitude scale factor
+ * @param rolloff atmospheric (changing speed of sound) scale factor
+ * @param reflectionCoefficient reflective/absorptive factor applied to reflections
+ * @param reverbDelay delay time before start of reverberation
+ * @param reverbOrder limit to number of reflections added to reverb signal
+ * @param distanceFilter frequency cutoff
+ * @param frequencyScaleFactor applied to change of pitch
+ * @param velocityScaleFactor applied to velocity of sound in relation to listener
+ */
+ public AuralAttributes(float gain,
+ float rolloff,
+ float reflectionCoefficient,
+ float reverbDelay,
+ int reverbOrder,
+ Point2f[] distanceFilter,
+ float frequencyScaleFactor,
+ float velocityScaleFactor) {
+ ((AuralAttributesRetained)this.retained).setAttributeGain(gain);
+ ((AuralAttributesRetained)this.retained).setRolloff(rolloff);
+ ((AuralAttributesRetained)this.retained).setReflectionCoefficient(
+ reflectionCoefficient);
+ ((AuralAttributesRetained)this.retained).setReverbDelay(reverbDelay);
+ ((AuralAttributesRetained)this.retained).setReverbOrder(reverbOrder);
+ ((AuralAttributesRetained)this.retained).setDistanceFilter(
+ distanceFilter);
+ ((AuralAttributesRetained)this.retained).setFrequencyScaleFactor(
+ frequencyScaleFactor);
+ ((AuralAttributesRetained)this.retained).setVelocityScaleFactor(
+ velocityScaleFactor);
+ }
+ /**
+ * Constructs and initializes a new AuralAttributes object using specified
+ * parameters with separate float arrays for components of distanceFilter.
+ * @param gain amplitude scale factor
+ * @param rolloff atmospheric (changing speed of sound) scale factor
+ * @param reflectionCoefficient reflection/absorption factor applied to reflections
+ * @param reverbDelay delay time before start of reverberation
+ * @param reverbOrder limit to number of reflections added to reverb signal
+ * @param distance filter frequency cutoff distances
+ * @param frequencyCutoff distance filter frequency cutoff
+ * @param frequencyScaleFactor applied to velocity/wave-length
+ * @param velocityScaleFactor applied to velocity of sound in relation to listener
+ */
+ public AuralAttributes(float gain,
+ float rolloff,
+ float reflectionCoefficient,
+ float reverbDelay,
+ int reverbOrder,
+ float[] distance,
+ float[] frequencyCutoff,
+ float frequencyScaleFactor,
+ float velocityScaleFactor) {
+ ((AuralAttributesRetained)this.retained).setAttributeGain(gain);
+ ((AuralAttributesRetained)this.retained).setRolloff(rolloff);
+ ((AuralAttributesRetained)this.retained).setReflectionCoefficient(
+ reflectionCoefficient);
+ ((AuralAttributesRetained)this.retained).setReverbDelay(reverbDelay);
+ ((AuralAttributesRetained)this.retained).setReverbOrder(reverbOrder);
+ ((AuralAttributesRetained)this.retained).setDistanceFilter(distance,
+ frequencyCutoff);
+ ((AuralAttributesRetained)this.retained).setFrequencyScaleFactor(
+ frequencyScaleFactor);
+ ((AuralAttributesRetained)this.retained).setVelocityScaleFactor(
+ velocityScaleFactor);
+ }
+
+ /**
+ * Constructs and initializes a new AuralAttributes object using specified
+ * parameters with separate float arrays for components of distanceFilter
+ * and full reverb parameters.
+ * @param gain amplitude scale factor
+ * @param rolloff atmospheric (changing speed of sound) scale factor
+ * @param reflectionCoefficient factor applied to early reflections
+ * @param reflectionDelay delay time before start of early reflections
+ * @param reverbCoefficient factor applied to late reflections
+ * @param reverbDelay delay time before start of late reverberation
+ * @param decayTime time (in milliseconds) reverb takes to decay to -60bD
+ * @param decayFilter reverb decay filter frequency cutoff
+ * @param diffusion percentage of echo dispersement between min and max
+ * @param density percentage of modal density between min and max
+ * @param distance filter frequency cutoff distances
+ * @param frequencyCutoff distance filter frequency cutoff
+ * @param frequencyScaleFactor applied to velocity/wave-length
+ * @param velocityScaleFactor applied to velocity of sound in relation to listener
+ * @since Java 3D 1.3
+ */
+ public AuralAttributes(float gain,
+ float rolloff,
+ float reflectionCoefficient,
+ float reflectionDelay,
+ float reverbCoefficient,
+ float reverbDelay,
+ float decayTime,
+ float decayFilter,
+ float diffusion,
+ float density,
+ float[] distance,
+ float[] frequencyCutoff,
+ float frequencyScaleFactor,
+ float velocityScaleFactor) {
+ ((AuralAttributesRetained)this.retained).setAttributeGain(gain);
+ ((AuralAttributesRetained)this.retained).setRolloff(rolloff);
+ ((AuralAttributesRetained)this.retained).setReflectionCoefficient(
+ reflectionCoefficient);
+ ((AuralAttributesRetained)this.retained).setReflectionDelay(
+ reflectionDelay);
+ ((AuralAttributesRetained)this.retained).setReverbCoefficient(
+ reverbCoefficient);
+ ((AuralAttributesRetained)this.retained).setReverbDelay(
+ reverbDelay);
+ ((AuralAttributesRetained)this.retained).setDecayTime(decayTime);
+ ((AuralAttributesRetained)this.retained).setDecayFilter(decayFilter);
+ ((AuralAttributesRetained)this.retained).setDiffusion(diffusion);
+ ((AuralAttributesRetained)this.retained).setDensity(density);
+ ((AuralAttributesRetained)this.retained).setDistanceFilter(distance,
+ frequencyCutoff);
+ ((AuralAttributesRetained)this.retained).setFrequencyScaleFactor(
+ frequencyScaleFactor);
+ ((AuralAttributesRetained)this.retained).setVelocityScaleFactor(
+ velocityScaleFactor);
+ }
+
+ /**
+ * Creates the retained mode AuralAttributesRetained object that this
+ * component object will point to.
+ */
+ void createRetained() {
+ this.retained = new AuralAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+ /** ****************************************
+ *
+ * Attribute Gain
+ *
+ * ****************************************/
+ /**
+ * Set Attribute Gain (amplitude) scale factor.
+ * @param gain scale factor applied to amplitude of direct and reflected sound
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAttributeGain(float gain) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ATTRIBUTE_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes0"));
+ ((AuralAttributesRetained)this.retained).setAttributeGain(gain);
+ }
+
+ /**
+ * Retrieve Attribute Gain (amplitude).
+ * @return gain amplitude scale factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getAttributeGain() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ATTRIBUTE_GAIN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes1"));
+ return ((AuralAttributesRetained)this.retained).getAttributeGain();
+ }
+
+ /**
+ * Set Attribute Gain Rolloff.
+ * @param rolloff atmospheric gain scale factor (changing speed of sound)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setRolloff(float rolloff) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ROLLOFF_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes2"));
+ ((AuralAttributesRetained)this.retained).setRolloff(rolloff);
+ }
+
+ /**
+ * Retrieve Attribute Gain Rolloff.
+ * @return rolloff atmospheric gain scale factor (changing speed of sound)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getRolloff() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ROLLOFF_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes3"));
+ return ((AuralAttributesRetained)this.retained).getRolloff();
+ }
+
+ /**
+ * Set Reflective Coefficient.
+ * Scales the amplitude of the early reflections of reverberated sounds
+ * @param coefficient reflection/absorption factor applied to reflections
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setReflectionCoefficient(float coefficient) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REFLECTION_COEFFICIENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes4"));
+ ((AuralAttributesRetained)this.retained).setReflectionCoefficient(coefficient);
+ }
+
+ /**
+ * Retrieve Reflective Coefficient.
+ * @return reflection coeff reflection/absorption factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getReflectionCoefficient() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REFLECTION_COEFFICIENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes21"));
+ return ((AuralAttributesRetained)this.retained).getReflectionCoefficient();
+ }
+
+ /*********************
+ *
+ * Early Reflection Delay
+ *
+ ********************/
+ /**
+ * Set early Refection Delay Time.
+ * In this form, the parameter specifies the time between the start of the
+ * direct, unreflected sound and the start of first order early reflections.
+ * In this method, this time is explicitly given in milliseconds.
+ * @param reflectionDelay delay time before start of reverberation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setReflectionDelay(float reflectionDelay) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REFLECTION_DELAY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes22"));
+ ((AuralAttributesRetained)this.retained).setReflectionDelay(reflectionDelay);
+ }
+
+ /**
+ * Retrieve Reflection Delay Time.
+ * @return reflection delay time
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public float getReflectionDelay() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REFLECTION_DELAY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes23"));
+ return ((AuralAttributesRetained)this.retained).getReflectionDelay();
+ }
+
+ /** ******************
+ *
+ * Reverb Coefficient
+ *
+ ********************/
+ /**
+ * Set Reverb Coefficient.
+ * Scale the amplitude of the late reflections including the decaying tail
+ * of reverberated sound.
+ * @param coefficient reflective/absorptive factor applied to late reflections
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setReverbCoefficient(float coefficient) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_COEFFICIENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes24"));
+ ((AuralAttributesRetained)this.retained).setReverbCoefficient(coefficient);
+ }
+
+ /**
+ * Retrieve Reverb Coefficient.
+ * @return late reflection coeff. reflection/absorption factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public float getReverbCoefficient() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_COEFFICIENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes25"));
+ return ((AuralAttributesRetained)this.retained).getReverbCoefficient();
+ }
+
+ /*********************
+ *
+ * Reverberation Delay
+ *
+ ********************/
+ /**
+ * Set Reverberation Delay Time.
+ * In this form, the parameter specifies the time between the start of the
+ * direct, unreflected sound and the start of reverberation. In this
+ * method, this time is explicitly given in milliseconds.
+ * @param reverbDelay delay time before start of reverberation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setReverbDelay(float reverbDelay) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_DELAY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes5"));
+ ((AuralAttributesRetained)this.retained).setReverbDelay(reverbDelay);
+ }
+
+ /**
+ * Retrieve Reverberation Delay Time.
+ * @return reverb delay time
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getReverbDelay() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_DELAY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes7"));
+ return ((AuralAttributesRetained)this.retained).getReverbDelay();
+ }
+
+ /** ******************
+ *
+ * Decay Time
+ *
+ ********************/
+ /**
+ * Set Decay Time
+ * Length of time from the start of late reflections reverberation volume
+ * takes to decay to effective zero (-60 dB of initial signal amplitude).
+ * @param decayTime of late reflections (reverb) in milliseconds
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setDecayTime(float decayTime) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DECAY_TIME_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes28"));
+ ((AuralAttributesRetained)this.retained).setDecayTime(decayTime);
+ }
+
+ /**
+ * Retrieve Decay Time.
+ * @return reverb decay time
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public float getDecayTime() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DECAY_TIME_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes29"));
+ return ((AuralAttributesRetained)this.retained).getDecayTime();
+ }
+
+ /** ******************
+ *
+ * Decay Filter
+ *
+ ********************/
+ /**
+ * Set Decay Filter
+ * In this form, reverberation decay filtering is defined as a low-pass
+ * filter, starting at the given reference frequency. This allows for
+ * higher frequencies to be attenuated at a different (typically faster)
+ * rate than lower frequencies.
+ * @param frequencyCutoff of reverberation decay low-pass filter
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setDecayFilter(float frequencyCutoff) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DECAY_FILTER_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes30"));
+ ((AuralAttributesRetained)this.retained).setDecayFilter(frequencyCutoff);
+ }
+
+ /**
+ * Retrieve Decay Filter.
+ * @return reverb decay filter cutoff frequency
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public float getDecayFilter() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DECAY_FILTER_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes31"));
+ return ((AuralAttributesRetained)this.retained).getDecayFilter();
+ }
+
+ /** ******************
+ *
+ * Diffusion
+ *
+ ********************/
+ /**
+ * Set Diffusion.
+ * Sets the echo dispersement of reverberation to an amount between
+ * the minimum (0.0) to the maximum (1.0) available. Changing this
+ * increases/decreases the 'smoothness' of reverb decay.
+ * @param ratio reverberation echo dispersement factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setDiffusion(float ratio) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DIFFUSION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes32"));
+ ((AuralAttributesRetained)this.retained).setDiffusion(ratio);
+ }
+
+ /**
+ * Retrieve Diffusion.
+ * @return reverb diffusion ratio
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public float getDiffusion() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIFFUSION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes33"));
+ return ((AuralAttributesRetained)this.retained).getDiffusion();
+ }
+
+ /** ******************
+ *
+ * Density
+ *
+ ********************/
+ /**
+ * Set Density.
+ * Sets the density of reverberation to an amount between
+ * the minimum (0.0) to the maximum (1.0) available. Changing this
+ * effects the spectral coloration (timbre) of late reflections.
+ * @param ratio reverberation modal density factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setDensity(float ratio) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DENSITY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes34"));
+ ((AuralAttributesRetained)this.retained).setDensity(ratio);
+ }
+
+ /**
+ * Retrieve Density.
+ * @return reverb density
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public float getDensity() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DENSITY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes35"));
+ return ((AuralAttributesRetained)this.retained).getDensity();
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setReverbBounds(Bounds)</code>
+ */
+ public void setReverbDelay(Bounds reverbVolume) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_DELAY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes5"));
+ ((AuralAttributesRetained)this.retained).setReverbBounds(reverbVolume);
+ }
+
+ /**
+ * Set Reverberation Bounds volume.
+ * In this form, the reverberation bounds volume parameter is used to
+ * calculate the reverberation Delay and Decay times. Specification
+ * of a non-null bounding volume causes the explicit values given for
+ * Reverb Delay and Decay to be overridden by the implicit values
+ * calculated from these bounds.
+ * ALLOW_REVERB_DELAY_WRITE flag used setting capability of this method.
+ * @param reverbVolume the bounding region
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.2
+ */
+ public void setReverbBounds(Bounds reverbVolume) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_DELAY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes26"));
+ ((AuralAttributesRetained)this.retained).setReverbBounds(reverbVolume);
+ }
+
+ /**
+ * Retrieve Reverberation Delay Bounds volume.
+ * @return reverb bounds volume that defines the Reverberation space and
+ * indirectly the delay/decay
+ * ALLOW_REVERB_DELAY_READ flag used setting capability of this method.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.2
+ */
+ public Bounds getReverbBounds() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_DELAY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes27"));
+ return ((AuralAttributesRetained)this.retained).getReverbBounds();
+ }
+
+ /** *******************
+ *
+ * Reverberation Order
+ *
+ ********************/
+ /**
+ * Set Reverberation Order
+ * This parameter limits the number of times reflections are added
+ * to the reverberation being rendered.
+ * A non-positive value specifies an unbounded number of reflections.
+ * @param reverbOrder limit to the number of times reflections added to reverb signal
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setReverbOrder(int reverbOrder) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REVERB_ORDER_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes8"));
+ ((AuralAttributesRetained)this.retained).setReverbOrder(reverbOrder);
+ }
+
+ /**
+ * Retrieve Reverberation Order
+ * @return reverb order
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getReverbOrder() {
+ if (!this.getCapability(ALLOW_REVERB_ORDER_READ))
+ if (isLiveOrCompiled())
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes9"));
+ return ((AuralAttributesRetained)this.retained).getReverbOrder();
+ }
+
+ /**
+ * Set Distance Filter using a single array containing distances and
+ * frequency cutoff as pairs of values as a single array of Point2f.
+ * @param attenuation array of pairs of distance and frequency cutoff
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDistanceFilter(Point2f[] attenuation) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DISTANCE_FILTER_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes10"));
+ ((AuralAttributesRetained)this.retained).setDistanceFilter(attenuation);
+ }
+
+ /**
+ * Set Distance Filter using separate arrays for distances and frequency
+ * cutoff. The distance and frequencyCutoff arrays should be of the same
+ * length. If the frequencyCutoff array length is greater than the distance
+ * array length, the frequencyCutoff array elements beyond the length of
+ * the distance array are ignored. If the frequencyCutoff array is shorter
+ * than the distance array, the last frequencyCutoff array value is repeated
+ * to fill an array of length equal to distance array.
+ * @param distance array of float distance with corresponding cutoff values
+ * @param frequencyCutoff array of frequency cutoff values in Hertz
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDistanceFilter(float[] distance,
+ float[] frequencyCutoff) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DISTANCE_FILTER_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes10"));
+ ((AuralAttributesRetained)this.retained).setDistanceFilter(
+ distance, frequencyCutoff );
+ }
+
+ /**
+ * Retrieve Distance Filter array length.
+ * @return attenuation array length
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getDistanceFilterLength() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DISTANCE_FILTER_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes12"));
+ return (((AuralAttributesRetained)this.retained).getDistanceFilterLength());
+ }
+ /**
+ * Retrieve Distance Filter as a single array containing distances
+ * and frequency cutoff. The distance filter is copied into
+ * the specified array.
+ * The array must be large enough to hold all of the points.
+ * The individual array elements must be allocated by the caller.
+ * @return attenuation array of pairs of distance and frequency cutoff values
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDistanceFilter(Point2f[] attenuation) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DISTANCE_FILTER_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes12"));
+ ((AuralAttributesRetained)this.retained).getDistanceFilter(attenuation);
+ }
+
+ /**
+ * Retrieve Distance Filter in separate distance and frequency cutoff arrays.
+ * The arrays must be large enough to hold all of the distance
+ * and frequency cutoff values.
+ * @param distance array
+ * @param frequencyCutoff cutoff array
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDistanceFilter(float[] distance,
+ float[] frequencyCutoff) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DISTANCE_FILTER_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes12"));
+ ((AuralAttributesRetained)this.retained).getDistanceFilter(
+ distance, frequencyCutoff);
+ }
+
+ /**
+ * This parameter specifies a scale factor applied to the frequency
+ * of sound during rendering playback. If the Doppler effect is
+ * disabled, this scale factor can be used to increase or
+ * decrease the original pitch of the sound. During rendering,
+ * this scale factor expands or contracts the usual frequency shift
+ * applied to the sound source due to Doppler calculations.
+ * Valid values are >= 0.0.
+ * A value of zero causes playing sounds to pause.
+ * @param frequencyScaleFactor factor applied to change of frequency
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setFrequencyScaleFactor(float frequencyScaleFactor) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_FREQUENCY_SCALE_FACTOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes15"));
+ ((AuralAttributesRetained)this.retained).setFrequencyScaleFactor(
+ frequencyScaleFactor);
+ }
+
+ /**
+ * Retrieve Frequency Scale Factor.
+ * @return scaleFactor factor applied to change of frequency
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getFrequencyScaleFactor() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_FREQUENCY_SCALE_FACTOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes17"));
+ return ((AuralAttributesRetained)this.retained).getFrequencyScaleFactor();
+ }
+
+ /** ******************************
+ *
+ * Velocity Scale Factor
+ *
+ *********************************/
+ /**
+ * Set Velocity scale factor applied during Doppler Effect calculation.
+ * This parameter specifies a scale factor applied to the velocity of
+ * the sound relative to the listener's position and movement in relation
+ * to the sound's position and movement. This scale factor is multipled
+ * by the calculated velocity portion of the Doppler effect equation used
+ * during sound rendering.
+ * A value of zero disables Doppler calculations.
+ * @param velocityScaleFactor applied to velocity of sound in relation
+ * to listener
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setVelocityScaleFactor(float velocityScaleFactor) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_VELOCITY_SCALE_FACTOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes19"));
+ ((AuralAttributesRetained)this.retained).setVelocityScaleFactor(
+ velocityScaleFactor);
+ }
+
+ /**
+ * Retrieve Velocity Scale Factor used to calculate Doppler Effect.
+ * @return scale factor applied to Doppler velocity of sound
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getVelocityScaleFactor() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_VELOCITY_SCALE_FACTOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("AuralAttributes20"));
+ return ((AuralAttributesRetained)this.retained).getVelocityScaleFactor();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>cloneNodeComponent(boolean forceDuplicate)</code>
+ */
+ public NodeComponent cloneNodeComponent() {
+ AuralAttributes a = new AuralAttributes();
+ a.duplicateNodeComponent(this, this.forceDuplicate);
+ return a;
+ }
+
+
+ /**
+ * Copies all AuralAttributes information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent,
+ forceDuplicate);
+
+ AuralAttributesRetained aural = (AuralAttributesRetained) originalNodeComponent.retained;
+ AuralAttributesRetained rt = (AuralAttributesRetained) retained;
+
+ rt.setAttributeGain(aural.getAttributeGain());
+ rt.setRolloff(aural.getRolloff());
+ rt.setReflectionCoefficient(aural.getReflectionCoefficient());
+ rt.setReverbDelay(aural.getReverbDelay());
+ rt.setReverbOrder(aural.getReverbOrder());
+ rt.setReverbBounds(aural.getReverbBounds());
+ rt.setFrequencyScaleFactor(aural.getFrequencyScaleFactor());
+ rt.setVelocityScaleFactor(aural.getVelocityScaleFactor());
+ int len = aural.getDistanceFilterLength();
+ float distance[] = new float[len];
+ float frequencyCutoff[] = new float[len];
+ aural.getDistanceFilter(distance, frequencyCutoff);
+ rt.setDistanceFilter(distance, frequencyCutoff);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/AuralAttributesRetained.java b/src/classes/share/javax/media/j3d/AuralAttributesRetained.java
new file mode 100644
index 0000000..7f118b5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/AuralAttributesRetained.java
@@ -0,0 +1,654 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+import javax.vecmath.Point2f;
+
+/**
+ * The AuralAttributesRetained object defines all rendering state that can
+ * be set as a component object of a retained Soundscape node.
+ */
+class AuralAttributesRetained extends NodeComponentRetained {
+
+ /**
+ * Gain Scale Factor applied to source with this attribute
+ */
+ float attributeGain = 1.0f; // Valid values are >= 0.0.
+
+ /**
+ * Atmospheric Rolloff - speed of sound - coeff
+ * Normal gain attenuation based on distance of sound from
+ * listener is scaled by a rolloff factor, which can increase
+ * or decrease the usual inverse-distance-square value.
+ */
+ float rolloff = 1.0f; // Valid values are >= 0.0
+ static final float SPEED_OF_SOUND = 0.344f; // in meters/milliseconds
+
+ /*
+ * Reverberation
+ *
+ * Within Java 3D's model for auralization, the components to
+ * reverberation for a particular space are:
+ * Reflection and Reverb Coefficients -
+ * attenuation of sound (uniform for all frequencies) due to
+ * absorption of reflected sound off materials within the
+ * listening space.
+ * Reflection and Reverb Delay -
+ * approximating time from the start of the direct sound that
+ * initial early and late reflection waves take to reach listener.
+ * Reverb Decay -
+ * approximating time from the start of the direct sound that
+ * reverberation is audible.
+ */
+
+ /**
+ * Coefficients for reverberation
+ * The (early) Reflection and Reverberation coefficient scale factors
+ * are used to approximate the reflective/absorptive characteristics
+ * of the surfaces in this bounded Auralizaton environment.
+ * Theses scale factors is applied to sound's amplitude regardless
+ * of sound's position.
+ * Value of 1.0 represents complete (unattenuated) sound reflection.
+ * Value of 0.0 represents full absorption; reverberation is disabled.
+ */
+ float reflectionCoefficient = 0.0f; // Range of values 0.0 to 1.0
+ float reverbCoefficient = 1.0f; // Range of values 0.0 to 1.0
+
+ /**
+ * Time Delays in milliseconds
+ * Set with either explicitly with time, or impliciticly by supplying
+ * bounds volume and having the delay time calculated.
+ * Bounds of reverberation space does not have to be the same as
+ * Attribute bounds.
+ */
+ float reflectionDelay = 20.0f; // in milliseconds
+ float reverbDelay = 40.0f; // in milliseconds
+ Bounds reverbBounds = null;
+
+ /**
+ * Decay parameters
+ * Length and timbre of reverb decay tail
+ */
+ float decayTime = 1000.0f; // in milliseconds
+ float decayFilter = 5000.0f; // low-pass cutoff frequency
+
+ /**
+ * Reverb Diffusion and Density ratios (0=min, 1=max)
+ */
+ float diffusion = 1.0f;
+ float density = 1.0f;
+
+ /**
+ * Reverberation order
+ * This limits the number of Reverberation iterations executed while
+ * sound is being reverberated. As long as reflection coefficient is
+ * small enough, the reverberated sound decreases (as it would naturally)
+ * each successive iteration.
+ * Value of > zero defines the greatest reflection order to be used by
+ * the reverberator.
+ * All positive values are used as the number of loop iteration.
+ * Value of <= zero signifies that reverberation is to loop until reverb
+ * gain reaches zero (-60dB or 1/1000 of sound amplitude).
+ */
+ int reverbOrder = 0;
+
+ /**
+ * 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.
+ */
+ static final int NO_FILTERING = -1;
+ static final int LOW_PASS = 1;
+
+ int filterType = NO_FILTERING;
+ float[] distance = null;
+ float[] frequencyCutoff = null;
+
+ /**
+ * Doppler Effect parameters
+ * Between two snapshots of the head and sound source positions some
+ * delta time apart, the distance between head and source is compared.
+ * If there has been no change in the distance between head and sound
+ * source over this delta time:
+ * f' = f
+ *
+ * If there has been a change in the distance between head and sound:
+ * f' = f * Af * v
+ *
+ * When head and sound are moving towards each other then
+ * | (S * Ar) + (deltaV(h,t) * Av) |
+ * v = | -------------------------------- |
+ * | (S * Ar) - (deltaV(s,t) * Av) |
+ *
+ * When head and sound are moving away from each other then
+ * | (S * Ar) - (deltaV(h,t) * Av) |
+ * v = | -------------------------------- |
+ * | (S * Ar) + (deltaV(s,t) * Av) |
+ *
+ *
+ * Af = AuralAttribute frequency scalefactor
+ * Ar = AuralAttribute rolloff scalefactor
+ * Av = AuralAttribute velocity scalefactor
+ * deltaV = delta velocity
+ * f = frequency of sound
+ * h = Listeners head position
+ * v = Ratio of delta velocities
+ * Vh = Vector from center ear to sound source
+ * S = Speed of sound
+ * s = Sound source position
+ * t = time
+ *
+ * If adjusted velocity of head or adjusted velocity of sound is
+ * greater than adjusted speed of sound, f' is undefined.
+ */
+ /**
+ * Frequency Scale Factor
+ * used to increase or reduce the change of frequency associated
+ * with normal rate of playback.
+ * Value of zero causes sounds to be paused.
+ */
+ float frequencyScaleFactor = 1.0f;
+ /**
+ * Velocity Scale Factor
+ * Float value applied to the Change of distance between Sound Source
+ * and Listener over some delta time. Non-zero if listener moving
+ * even if sound is not. Value of zero implies no Doppler applied.
+ */
+ float velocityScaleFactor = 0.0f;
+
+ /**
+ * This boolean is set when something changes in the attributes
+ */
+ boolean aaDirty = true;
+
+ /**
+ * The mirror copy of this AuralAttributes.
+ */
+ AuralAttributesRetained mirrorAa = null;
+
+ /**
+ ** Debug print mechanism for Sound nodes
+ **/
+ static final // 'static final' so compiler doesn't include debugPrint calls
+ boolean debugFlag = false;
+
+ static final // 'static final' so internal error message are not compiled
+ boolean internalErrors = false;
+
+ void debugPrint(String message) {
+ if (debugFlag) // leave test in in case debugFlag made non-static final
+ System.out.println(message);
+ }
+
+
+ // ****************************************
+ //
+ // Set and Get individual attribute values
+ //
+ // ****************************************
+
+ /**
+ * Set Attribute Gain (amplitude)
+ * @param gain scale factor applied to amplitude
+ */
+ void setAttributeGain(float gain) {
+ this.attributeGain = gain;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Attribute Gain (amplitude)
+ * @return gain amplitude scale factor
+ */
+ float getAttributeGain() {
+ return this.attributeGain;
+ }
+
+ /**
+ * Set Attribute Gain Rolloff
+ * @param rolloff atmospheric gain scale factor (changing speed of sound)
+ */
+ void setRolloff(float rolloff) {
+ this.rolloff = rolloff;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Attribute Gain Rolloff
+ * @return rolloff atmospheric gain scale factor (changing speed of sound)
+ */
+ float getRolloff() {
+ return this.rolloff;
+ }
+
+ /**
+ * Set Reflective Coefficient
+ * @param reflectionCoefficient reflection/absorption factor applied to
+ * early reflections.
+ */
+ void setReflectionCoefficient(float reflectionCoefficient) {
+ this.reflectionCoefficient = reflectionCoefficient;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Reflective Coefficient
+ * @return reflection coeff reflection/absorption factor applied to
+ * early reflections.
+ */
+ float getReflectionCoefficient() {
+ return this.reflectionCoefficient;
+ }
+
+ /**
+ * Set Reflection Delay Time
+ * @param reflectionDelay time before the start of early (first order)
+ * reflections.
+ */
+ void setReflectionDelay(float reflectionDelay) {
+ this.reflectionDelay = reflectionDelay;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Reflection Delay Time
+ * @return reflection delay time
+ */
+ float getReflectionDelay() {
+ return this.reflectionDelay;
+ }
+
+ /**
+ * Set Reverb Coefficient
+ * @param reverbCoefficient reflection/absorption factor applied to
+ * late reflections.
+ */
+ void setReverbCoefficient(float reverbCoefficient) {
+ this.reverbCoefficient = reverbCoefficient;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Reverb Coefficient
+ * @return reverb coeff reflection/absorption factor applied to late
+ * reflections.
+ */
+ float getReverbCoefficient() {
+ return this.reverbCoefficient;
+ }
+
+ /**
+ * Set Revereration Delay Time
+ * @param reverbDelay time between each order of reflection
+ */
+ void setReverbDelay(float reverbDelay) {
+ this.reverbDelay = reverbDelay;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Revereration Delay Time
+ * @return reverb delay time between each order of reflection
+ */
+ float getReverbDelay() {
+ return this.reverbDelay;
+ }
+ /**
+ * Set Decay Time
+ * @param decayTime length of time reverb takes to decay
+ */
+ void setDecayTime(float decayTime) {
+ this.decayTime = decayTime;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Revereration Decay Time
+ * @return reverb delay time
+ */
+ float getDecayTime() {
+ return this.decayTime;
+ }
+
+ /**
+ * Set Decay Filter
+ * @param decayFilter frequency referenced used in low-pass filtering
+ */
+ void setDecayFilter(float decayFilter) {
+ this.decayFilter = decayFilter;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+
+ /**
+ * Retrieve Revereration Decay Filter
+ * @return reverb delay Filter
+ */
+ float getDecayFilter() {
+ return this.decayFilter;
+ }
+
+ /**
+ * Set Reverb Diffusion
+ * @param diffusion ratio between min and max device diffusion settings
+ */
+ void setDiffusion(float diffusion) {
+ this.diffusion = diffusion;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+
+ /**
+ * Retrieve Revereration Decay Diffusion
+ * @return reverb diffusion
+ */
+ float getDiffusion() {
+ return this.diffusion;
+ }
+
+ /**
+ * Set Reverb Density
+ * @param density ratio between min and max device density settings
+ */
+ void setDensity(float density) {
+ this.density = density;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+
+ /**
+ * Retrieve Revereration Density
+ * @return reverb density
+ */
+ float getDensity() {
+ return this.density;
+ }
+
+
+ /**
+ * Set Revereration Bounds
+ * @param reverbVolume bounds used to approximate reverb time.
+ */
+ synchronized void setReverbBounds(Bounds reverbVolume) {
+ this.reverbBounds = reverbVolume;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Revereration Delay Bounds volume
+ * @return reverb bounds volume that defines the Reverberation space and
+ * indirectly the delay
+ */
+ Bounds getReverbBounds() {
+ return this.reverbBounds;
+ }
+
+ /**
+ * Set Reverberation Order of Reflections
+ * @param reverbOrder number of times reflections added to reverb signal
+ */
+ void setReverbOrder(int reverbOrder) {
+ this.reverbOrder = reverbOrder;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Reverberation Order of Reflections
+ * @return reverb order number of times reflections added to reverb signal
+ */
+ int getReverbOrder() {
+ return this.reverbOrder;
+ }
+
+ /**
+ * Set Distance Filter (based on distances and frequency cutoff)
+ * @param attenuation array of pairs defining distance frequency cutoff
+ */
+ synchronized void setDistanceFilter(Point2f[] attenuation) {
+ if (attenuation == null) {
+ this.filterType = NO_FILTERING;
+ return;
+ }
+ int attenuationLength = attenuation.length;
+ if (attenuationLength == 0) {
+ this.filterType = NO_FILTERING;
+ return;
+ }
+ this.filterType = LOW_PASS;
+ // Reallocate every time unless size of new array equal old array
+ if ( distance == null ||
+ (distance != null && (distance.length != attenuationLength) ) ) {
+ this.distance = new float[attenuationLength];
+ this.frequencyCutoff = new float[attenuationLength];
+ }
+ for (int i = 0; i< attenuationLength; i++) {
+ this.distance[i] = attenuation[i].x;
+ this.frequencyCutoff[i] = attenuation[i].y;
+ }
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Set Distance Filter (based on distances and frequency cutoff) using
+ * separate arrays
+ * @param distance array containing distance values
+ * @param filter array containing low-pass frequency cutoff values
+ */
+ synchronized void setDistanceFilter(float[] distance, float[] filter) {
+ if (distance == null || filter == null) {
+ this.filterType = NO_FILTERING;
+ return;
+ }
+ int distanceLength = distance.length;
+ int filterLength = filter.length;
+ if (distanceLength == 0 || filterLength == 0) {
+ this.filterType = NO_FILTERING;
+ return;
+ }
+ // Reallocate every time unless size of new array equal old array
+ if ( this.distance == null ||
+ ( this.distance != null &&
+ (this.distance.length != filterLength) ) ) {
+ this.distance = new float[distanceLength];
+ this.frequencyCutoff = new float[distanceLength];
+ }
+ this.filterType = LOW_PASS;
+ // Copy the distance array into nodes field
+ System.arraycopy(distance, 0, this.distance, 0, distanceLength);
+ // Copy the filter array an array of same length as the distance array
+ if (distanceLength <= filterLength) {
+ System.arraycopy(filter, 0, this.frequencyCutoff,0, distanceLength);
+ }
+ else {
+ System.arraycopy(filter, 0, this.frequencyCutoff, 0, filterLength);
+ // Extend filter array to length of distance array by
+ // replicate last filter values.
+ for (int i=filterLength; i< distanceLength; i++) {
+ this.frequencyCutoff[i] = filter[filterLength - 1];
+ }
+ }
+ if (debugFlag) {
+ debugPrint("AAR setDistanceFilter(D,F)");
+ for (int jj=0;jj<distanceLength;jj++) {
+ debugPrint(" from distance, freq = " + distance[jj] + ", " +
+ filter[jj]);
+ debugPrint(" into distance, freq = " + this.distance[jj] + ", " +
+ this.frequencyCutoff[jj]);
+ }
+ }
+ this.aaDirty = true;
+ notifyUsers();
+ }
+
+ /**
+ * Retrieve Distance Filter array length
+ * @return attenuation array length
+ */
+ int getDistanceFilterLength() {
+ if (distance == null)
+ return 0;
+ else
+ return this.distance.length;
+ }
+
+
+ /**
+ * Retrieve Distance Filter (distances and frequency cutoff)
+ * @return attenaution pairs of distance and frequency cutoff filter
+ */
+ void getDistanceFilter(Point2f[] attenuation) {
+ // Write into existing param array already allocated
+ if (attenuation == null)
+ return;
+ if (this.distance == null || this.frequencyCutoff == null)
+ return;
+ // The two filter attenuation arrays length should be the same
+ // We can assume that distance and filter lengths are the same
+ // and are non-zero.
+ int distanceLength = this.distance.length;
+ // check that attenuation array large enough to contain
+ // auralAttribute arrays
+ if (distanceLength > attenuation.length)
+ distanceLength = attenuation.length;
+ for (int i=0; i< distanceLength; i++) {
+ attenuation[i].x = this.distance[i];
+ if (filterType == NO_FILTERING)
+ attenuation[i].y = Sound.NO_FILTER;
+ else if (filterType == LOW_PASS)
+ attenuation[i].y = this.frequencyCutoff[i];
+ if (debugFlag)
+ debugPrint("AAR: getDistF: " + attenuation[i].x + ", " +
+ attenuation[i].y);
+ }
+ }
+ /**
+ * Retrieve Distance Filter as arrays distances and frequency cutoff array
+ * @param distance array of float values
+ * @param frequencyCutoff array of float cutoff filter values in Hertz
+ */
+ void getDistanceFilter(float[] distance, float[] filter) {
+ // Write into existing param arrays already allocated
+ if (distance == null || filter == null)
+ return;
+ if (this.distance == null || this.frequencyCutoff == null)
+ return;
+ int distanceLength = this.distance.length;
+ // check that distance parameter large enough to contain auralAttribute
+ // distance array
+ // We can assume that distance and filter lengths are the same
+ // and are non-zero.
+ if (distance.length < distanceLength)
+ // parameter array not large enough to hold all this.distance data
+ distanceLength = distance.length;
+ System.arraycopy(this.distance, 0, distance, 0, distanceLength);
+ if (debugFlag)
+ debugPrint("AAR getDistanceFilter(D,F) " + this.distance[0]);
+ int filterLength = this.frequencyCutoff.length;
+ if (filter.length < filterLength)
+ // parameter array not large enough to hold all this.filter data
+ filterLength = filter.length;
+ if (filterType == NO_FILTERING) {
+ for (int i=0; i< filterLength; i++)
+ filter[i] = Sound.NO_FILTER;
+ }
+ if (filterType == LOW_PASS) {
+ System.arraycopy(this.frequencyCutoff, 0, filter, 0, filterLength);
+ }
+ if (debugFlag)
+ debugPrint(", " + this.frequencyCutoff[0]);
+ }
+
+ /**
+ * Set Frequency Scale Factor
+ * @param frequencyScaleFactor factor applied to sound's base frequency
+ */
+ void setFrequencyScaleFactor(float frequencyScaleFactor) {
+ this.frequencyScaleFactor = frequencyScaleFactor;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Frequency Scale Factor
+ * @return frequency scale factor applied to sound's base frequency
+ */
+ float getFrequencyScaleFactor() {
+ return this.frequencyScaleFactor;
+ }
+
+ /**
+ * Set Velocity ScaleFactor used in calculating Doppler Effect
+ * @param velocityScaleFactor applied to velocity of sound in relation to listener
+ */
+ void setVelocityScaleFactor(float velocityScaleFactor) {
+ this.velocityScaleFactor = velocityScaleFactor;
+ this.aaDirty = true;
+ notifyUsers();
+ }
+ /**
+ * Retrieve Velocity ScaleFactor used in calculating Doppler Effect
+ * @return velocity scale factor
+ */
+ float getVelocityScaleFactor() {
+ return this.velocityScaleFactor;
+ }
+
+ synchronized void reset(AuralAttributesRetained aa) {
+ int i;
+
+ this.attributeGain = aa.attributeGain;
+ this.rolloff = aa.rolloff;
+ this.reflectionCoefficient = aa.reflectionCoefficient;
+ this.reverbCoefficient = aa.reverbCoefficient;
+ this.reflectionDelay = aa.reflectionDelay;
+ this.reverbDelay = aa.reverbDelay;
+ this.reverbBounds = aa.reverbBounds;
+ this.reverbOrder = aa.reverbOrder;
+ this.decayTime = aa.decayTime;
+ this.decayFilter = aa.decayFilter;
+ this.diffusion = aa.diffusion;
+ this.density = aa.density;
+ this.frequencyScaleFactor = aa.frequencyScaleFactor;
+ this.velocityScaleFactor = aa.velocityScaleFactor;
+
+ if (aa.distance != null) {
+ this.distance = new float[aa.distance.length];
+ if (debugFlag)
+ debugPrint("reset aa; aa.distance.length = " + this.distance.length);
+ System.arraycopy(aa.distance, 0, this.distance, 0, this.distance.length);
+ }
+ else
+ if (debugFlag)
+ debugPrint("reset aa; aa.distance = null");
+ if (aa.frequencyCutoff != null) {
+ this.frequencyCutoff = new float[aa.frequencyCutoff.length];
+ if (debugFlag)
+ debugPrint("reset aa; aa.frequencyCutoff.length = " + this.frequencyCutoff.length);
+ System.arraycopy(aa.frequencyCutoff, 0, this.frequencyCutoff, 0,
+ this.frequencyCutoff.length);
+ }
+ else
+ if (debugFlag)
+ debugPrint("reset aa; aa.frequencyCutoff = null");
+ // TODO: (Enhancement) Why are these dirtyFlag cleared rather than aa->this
+ this.aaDirty = false;
+ aa.aaDirty = false;
+ }
+
+ void update(AuralAttributesRetained aa) {
+ this.reset(aa);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/BHInsertStructure.java b/src/classes/share/javax/media/j3d/BHInsertStructure.java
new file mode 100644
index 0000000..81eb7be
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BHInsertStructure.java
@@ -0,0 +1,139 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+class BHInsertStructure {
+
+ static boolean debug = false;
+ static boolean debug2 = false;
+
+ Random randomNumber;
+ ArrayList[] bhListArr = null;
+ ArrayList[] oldBhListArr = null;
+ BHNode[] bhListArrRef = null;
+ BHNode[] oldBhListArrRef = null;
+ int bhListArrCnt = 0;
+ int bhListArrMaxCnt = 0;
+ int blockSize = 0;
+
+ BHInsertStructure(int length) {
+ randomNumber = new Random(0);
+
+ if(length > 50) {
+ length = 50;
+ }
+
+ blockSize = 50;
+ bhListArr = new ArrayList[length];
+ bhListArrRef = new BHNode[length];
+ bhListArrCnt = 0;
+ bhListArrMaxCnt = length;
+
+ }
+
+ void clear() {
+
+ for(int i=0; i< bhListArrCnt; i++) {
+ bhListArr[i].clear();
+ bhListArrRef[i] = null;
+ }
+ bhListArrCnt = 0;
+ }
+
+ void lookupAndInsert(BHNode parent, BHNode child) {
+ boolean found = false;
+
+ for ( int i=0; i<bhListArrCnt; i++ ) {
+ // check for current parent
+ if ( bhListArrRef[i] == parent ) {
+ // place child element in currents array of children
+ bhListArr[i].add(child);
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found ) {
+
+ if(bhListArrCnt >= bhListArrMaxCnt) {
+ // allocate a bigger array here....
+ if(debug)
+ System.out.println("(1) Expanding bhListArr array ...");
+ bhListArrMaxCnt += blockSize;
+ oldBhListArr = bhListArr;
+ oldBhListArrRef = bhListArrRef;
+
+ bhListArr = new ArrayList[bhListArrMaxCnt];
+ bhListArrRef = new BHNode[bhListArrMaxCnt];
+ System.arraycopy(oldBhListArr, 0, bhListArr, 0, oldBhListArr.length);
+ System.arraycopy(oldBhListArrRef, 0, bhListArrRef, 0,
+ oldBhListArrRef.length);
+ }
+
+ bhListArrRef[bhListArrCnt] = parent;
+ bhListArr[bhListArrCnt] = new ArrayList();
+ bhListArr[bhListArrCnt].add(child);
+ bhListArrCnt++;
+ }
+
+ }
+
+ void updateBoundingTree(BHTree bhTree) {
+
+ // based on the data in this stucture, update the tree such that
+ // all things work out now .. i.e for each element of the array list
+ // of bhListArr ... create a new reclustered tree.
+ int size, cnt;
+ BHNode child1, child2;
+
+ for ( int i=0; i < bhListArrCnt; i++ ) {
+ // extract and form an array of all children : l, r, and n1 ... nk
+ cnt = 0;
+ child1 = ((BHInternalNode)(bhListArrRef[i])).getLeftChild();
+ child2 = ((BHInternalNode)(bhListArrRef[i])).getRightChild();
+ if(child1 != null) cnt++;
+ if(child2 != null) cnt++;
+
+ size = bhListArr[i].size();
+
+ BHNode bhArr[] = new BHNode[cnt + size];
+
+ bhListArr[i].toArray(bhArr);
+
+ //reset cnt, so that we can reuse it.
+ cnt = 0;
+ if(child1 != null) {
+ bhArr[size] = child1;
+ cnt++;
+ bhArr[size + cnt] = child2;
+ }
+
+ if(debug2)
+ if((child1 == null) || (child2 == null)) {
+ System.out.println("child1 or child2 is null ...");
+ System.out.println("This is bad, it shouldn't happen");
+
+ }
+
+ ((BHInternalNode)(bhListArrRef[i])).setRightChild(null);
+ ((BHInternalNode)(bhListArrRef[i])).setLeftChild(null);
+
+ bhTree.cluster((BHInternalNode)bhListArrRef[i], bhArr);
+ }
+ }
+
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/BHInternalNode.java b/src/classes/share/javax/media/j3d/BHInternalNode.java
new file mode 100644
index 0000000..7c7a0b2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BHInternalNode.java
@@ -0,0 +1,196 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+class BHInternalNode extends BHNode {
+
+ static boolean debug2 = true;
+
+ BHNode rChild;
+ BHNode lChild;
+
+ BHInternalNode() {
+ super();
+ nodeType = BH_TYPE_INTERNAL;
+ this.rChild = null;
+ this.lChild = null;
+ }
+
+ BHInternalNode(BHNode parent) {
+ super(parent);
+ nodeType = BH_TYPE_INTERNAL;
+ this.rChild = null;
+ this.lChild = null;
+ }
+
+ BHInternalNode(BHNode parent, BHNode rChild, BHNode lChild) {
+ super(parent);
+ nodeType = BH_TYPE_INTERNAL;
+ this.rChild = rChild;
+ this.lChild = lChild;
+ }
+
+ BHInternalNode(BHNode parent, BoundingBox bHull) {
+ super(parent, bHull);
+ nodeType = BH_TYPE_INTERNAL;
+ this.rChild = null;
+ this.lChild = null;
+ }
+
+ BHInternalNode(BHNode parent, BHNode rChild, BHNode lChild, BoundingBox bHull) {
+ super(parent, bHull);
+ nodeType = BH_TYPE_INTERNAL;
+ this.rChild = rChild;
+ this.lChild = lChild;
+ }
+
+ BHNode getLeftChild() {
+ return (BHNode) lChild;
+ }
+
+ BHNode getRightChild() {
+ return (BHNode) rChild;
+ }
+
+ void setLeftChild(BHNode child) {
+ lChild = child;
+ }
+
+ void setRightChild(BHNode child) {
+ rChild = child;
+ }
+
+ void computeBoundingHull(BoundingBox bHull) {
+ computeBoundingHull();
+ bHull.set(this.bHull);
+ }
+
+ void computeBoundingHull() {
+ BoundingBox rChildBound = null;
+ BoundingBox lChildBound = null;
+ int i;
+
+ if((lChild==null) && (rChild==null)) {
+ bHull = null;
+ return;
+ }
+
+ if(lChild != null)
+ lChildBound = lChild.getBoundingHull();
+
+ if(rChild != null)
+ rChildBound = rChild.getBoundingHull();
+
+ if(bHull == null)
+ bHull = new BoundingBox();
+
+ // Since left child is null. bHull is equal to right child's Hull.
+ if(lChild == null) {
+ bHull.set(rChildBound);
+ return;
+ }
+
+ // Since right child is null. bHull is equal to left child's Hull.
+ if(rChild == null) {
+ bHull.set(lChildBound);
+ return;
+ }
+
+ // Compute the combined bounds of the children.
+ bHull.set(rChildBound);
+ bHull.combine(lChildBound);
+
+ }
+
+ void updateMarkedBoundingHull() {
+
+ if(mark == false)
+ return;
+
+ rChild.updateMarkedBoundingHull();
+ lChild.updateMarkedBoundingHull();
+ computeBoundingHull();
+ mark = false;
+
+ }
+
+ // this method inserts a single element into the tree given the stipulation
+ // that the current tree node already contains the child ... 3 cases
+ // one --node is inside the left child, and not inside the right
+ // so recurse placing it inside the left child
+ // two -- node is not inside the left but is inside the right
+ // recurse placing it inside the right child
+ // three -- node is not inside either one, added it to the current
+ // element
+
+ void insert( BHNode node, BHInsertStructure insertStructure ) {
+ // NOTE: the node must already be inside this node if its not then fail.
+ if(debug2)
+ if ( !this.isInside(node.bHull) ) {
+ System.out.println("Incorrect use of insertion, current node");
+ System.out.println("must contain the input element ...");
+ }
+
+ boolean insideRightChild = false;
+ boolean insideLeftChild = false;
+
+ // leaf children are considered inpenetrable for insert so returns false
+ if(this.rChild.nodeType == BHNode.BH_TYPE_LEAF) {
+ insideRightChild = false;
+ } else {
+ insideRightChild = this.rChild.isInside(node.bHull);
+ }
+ if(this.lChild.nodeType == BHNode.BH_TYPE_LEAF) {
+ insideLeftChild = false;
+ } else {
+ insideLeftChild = this.lChild.isInside(node.bHull);
+ }
+
+ if ( insideLeftChild && !insideRightChild ) {
+ ((BHInternalNode)this.lChild).insert(node, insertStructure);
+ } else if ( !insideLeftChild && insideRightChild ) {
+ ((BHInternalNode)this.rChild).insert(node, insertStructure);
+ } else if ( insideLeftChild && insideRightChild ) {
+ // choose randomly to put it in the left or right
+ if ( insertStructure.randomNumber.nextBoolean() ) {
+ ((BHInternalNode)this.lChild).insert(node, insertStructure);
+ } else {
+ ((BHInternalNode)this.rChild).insert(node, insertStructure);
+ }
+ } else {
+ // doesn't fit in either one ....
+ // lookup the current node this in the auxilaryInsertStructure
+ // if it appears then add element to the array of sub elements
+ // if not then allocate a new element to the array
+ insertStructure.lookupAndInsert(this, node);
+ }
+ }
+
+ void destroyTree(BHNode[] bhArr, int[] index) {
+
+ if(rChild != null)
+ rChild.destroyTree(bhArr, index);
+
+ if(lChild != null)
+ lChild.destroyTree(bhArr, index);
+
+ rChild = null;
+ lChild = null;
+
+ // add to free list ...
+ FreeListManager.freeObject(FreeListManager.BHINTERNAL, this);
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/BHLeafInterface.java b/src/classes/share/javax/media/j3d/BHLeafInterface.java
new file mode 100644
index 0000000..bf121ac
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BHLeafInterface.java
@@ -0,0 +1,26 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+interface BHLeafInterface {
+
+ abstract BoundingBox computeBoundingHull();
+
+ abstract boolean isEnable();
+
+ abstract boolean isEnable(int visibilityPolicy);
+
+ // Can't use getLocale, it is used by BranchGroupRetained
+ abstract Locale getLocale2();
+
+}
diff --git a/src/classes/share/javax/media/j3d/BHLeafNode.java b/src/classes/share/javax/media/j3d/BHLeafNode.java
new file mode 100644
index 0000000..21215e0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BHLeafNode.java
@@ -0,0 +1,91 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+class BHLeafNode extends BHNode {
+
+ BHLeafInterface leafIF;
+
+ BHLeafNode() {
+ super();
+ nodeType = BH_TYPE_LEAF;
+ leafIF = null;
+ }
+
+ BHLeafNode(BHNode parent) {
+ super(parent);
+ nodeType = BH_TYPE_LEAF;
+ }
+
+ BHLeafNode(BHLeafInterface lIF) {
+ super();
+ nodeType = BH_TYPE_LEAF;
+ leafIF = lIF;
+ }
+
+ BHLeafNode(BHNode parent, BHLeafInterface lIF) {
+ super(parent);
+ leafIF = lIF;
+ nodeType = BH_TYPE_LEAF;
+ }
+
+ BHLeafNode(BHNode parent, BoundingBox bHull) {
+ super(parent, bHull);
+ nodeType = BH_TYPE_LEAF;
+ }
+
+ BHLeafNode(BHNode parent, BHLeafInterface lIF, BoundingBox bHull) {
+ super(parent, bHull);
+ leafIF = lIF;
+ nodeType = BH_TYPE_LEAF;
+ }
+
+ void computeBoundingHull() {
+ bHull = leafIF.computeBoundingHull();
+ }
+
+ void updateMarkedBoundingHull() {
+
+ if(mark == false)
+ return;
+
+ computeBoundingHull();
+ mark = false;
+ }
+
+ boolean isEnable() {
+ return leafIF.isEnable();
+ }
+
+ boolean isEnable(int vis) {
+ return leafIF.isEnable(vis);
+ }
+
+ Locale getLocale() {
+ return leafIF.getLocale2();
+ }
+
+ void destroyTree(BHNode[] bhArr, int[] index) {
+ if(bhArr.length <= index[0]) {
+ // System.out.println("BHLeafNode : Problem bhArr overflow!!!");
+ return;
+ }
+
+ parent = null;
+ bhArr[index[0]] = this;
+ index[0]++;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/BHNode.java b/src/classes/share/javax/media/j3d/BHNode.java
new file mode 100644
index 0000000..7e11af3
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BHNode.java
@@ -0,0 +1,271 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+abstract class BHNode {
+
+ static final byte BH_TYPE_INTERNAL = 1;
+ static final byte BH_TYPE_LEAF = 2;
+
+ static final int NUMBER_OF_PLANES = 6;
+
+ static final boolean debug = false;
+ static final boolean debug2 = false;
+
+ BHNode parent;
+ byte nodeType;
+ BoundingBox bHull = null;
+ boolean mark;
+
+ BHNode () {
+ this.parent = null;
+ mark = false;
+ }
+
+ BHNode (BHNode parent) {
+ this.parent = parent;
+ mark = false;
+ }
+
+ BHNode (BHNode parent, BoundingBox bHull) {
+ this.parent = parent;
+ mark = false;
+
+ this.bHull = bHull;
+ }
+
+ BHNode getParent () {
+ return (this.parent) ;
+ }
+
+ abstract void computeBoundingHull();
+ abstract void updateMarkedBoundingHull();
+ abstract void destroyTree(BHNode[] bhArr, int[] index);
+
+ void setParent (BHNode node) {
+ this.parent = node;
+ }
+
+ BoundingBox getBoundingHull() {
+ return (this.bHull);
+ }
+
+ void setBoundingHull(BoundingBox bHull) {
+ this.bHull = bHull;
+ }
+
+ // given two nodes determine the bHull surrounding them, ie. the parent hull
+ void combineBHull(BHNode node1, BHNode node2 ) {
+ BoundingBox bHull1 = null;
+ BoundingBox bHull2 = null;
+
+ bHull1 = node1.getBoundingHull();
+ bHull2 = node2.getBoundingHull();
+
+ if(this.bHull==null)
+ this.bHull = new BoundingBox(bHull1);
+ else
+ this.bHull.set(bHull1);
+
+ this.bHull.combine(bHull2);
+
+ }
+
+ // returns true iff the bHull is completely inside this
+ // bounding hull i.e. bHull values are strictly less
+ // than or equal to all this.bHull values
+ boolean isInside(BoundingBox bHull) {
+ if(bHull == null)
+ return false;
+
+ if( this.bHull.isEmpty() || bHull.isEmpty() ) {
+ return false;
+ }
+
+ if( this.bHull.upper.x < bHull.upper.x ||
+ this.bHull.upper.y < bHull.upper.y ||
+ this.bHull.upper.z < bHull.upper.z ||
+ this.bHull.lower.x > bHull.lower.x ||
+ this.bHull.lower.y > bHull.lower.y ||
+ this.bHull.lower.z > bHull.lower.z )
+ return false;
+ else
+ return true;
+ }
+
+ // finds the node matching the search element in the tree and returns
+ // the node if found, else it returns null if the node couldn't be found
+ BHNode findNode(BHNode node) {
+ BHNode fNode = null;
+
+ if ( this.nodeType == BHNode.BH_TYPE_LEAF) {
+ if ( this == node ) {
+ return this;
+ }
+ }
+ else {
+ if (((BHInternalNode) this).rChild.isInside(node.bHull)) {
+ fNode = ((BHInternalNode)this).rChild.findNode(node);
+ if(fNode != null) {
+ return fNode;
+ }
+ }
+ if (((BHInternalNode)this).lChild.isInside(node.bHull)) {
+ return ((BHInternalNode)this).lChild.findNode(node);
+ }
+ }
+ return null;
+ }
+
+ void deleteFromParent() {
+ BHInternalNode parent;
+
+ // System.out.println("deleteFromParent - this " + this );
+ parent = (BHInternalNode) (this.parent);
+ if(parent != null) {
+ if(parent.rChild == this)
+ parent.rChild = null;
+ else if(parent.lChild == this)
+ parent.lChild = null;
+ else {
+ if(debug2) {
+ System.out.println("BHNode.java: Trouble! No match found. This can't happen.");
+ System.out.println("this " + this );
+ if ( this.nodeType == BHNode.BH_TYPE_INTERNAL) {
+ System.out.println("rChild " + ((BHInternalNode)this).rChild +
+ " lChild " + ((BHInternalNode)this).lChild);
+ }
+ System.out.println("parent " + parent +
+ " parent.rChild " + parent.rChild +
+ " parent.lChild " + parent.lChild);
+ }
+ }
+ }
+
+ // add to free list ...
+ VirtualUniverse.mc.addBHNodeToFreelists(this);
+ }
+
+ // delete all leaf nodes marked with DELETE_UPDATE and update the
+ // bounds of the parents node
+ BHNode deleteAndUpdateMarkedNodes() {
+
+ if (this.mark == true) {
+ if (this.nodeType == BH_TYPE_LEAF) {
+ this.deleteFromParent();
+ return null;
+
+ } else {
+ if(debug)
+ if(((BHInternalNode)(this)).rChild == ((BHInternalNode)(this)).lChild)
+ System.out.println("rChild " + ((BHInternalNode)(this)).rChild +
+ " lChild " + ((BHInternalNode)(this)).lChild);
+
+
+ if(((BHInternalNode)(this)).rChild != null)
+ ((BHInternalNode)(this)).rChild =
+ ((BHInternalNode)(this)).rChild.deleteAndUpdateMarkedNodes();
+ if(((BHInternalNode)(this)).lChild != null)
+ ((BHInternalNode)(this)).lChild =
+ ((BHInternalNode)(this)).lChild.deleteAndUpdateMarkedNodes();
+
+ if ((((BHInternalNode)(this)).rChild == null) &&
+ (((BHInternalNode)(this)).lChild == null)) {
+ this.deleteFromParent();
+ return null;
+ } else {
+ if ( ((BHInternalNode)this).rChild == null ) {
+ BHNode leftChild = ((BHInternalNode)this).lChild;
+ leftChild.parent = this.parent;
+ // delete self, return lChild
+ this.deleteFromParent();
+ return leftChild;
+ } else if ( ((BHInternalNode)this).lChild == null ) {
+ BHNode rightChild = ((BHInternalNode)this).rChild;
+ rightChild.parent = this.parent;
+ // delete self, return rChild
+ this.deleteFromParent();
+ return rightChild;
+ } else {
+ // recompute your bounds and return yourself
+ this.combineBHull(((BHInternalNode)this).rChild,
+ ((BHInternalNode)this).lChild);
+ // update the parent's pointers
+ ((BHInternalNode)this).rChild.parent = this;
+ ((BHInternalNode)this).lChild.parent = this;
+ this.mark = false;
+ return this;
+ }
+ }
+ }
+ } else {
+ // mark is NOT set, simply return self
+ return this;
+ }
+ }
+
+
+ // generic tree gathering statistics operations
+
+ int countNumberOfInternals() {
+ if ( this.nodeType == BHNode.BH_TYPE_LEAF ) {
+ return 0;
+ } else {
+ return (((BHInternalNode)this).rChild.countNumberOfInternals() +
+ ((BHInternalNode)this).lChild.countNumberOfInternals() + 1 );
+ }
+ }
+
+ // recursively traverse the tree and compute the total number of leaves
+ int countNumberOfLeaves() {
+ if ( this.nodeType == BHNode.BH_TYPE_LEAF ) {
+ return 1;
+ } else {
+ return ( ((BHInternalNode)this).rChild.countNumberOfLeaves() +
+ ((BHInternalNode)this).lChild.countNumberOfLeaves() );
+ }
+ }
+
+
+ // traverse tree and compute the maximum depth to a leaf
+ int computeMaxDepth (int currentDepth) {
+ if ( this.nodeType == BHNode.BH_TYPE_LEAF ) {
+ return (currentDepth);
+ } else {
+ int rightDepth = ((BHInternalNode)this).rChild.computeMaxDepth(currentDepth + 1);
+ int leftDepth = ((BHInternalNode)this).lChild.computeMaxDepth(currentDepth + 1);
+ if( rightDepth > leftDepth )
+ return rightDepth;
+ return leftDepth;
+ }
+ }
+
+ // compute the average depth of the leaves ...
+ float computeAverageLeafDepth ( int numberOfLeaves, int currentDepth ) {
+ int sumOfDepths = this.computeSumOfDepths(0);
+ return ( (float)sumOfDepths / (float)numberOfLeaves );
+ }
+
+ int computeSumOfDepths ( int currentDepth ) {
+ if ( this.nodeType == BHNode.BH_TYPE_LEAF ) {
+ return ( currentDepth );
+ } else {
+ return (((BHInternalNode)this).rChild.computeSumOfDepths(currentDepth + 1) +
+ ((BHInternalNode)this).lChild.computeSumOfDepths(currentDepth + 1) ) ;
+ }
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/BHTree.java b/src/classes/share/javax/media/j3d/BHTree.java
new file mode 100644
index 0000000..ef622c2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BHTree.java
@@ -0,0 +1,1136 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+import java.util.Vector;
+import javax.vecmath.Point4d;
+
+class BHTree {
+
+ Locale locale;
+ BHNode root;
+ BHInsertStructure insertStructure = null;
+
+
+ // Temporary point, so we dont generate garbage
+ Point4d tPoint4d = new Point4d();
+
+ // A flag to signal that number of renderAtoms sent to RenderBin is stable.
+ private boolean stable = false;
+
+ // An estimate of the maxmium depth of this tree (upper bound).
+ int estMaxDepth;
+
+ static final double LOG_OF_2 = Math.log(2);
+
+ // Assume that the size avg. leaf node is 256 bytes. For a 64bit system, we'll
+ // down with max depth of 56 for an ideal balance tree.
+ static final int DEPTH_UPPER_BOUND = 56;
+ static final int INCR_DEPTH_BOUND = 5;
+ int depthUpperBound = DEPTH_UPPER_BOUND;
+
+ BHTree() {
+ locale = null;
+ root = null;
+ }
+
+ BHTree(Locale loc) {
+ locale = loc;
+ root = null;
+ }
+
+ BHTree(BHNode bhArr[]) {
+ locale = null;
+ root = null;
+ create(bhArr);
+ }
+
+ void setLocale(Locale loc) {
+ locale = loc;
+ }
+
+ Locale getLocale() {
+ return locale;
+ }
+
+ void cluster(BHInternalNode root, BHNode[] bhArr) {
+
+ if(J3dDebug.devPhase) {
+ if(J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4,
+ "BHTree.java :In cluster length is " + bhArr.length
+ + "\n")) {
+
+ for(int i=0; i<bhArr.length; i++) {
+ System.out.println(bhArr[i]);
+ }
+ }
+ }
+
+ if((bhArr == null) || (bhArr.length < 2) || (root == null)){
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_1,
+ "BHTree.java : cluster : trouble! \n");
+ return;
+ }
+
+ int centerValuesIndex[] = new int[bhArr.length];
+ float centerValues[][] = computeCenterValues(bhArr, centerValuesIndex);
+
+ constructTree(root, bhArr, centerValues, centerValuesIndex);
+
+ }
+
+ // bhArr can only contains BHLeafNode.
+
+ void boundsChanged(BHNode bhArr[], int size) {
+ // Mark phase.
+ markParentChain(bhArr, size);
+
+ // Compute phase.
+ root.updateMarkedBoundingHull();
+ }
+
+
+ // Return true if bhTree's root in encompass by frustumBBox and nothing changed.
+ boolean getVisibleBHTrees(RenderBin rBin, ArrayList bhTrees,
+ BoundingBox frustumBBox, long referenceTime,
+ boolean stateChanged, int visibilityPolicy,
+ boolean singleLocale) {
+
+ int i, j, size;
+
+ if ((frustumBBox != null) && (root != null)) {
+
+ boolean inSide = aEncompassB(frustumBBox, root.bHull);
+ /*
+ System.out.println("stateChanged is " + stateChanged);
+ System.out.println("frustumBBox is " + frustumBBox);
+ System.out.println("root.bHull is " + root.bHull);
+ System.out.println("inSide is " + inSide);
+ */
+
+ if(singleLocale && !stateChanged && inSide && stable) {
+ // just return the whole tree, no change in render mol..
+ // System.out.println("Optimize case 1 ..." + this);
+ bhTrees.add(root);
+ return true;
+ }
+ else if(!stateChanged && inSide) {
+ // the whole tree is in, but we've to be sure that RenderBin is
+ // stable ...
+ // System.out.println("Optimize case 2 ..." + this);
+ select(rBin, bhTrees, frustumBBox, root, referenceTime,
+ visibilityPolicy, true);
+
+ bhTrees.add(root);
+ stable = true;
+ } else {
+ // System.out.println("Not in Optimize case ..." + this);
+ select(rBin, bhTrees, frustumBBox, root, referenceTime,
+ visibilityPolicy, false);
+
+ stable = false;
+ }
+
+ }
+
+ return false;
+ }
+
+ private void select(RenderBin rBin, ArrayList bhTrees, BoundingBox frustumBBox,
+ BHNode bh, long referenceTime, int visibilityPolicy,
+ boolean inSide) {
+
+ if ((bh == null) || (bh.bHull.isEmpty())) {
+ return;
+ }
+
+ switch(bh.nodeType) {
+ case BHNode.BH_TYPE_LEAF:
+ if((((BHLeafNode) bh).leafIF instanceof GeometryAtom) &&
+ (((BHLeafNode) bh).isEnable(visibilityPolicy)) &&
+ ((inSide) || (frustumBBox.intersect(bh.bHull)))) {
+
+ // do render atom setup.
+ rBin.processGeometryAtom((GeometryAtom)
+ (((BHLeafNode)bh).leafIF),
+ referenceTime);
+ if(!inSide) {
+ bhTrees.add(bh);
+ }
+ }
+ break;
+ case BHNode.BH_TYPE_INTERNAL:
+ if(inSide) {
+ select(rBin, bhTrees, frustumBBox,
+ ((BHInternalNode)bh).getRightChild(),
+ referenceTime, visibilityPolicy, true);
+ select(rBin, bhTrees, frustumBBox,
+ ((BHInternalNode)bh).getLeftChild(),
+ referenceTime, visibilityPolicy, true);
+ }
+ else if(aEncompassB(frustumBBox, bh.bHull)) {
+ bhTrees.add(bh);
+ select(rBin, bhTrees, frustumBBox,
+ ((BHInternalNode)bh).getRightChild(),
+ referenceTime, visibilityPolicy, true);
+ select(rBin, bhTrees, frustumBBox,
+ ((BHInternalNode)bh).getLeftChild(),
+ referenceTime, visibilityPolicy, true);
+ }
+ else if(frustumBBox.intersect(bh.bHull)) {
+ select(rBin, bhTrees, frustumBBox,
+ ((BHInternalNode)bh).getRightChild(),
+ referenceTime, visibilityPolicy, false);
+ select(rBin, bhTrees, frustumBBox,
+ ((BHInternalNode)bh).getLeftChild(),
+ referenceTime, visibilityPolicy, false);
+ }
+ break;
+ }
+ }
+
+ // returns true iff the bBox is completely inside aBox
+ // i.e. bBoxl values are strictly less than or equal to all aBox values.
+ static boolean aEncompassB(BoundingBox aBox, BoundingBox bBox) {
+ return ((aBox.upper.x >= bBox.upper.x) &&
+ (aBox.upper.y >= bBox.upper.y) &&
+ (aBox.upper.z >= bBox.upper.z) &&
+ (aBox.lower.x <= bBox.lower.x) &&
+ (aBox.lower.y <= bBox.lower.y) &&
+ (aBox.lower.z <= bBox.lower.z));
+ }
+
+
+ BHLeafInterface selectAny(GeometryAtom atom, int accurancyMode) {
+ if (atom.source.geometryList == null)
+ return null;
+ BHNode bhNode = doSelectAny(atom, root, accurancyMode);
+ if (bhNode == null) {
+ return null;
+ }
+
+ return ((BHLeafNode) bhNode).leafIF;
+ }
+
+
+ BHLeafInterface selectAny(GeometryAtom atoms[], int size, int accurancyMode) {
+ BHNode bhNode = doSelectAny(atoms, size, root, accurancyMode);
+ if (bhNode == null) {
+ return null;
+ }
+
+ return ((BHLeafNode) bhNode).leafIF;
+ }
+
+
+ private BHNode doSelectAny(GeometryAtom atoms[],int atomSize,
+ BHNode bh, int accurancyMode) {
+ if ((bh == null) || (bh.bHull.isEmpty())) {
+ return null;
+ }
+ switch (bh.nodeType) {
+ case BHNode.BH_TYPE_LEAF:
+ BHLeafInterface leaf = ((BHLeafNode) bh).leafIF;
+ GeometryAtom atom;
+ int i;
+
+ if (leaf instanceof GeometryAtom) {
+ GeometryAtom leafAtom = (GeometryAtom) leaf;
+
+ if (((BHLeafNode) bh).isEnable() &&
+ leafAtom.source.isCollidable) {
+
+ // atom self intersection between atoms[]
+ for (i=atomSize-1; i >=0; i--) {
+ if (atoms[i] == leafAtom) {
+ return null;
+ }
+ }
+ for (i=atomSize-1; i >=0; i--) {
+ atom = atoms[i];
+ if ((atom.source.sourceNode != leafAtom.source.sourceNode) &&
+ (atom.source.collisionVwcBound.intersect(leafAtom.source.collisionVwcBound)) &&
+ ((accurancyMode == WakeupOnCollisionEntry.USE_BOUNDS) ||
+ ((leafAtom.source.geometryList != null) &&
+ (atom.source.intersectGeometryList(leafAtom.source))))) {
+ return bh;
+ }
+ }
+ }
+ } else if (leaf instanceof GroupRetained) {
+ if (((BHLeafNode) bh).isEnable() &&
+ ((GroupRetained) leaf).sourceNode.collidable) {
+ for (i=atomSize-1; i >=0; i--) {
+ atom = atoms[i];
+ if (atom.source.collisionVwcBound.intersect(bh.bHull) &&
+ ((accurancyMode == WakeupOnCollisionEntry.USE_BOUNDS) ||
+ (atom.source.intersectGeometryList(
+ atom.source.getCurrentLocalToVworld(0), bh.bHull)))) {
+ return bh;
+ }
+ }
+ }
+ }
+ return null;
+ case BHNode.BH_TYPE_INTERNAL:
+ for (i=atomSize-1; i >=0; i--) {
+ atom = atoms[i];
+ if (atom.source.collisionVwcBound.intersect(bh.bHull))
+ {
+ BHNode hitNode = doSelectAny(atoms,
+ atomSize,
+ ((BHInternalNode) bh).getRightChild(),
+ accurancyMode);
+ if (hitNode != null)
+ return hitNode;
+
+ return doSelectAny(atoms, atomSize,
+ ((BHInternalNode) bh).getLeftChild(),
+ accurancyMode);
+ }
+ }
+ return null;
+ }
+ return null;
+ }
+
+
+ private BHNode doSelectAny(GeometryAtom atom, BHNode bh, int accurancyMode) {
+ if ((bh == null) || (bh.bHull.isEmpty())) {
+ return null;
+ }
+ switch (bh.nodeType) {
+ case BHNode.BH_TYPE_LEAF:
+ BHLeafInterface leaf = ((BHLeafNode) bh).leafIF;
+ if (leaf instanceof GeometryAtom) {
+ GeometryAtom leafAtom = (GeometryAtom) leaf;
+ if ((atom.source.sourceNode != leafAtom.source.sourceNode) &&
+ (((BHLeafNode) bh).isEnable()) &&
+ (leafAtom.source.isCollidable) &&
+ (atom.source.collisionVwcBound.intersect(leafAtom.source.collisionVwcBound)) &&
+ ((accurancyMode == WakeupOnCollisionEntry.USE_BOUNDS) ||
+ ((leafAtom.source.geometryList != null) &&
+ (atom.source.intersectGeometryList(leafAtom.source))))) {
+ return bh;
+ }
+ } else if (leaf instanceof GroupRetained) {
+ if (((BHLeafNode) bh).isEnable() &&
+ ((GroupRetained) leaf).sourceNode.collidable &&
+ atom.source.collisionVwcBound.intersect(bh.bHull) &&
+ ((accurancyMode == WakeupOnCollisionEntry.USE_BOUNDS) ||
+ (atom.source.intersectGeometryList(
+ atom.source.getCurrentLocalToVworld(0), bh.bHull)))) {
+ return bh;
+ }
+ }
+ return null;
+ case BHNode.BH_TYPE_INTERNAL:
+ if (atom.source.collisionVwcBound.intersect(bh.bHull)) {
+ BHNode hitNode = doSelectAny(atom,
+ ((BHInternalNode) bh).getRightChild(),
+ accurancyMode);
+ if (hitNode != null)
+ return hitNode;
+
+ return doSelectAny(atom,
+ ((BHInternalNode) bh).getLeftChild(),
+ accurancyMode);
+ }
+ return null;
+ }
+ return null;
+ }
+
+ BHLeafInterface selectAny(Bounds bound, int accurancyMode,
+ NodeRetained armingNode) {
+ if (bound == null) {
+ return null;
+ }
+ BHNode bhNode = doSelectAny(bound, root, accurancyMode, armingNode);
+ if (bhNode == null) {
+ return null;
+ }
+ return ((BHLeafNode) bhNode).leafIF;
+ }
+
+ private BHNode doSelectAny(Bounds bound, BHNode bh, int accurancyMode,
+ NodeRetained armingNode) {
+ if ((bh == null) || (bh.bHull.isEmpty())) {
+ return null;
+ }
+
+ switch (bh.nodeType) {
+ case BHNode.BH_TYPE_LEAF:
+ BHLeafInterface leaf = ((BHLeafNode) bh).leafIF;
+ if (leaf instanceof GeometryAtom) {
+ GeometryAtom leafAtom = (GeometryAtom) leaf;
+ if ((((BHLeafNode) bh).isEnable()) &&
+ (leafAtom.source.isCollidable) &&
+ (bound.intersect(leafAtom.source.collisionVwcBound)) &&
+ ((accurancyMode == WakeupOnCollisionEntry.USE_BOUNDS) ||
+ ((leafAtom.source.geometryList != null) &&
+ (leafAtom.source.intersectGeometryList(
+ leafAtom.source.getCurrentLocalToVworld(0), bound))))) {
+ return bh;
+ }
+ } else if (leaf instanceof GroupRetained) {
+ if ((leaf != armingNode) &&
+ ((BHLeafNode) bh).isEnable() &&
+ ((GroupRetained) leaf).sourceNode.collidable &&
+ bound.intersect(bh.bHull)) {
+ return bh;
+ }
+ }
+ return null;
+ case BHNode.BH_TYPE_INTERNAL:
+ if (bound.intersect(bh.bHull)) {
+ BHNode hitNode = doSelectAny(bound,
+ ((BHInternalNode) bh).getRightChild(),
+ accurancyMode,
+ armingNode);
+ if (hitNode != null)
+ return hitNode;
+
+ return doSelectAny(bound,
+ ((BHInternalNode) bh).getLeftChild(),
+ accurancyMode,
+ armingNode);
+ }
+ return null;
+ }
+ return null;
+ }
+
+
+ BHLeafInterface selectAny(Bounds bound, int accurancyMode,
+ GroupRetained armingGroup) {
+ if (bound == null) {
+ return null;
+ }
+ BHNode bhNode = doSelectAny(bound, root, accurancyMode, armingGroup);
+ if (bhNode == null) {
+ return null;
+ }
+ return ((BHLeafNode) bhNode).leafIF;
+ }
+
+ private BHNode doSelectAny(Bounds bound, BHNode bh, int accurancyMode,
+ GroupRetained armingGroup) {
+ if ((bh == null) || (bh.bHull.isEmpty())) {
+ return null;
+ }
+ switch (bh.nodeType) {
+ case BHNode.BH_TYPE_LEAF:
+ BHLeafInterface leaf = ((BHLeafNode) bh).leafIF;
+
+ if (leaf instanceof GeometryAtom) {
+ GeometryAtom leafAtom = (GeometryAtom) leaf;
+ if ((((BHLeafNode) bh).isEnable()) &&
+ (leafAtom.source.isCollidable) &&
+ (bound.intersect(leafAtom.source.collisionVwcBound)) &&
+ (!isDescendent(leafAtom.source.sourceNode,
+ armingGroup, leafAtom.source.key)) &&
+ ((accurancyMode == WakeupOnCollisionEntry.USE_BOUNDS) ||
+ ((leafAtom.source.geometryList != null) &&
+ (leafAtom.source.intersectGeometryList(
+ leafAtom.source.getCurrentLocalToVworld(0), bound))))) {
+ return bh;
+ }
+ } else if (leaf instanceof GroupRetained) {
+ GroupRetained group = (GroupRetained) leaf;
+ if (((BHLeafNode) bh).isEnable() &&
+ group.sourceNode.collidable &&
+ bound.intersect(bh.bHull) &&
+ !isDescendent(group.sourceNode, armingGroup, group.key)) {
+ return bh;
+ }
+ }
+ return null;
+ case BHNode.BH_TYPE_INTERNAL:
+ if (bound.intersect(bh.bHull)) {
+ BHNode hitNode = doSelectAny(bound,
+ ((BHInternalNode) bh).getRightChild(),
+ accurancyMode,
+ armingGroup);
+ if (hitNode != null)
+ return hitNode;
+
+ return doSelectAny(bound,
+ ((BHInternalNode) bh).getLeftChild(),
+ accurancyMode,
+ armingGroup);
+ }
+ return null;
+ }
+ return null;
+ }
+
+ // Return true if node is a descendent of group
+ private boolean isDescendent(NodeRetained node,
+ GroupRetained group,
+ HashKey key) {
+
+ synchronized (group.universe.sceneGraphLock) {
+ if (node.inSharedGroup) {
+ // getlastNodeId() will destroy this key
+ if (key != null) {
+ key = new HashKey(key);
+ }
+ }
+
+ do {
+ if (node == group) {
+ return true;
+ }
+ if (node instanceof SharedGroupRetained) {
+ // retrieve the last node ID
+ String nodeId = key.getLastNodeId();
+ NodeRetained prevNode = node;
+ Vector parents = ((SharedGroupRetained) node).parents;
+ for(int i=parents.size()-1; i >=0; i--) {
+ NodeRetained link = (NodeRetained) parents.elementAt(i);
+ if (link.nodeId.equals(nodeId)) {
+ node = link;
+ break;
+ }
+ }
+ if (prevNode == node) {
+ // branch is already detach
+ return true;
+ }
+ }
+ node = node.parent;
+ } while (node != null); // reach Locale
+ }
+ return false;
+ }
+
+
+ void select(PickShape pickShape, UnorderList hitArrList) {
+
+ if((pickShape == null)||(root == null))
+ return;
+
+ doSelect(pickShape, hitArrList, root, tPoint4d);
+
+ }
+
+
+ private void doSelect(PickShape pickShape, UnorderList hitArrList,
+ BHNode bh, Point4d pickPos) {
+
+ if ((bh == null) || (bh.bHull.isEmpty())) {
+ return;
+ }
+
+ switch(bh.nodeType) {
+ case BHNode.BH_TYPE_LEAF:
+ if (((BHLeafNode)(bh)).isEnable() &&
+ (((BHLeafNode) bh).leafIF instanceof GeometryAtom) &&
+ ((GeometryAtom) (((BHLeafNode)
+ bh).leafIF)).source.isPickable &&
+ pickShape.intersect(bh.bHull, pickPos)) {
+ hitArrList.add(bh);
+ }
+ break;
+ case BHNode.BH_TYPE_INTERNAL:
+ if (pickShape.intersect(bh.bHull, pickPos)) {
+ doSelect(pickShape,
+ hitArrList,
+ ((BHInternalNode)bh).getRightChild(),
+ pickPos);
+ doSelect(pickShape,
+ hitArrList,
+ ((BHInternalNode)bh).getLeftChild(),
+ pickPos);
+ }
+ break;
+ }
+ }
+
+ BHNode selectAny(PickShape pickShape) {
+
+ if((pickShape == null)||(root == null))
+ return null;
+
+ return doSelectAny(pickShape, root, tPoint4d);
+
+ }
+
+
+ private BHNode doSelectAny(PickShape pickShape, BHNode bh, Point4d pickPos) {
+
+ BHNode hitNode = null;
+
+ if((bh == null) || (bh.bHull.isEmpty()))
+ return null;
+
+ switch(bh.nodeType) {
+ case BHNode.BH_TYPE_LEAF:
+ if (((BHLeafNode)(bh)).isEnable() &&
+ (((BHLeafNode) bh).leafIF instanceof GeometryAtom) &&
+ ((GeometryAtom) (((BHLeafNode)
+ bh).leafIF)).source.isPickable &&
+ pickShape.intersect(bh.bHull, pickPos)) {
+ return bh;
+ }
+ break;
+ case BHNode.BH_TYPE_INTERNAL:
+ if (pickShape.intersect(bh.bHull, pickPos)) {
+ hitNode = doSelectAny(pickShape,
+ ((BHInternalNode)bh).getRightChild(),
+ pickPos);
+
+ if (hitNode != null) {
+ return hitNode;
+ }
+
+ return doSelectAny(pickShape,
+ ((BHInternalNode)bh).getLeftChild(),
+ pickPos);
+ }
+ break;
+ }
+ return null;
+ }
+
+
+ private void create(BHNode bhArr[]) {
+ int i;
+
+ if(bhArr == null) {
+ root = null;
+ return;
+ }
+
+ if(bhArr.length == 1) {
+ bhArr[0].computeBoundingHull();
+ root = (BHNode)bhArr[0];
+ return;
+ }
+
+ int centerValuesIndex[] = new int[bhArr.length];
+ float centerValues[][] = computeCenterValues(bhArr, centerValuesIndex);
+
+ /*
+ System.out.println("Length of array is " + bhArr.length);
+ for(int kk=0; kk<bhArr.length;kk++) {
+ System.out.println("( " + centerValues[kk][0] + ", " +
+ centerValues[kk][1] + ", " + centerValues[kk][2] + " )");
+ }
+ */
+
+ root = VirtualUniverse.mc.getBHNode(BHNode.BH_TYPE_INTERNAL);
+ constructTree((BHInternalNode) root, bhArr, centerValues,
+ centerValuesIndex);
+
+
+ if(J3dDebug.devPhase && J3dDebug.debug)
+ gatherTreeStatistics();
+
+ }
+
+ void insert(BHNode bhArr[], int size) {
+ // first pass: add all elements to the tree creating k array internal
+ // nodes using the auxiliaryInsertStucture
+ // second pass: go through all elements of the auxiliaryInsertStructure
+ // and then update these nodes reclustering the trees with the new
+ // k element siblings ...
+
+ if(J3dDebug.devPhase && J3dDebug.debug)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4,
+ "BHTree.java : Insert - bhArr.length is " +
+ bhArr.length + "\n");
+
+ if((bhArr == null) || (size < 1) || (bhArr.length < 1))
+ return;
+
+ if(root == null) {
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4,
+ "BHTree.java : Tree has not be created yet.\n");
+
+ // This extra "new" is needed, because create() require its input
+ // array's length be equal to the number of inserted nodes.
+ BHNode[] newbhArr = new BHNode[size];
+ System.arraycopy(bhArr, 0, newbhArr, 0, size);
+ create(newbhArr);
+ return;
+ }
+
+ if(root.nodeType == BHNode.BH_TYPE_LEAF) {
+ BHNode[] oldBhArr = bhArr;
+ bhArr = new BHNode[size + 1];
+ System.arraycopy(oldBhArr, 0, bhArr, 0, size);
+ bhArr[size] = root;
+ create(bhArr);
+ return;
+ }
+
+ if(insertStructure == null) {
+ insertStructure = new BHInsertStructure(size);
+ }
+ else {
+ insertStructure.clear();
+ }
+
+ for (int i=0; i<size; i++) {
+ // test if its inside the 'root' element
+ if ( root.isInside(bhArr[i].bHull) ) {
+ ((BHInternalNode)root).insert(bhArr[i], insertStructure);
+ }
+ else {
+ // extend the bounds of root by joining with current element
+ root.bHull.combine(bhArr[i].bHull);
+ insertStructure.lookupAndInsert(root, bhArr[i]);
+ }
+ }
+
+ insertStructure.updateBoundingTree(this);
+ // System.out.println("BHTree - Inserting ...");
+
+ // Guard against size<1 is done at the start of this method.
+ estMaxDepth += (int) (Math.log(size)/LOG_OF_2) + 1;
+
+ if(estMaxDepth > depthUpperBound) {
+ int maxDepth = root.computeMaxDepth(0);
+ int leafCount = root.countNumberOfLeaves();
+ double compDepth = Math.log(leafCount)/LOG_OF_2;
+ /*
+ System.out.println("BHTree - evaluate for reConstructTree ...");
+ System.out.println("compDepth " + compDepth);
+ System.out.println("maxDepth " + maxDepth);
+ System.out.println("leafCount " + leafCount);
+ */
+
+ // Upper bound guard.
+ if(maxDepth > depthUpperBound) {
+ reConstructTree(leafCount);
+ maxDepth = root.computeMaxDepth(0);
+ /*
+ System.out.println("BHTree - Did reConstructTree ...");
+ System.out.println("compDepth " + compDepth);
+ System.out.println("maxDepth " + maxDepth);
+ */
+ }
+
+ // Adjust depthUpperBound according to app. need.
+ // If we encounter lots of overlapping bounds, the re-balanced
+ // tree may not be an ideal balance tree. So there might be a
+ // likehood of maxDepth exceeding the preset depthUpperBound.
+ if(maxDepth > depthUpperBound) {
+ depthUpperBound = depthUpperBound + INCR_DEPTH_BOUND;
+ }else if((depthUpperBound != DEPTH_UPPER_BOUND) &&
+ (maxDepth * 1.5 < depthUpperBound)) {
+ depthUpperBound = depthUpperBound - INCR_DEPTH_BOUND;
+
+ if(depthUpperBound < DEPTH_UPPER_BOUND) {
+ // Be sure that DEPTH_UPPER_BOUND is the min.
+ depthUpperBound = DEPTH_UPPER_BOUND;
+ }
+ }
+
+ // This is the only place for resetting estMaxDepth to the tree real
+ // maxDepth. Hence in cases where tree may get deteriorate fast, such
+ // as multiple inserts and deletes frequently. estMaxDepth is accuminated,
+ // and will lead to tree re-evaluation and possibly re-balancing.
+ estMaxDepth = maxDepth;
+ }
+
+ }
+
+
+ // mark all elements of the node and its parent as needing updating
+ private void markParentChain(BHNode[] nArr, int size) {
+ BHNode node;
+
+ for(int i=0; i<size; i++) {
+ node = nArr[i];
+ node.mark = true;
+ while((node.parent != null) && (node.parent.mark == false)) {
+ node = node.parent;
+ node.mark = true;
+ }
+ }
+ }
+
+ // mark all elements of the node and its parent as needing updating
+ private void markParentChain(BHNode node) {
+ node.mark = true;
+ while((node.parent != null) && (node.parent.mark == false)) {
+ node = node.parent;
+ node.mark = true;
+ }
+ }
+
+ // Delete a series of n node elements from the input binary tree.
+ // These elements are removed from the tree in a 2 phase process.
+ // First, all elements to be removed are marked and all parent
+ // chains are marked ... then a second phase of the algorithm goes
+ // through and deletes them and updates all of the bounds ...
+
+ // delete the n elements in the array from the tree
+ void delete(BHNode bhArr[], int size) {
+ BHNode node;
+
+ /*
+ if((bhArr == null) || (bhArr.length < 1))
+ return;
+ System.out.println("BHTree.java : delete - bhArr.length is " +
+ bhArr.length);
+ for(int i=0; i<bhArr.length; i++)
+ System.out.println("bhArr[" + i +"] " + bhArr[i]);
+
+ */
+
+ for(int i=0; i<size; i++) {
+ if((bhArr[i] != null) && (bhArr[i].nodeType == BHNode.BH_TYPE_LEAF)) {
+ markParentChain(bhArr[i]);
+ } else {
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_1,
+ "Warning, element " + i + " is null/not leaf node.\n"
+ + "Error in deletion routine, element " +
+ bhArr[i] + "\n" +
+ "In tree = " + this +
+ " can not delete it ...\n");
+ }
+
+ }
+
+ root = root.deleteAndUpdateMarkedNodes();
+
+ if(J3dDebug.devPhase)
+ if (root == null) {
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4,
+ "Tree has been completely deleted ...\n");
+ }
+ }
+
+ // compute the center values along each of the three dimensions given
+ // the array of leaf objects to be split and joined
+ float[][] computeCenterValues(BHNode[] bhArr, int[] cIndex) {
+ float centers[][] = new float[bhArr.length][3];
+
+ // compute the center values of the input array of nodes
+ for ( int i=0; i < bhArr.length; i++ ) {
+ cIndex[i] = i;
+
+ bhArr[i].computeBoundingHull();
+
+ centers[i][0] =
+ (float)((bhArr[i].bHull.upper.x + bhArr[i].bHull.lower.x))/ 2.0f;
+ centers[i][1] =
+ (float)((bhArr[i].bHull.upper.y + bhArr[i].bHull.lower.y)) / 2.0f;
+ centers[i][2] =
+ (float)((bhArr[i].bHull.upper.z + bhArr[i].bHull.lower.z)) / 2.0f;
+
+ }
+ return centers;
+ }
+
+
+ void computeMeansAndSumSquares(float[][] centerValues, int[] centerValuesIndex,
+ float[] means, float[] ss) {
+
+ int i, arrLen;
+ float sumCenters[] = new float[3];
+ float temp = 0.0f;
+
+ arrLen = centerValuesIndex.length;
+ // Initialization.
+ for(i=2; i>=0; i--) {
+ sumCenters[i] = 0.0f;
+ ss[i] = 0.0f;
+ }
+
+ for(i=arrLen-1; i>=0 ; i--) {
+ sumCenters[0] += centerValues[centerValuesIndex[i]][0];
+ sumCenters[1] += centerValues[centerValuesIndex[i]][1];
+ sumCenters[2] += centerValues[centerValuesIndex[i]][2];
+ }
+
+ means[0] = sumCenters[0]/(float)arrLen;
+ means[1] = sumCenters[1]/(float)arrLen;
+ means[2] = sumCenters[2]/(float)arrLen;
+
+ for(i=arrLen-1; i>=0 ; i--) {
+ temp = (centerValues[centerValuesIndex[i]][0] - means[0]);
+ ss[0] += (temp*temp);
+ temp = (centerValues[centerValuesIndex[i]][1] - means[1]);
+ ss[1] += (temp*temp);
+ temp = (centerValues[centerValuesIndex[i]][2] - means[2]);
+ ss[2] += (temp*temp);
+
+ }
+
+ }
+
+ // find the split axis (the highest ss and return its index) for
+ // a given set of ss values
+ int findSplitAxis ( float ss[] ) {
+ int splitAxis = -1;
+ float maxSS = 0.0f;
+
+ // the largest ss index value
+ for (int i=0; i < 3; i++) {
+ if ( ss[i] > maxSS ) {
+ maxSS = ss[i];
+ splitAxis = i;
+ }
+ }
+ return splitAxis;
+ }
+
+ // Recursive method for constructing a binary tree.
+ void constructTree( BHInternalNode parent, BHNode bhArr[],
+ float[][] centerValues,
+ int[] centerValuesIndex ){
+
+ int i, splitAxis;
+ int rightSetCount = 0;
+ int leftSetCount = 0;
+ float means[] = new float[3];
+ float ss[] = new float[3];
+
+ if(J3dDebug.devPhase)
+ if ( bhArr.length <= 1 ) {
+ // this is only here for debugging can be removed after testing
+ // to ensure that it never gets called
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_1,
+ "constructTree - bhArr.length <= 1. Bad !!!\n");
+ }
+
+ computeMeansAndSumSquares(centerValues, centerValuesIndex, means, ss);
+
+ splitAxis = findSplitAxis(ss);
+
+ // an array of decision variables for storing the values of inside
+ // the right or left set for a particular element of bhArr.
+ // true if its in the left set, false if its in the right set
+ boolean leftOrRightSet[] = new boolean[bhArr.length];
+
+ if ( splitAxis == -1 ) {
+ // This is bad. Since we can't find a split axis, the best thing
+ // to do is to split the set in two sets; each with about the
+ // same number of elements. By doing this we can avoid constructing
+ // a skew tree.
+
+ // split elements into half.
+ for ( i=0; i < bhArr.length; i++) {
+ if(leftSetCount > rightSetCount) {
+ rightSetCount++;
+ leftOrRightSet[i] = false;
+ } else {
+ leftSetCount++;
+ leftOrRightSet[i] = true;
+ }
+ }
+ }
+ else {
+ for ( i=0; i < bhArr.length; i++) {
+ // the split criterion, special multiple equals cases added
+ if ( centerValues[centerValuesIndex[i]][splitAxis] <
+ means[splitAxis]) {
+
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4,
+ "Found a left element\n");
+ leftSetCount++;
+ leftOrRightSet[i] = true;
+ } else if ( centerValues[centerValuesIndex[i]][splitAxis] >
+ means[splitAxis]) {
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4,
+ "Found a right element\n");
+ rightSetCount++;
+ leftOrRightSet[i] = false;
+ } else {
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4,
+ "Found an equal element\n");
+ if(leftSetCount > rightSetCount) {
+ rightSetCount++;
+ leftOrRightSet[i] = false;
+ } else {
+ leftSetCount++;
+ leftOrRightSet[i] = true;
+ }
+ }
+ }
+ }
+
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_2,
+ "LeftSetCount " + leftSetCount + " RightSetCount "+
+ rightSetCount + "\n");
+
+
+ // Don't think that this guard is needed, but still like to have it.
+ // Just in case, bad means and the sum of squares might lead us into the guard.
+ if (leftSetCount == bhArr.length) {
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_1,
+ "Split Axis of = " + splitAxis + " didn't yield "+
+ "any split among the objects ?\n");
+ // split elements into half
+ rightSetCount = 0;
+ leftSetCount = 0;
+ for ( i=0; i < bhArr.length; i++) {
+ if(leftSetCount > rightSetCount) {
+ rightSetCount++;
+ leftOrRightSet[i] = false;
+ } else {
+ leftSetCount++;
+ leftOrRightSet[i] = true;
+ }
+ }
+ } else if (rightSetCount == bhArr.length) {
+ if(J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_1,
+ "Split Axis of = " + splitAxis + " didn't yield "+
+ "any split among the objects ?\n");
+ // split elements into half
+ rightSetCount = 0;
+ leftSetCount = 0;
+ for ( i=0; i < bhArr.length; i++) {
+ if(leftSetCount > rightSetCount) {
+ rightSetCount++;
+ leftOrRightSet[i] = false;
+ } else {
+ leftSetCount++;
+ leftOrRightSet[i] = true;
+ }
+ }
+ }
+
+ if(J3dDebug.devPhase)
+ if(J3dDebug.doDebug(J3dDebug.bHTree, J3dDebug.LEVEL_4))
+ // check to make sure that rightSet and leftSet sum to the
+ // number of elements in the original array.
+ if ( bhArr.length != (rightSetCount + leftSetCount) ) {
+ System.out.println("An error has occurred in spliting");
+ }
+
+ BHNode rightSet[] = new BHNode[rightSetCount];
+ BHNode leftSet[] = new BHNode[leftSetCount];
+ int centerValuesIndexR[] = new int[rightSetCount];
+ int centerValuesIndexL[] = new int[leftSetCount];
+
+ rightSetCount = 0;
+ leftSetCount = 0;
+
+ for (i=0; i < bhArr.length; i++) {
+ if ( leftOrRightSet[i] ) { // element in left set
+ leftSet[leftSetCount] = bhArr[i];
+ centerValuesIndexL[leftSetCount] = centerValuesIndex[i];
+ leftSetCount++;
+ } else {
+ rightSet[rightSetCount] = bhArr[i];
+ centerValuesIndexR[rightSetCount] = centerValuesIndex[i];
+ rightSetCount++;
+ }
+ }
+
+ if (rightSet.length != 1) {
+ parent.rChild = VirtualUniverse.mc.getBHNode(BHNode.BH_TYPE_INTERNAL);
+ parent.rChild.setParent(parent);
+ constructTree((BHInternalNode)(parent.rChild), rightSet, centerValues,
+ centerValuesIndexR);
+ } else {
+ parent.rChild = rightSet[0];
+ parent.rChild.setParent(parent);
+ }
+
+ if (leftSet.length != 1) {
+ parent.lChild = VirtualUniverse.mc.getBHNode(BHNode.BH_TYPE_INTERNAL);
+ parent.lChild.setParent(parent);
+ constructTree((BHInternalNode)(parent.lChild), leftSet, centerValues,
+ centerValuesIndexL);
+ } else {
+ parent.lChild = leftSet[0];
+ parent.lChild.setParent(parent);
+ }
+
+ parent.combineBHull(parent.rChild, parent.lChild);
+ }
+
+
+ void reConstructTree(int numOfLeaf) {
+ if(root == null)
+ return;
+
+ BHNode bhArr[] = new BHNode[numOfLeaf];
+ int index[] = new int[1];
+ index[0] = 0;
+ root.destroyTree(bhArr, index);
+
+ /*
+ if(bhArr.length != index[0])
+ System.out.println("BHTree - This isn't right!!! - bhArr.length " +
+ bhArr.length + " index " + index[0]);
+ */
+
+ create(bhArr);
+
+ }
+
+ void gatherTreeStatistics() {
+
+ int leafCount = root.countNumberOfLeaves();
+ int internalCount = root.countNumberOfInternals();
+ int maxDepth = root.computeMaxDepth(0);
+ float averageDepth = root.computeAverageLeafDepth ( leafCount, 0);
+
+
+ System.out.println("Statistics for tree = " + this);
+ System.out.println("Total Number of nodes in tree = " +
+ (leafCount + internalCount) );
+ System.out.println("Number of Leaf Nodes = " + leafCount );
+ System.out.println("Number of Internal Nodes = " + internalCount );
+ System.out.println("Maximum Leaf depth = " + maxDepth );
+ System.out.println("Average Leaf depth = " + averageDepth );
+ System.out.println("root.bHull = " + root.bHull);
+ // printTree(root);
+
+ }
+
+
+ void printTree(BHNode bh) {
+ if(bh!= null) {
+ if(bh.nodeType == BHNode.BH_TYPE_INTERNAL) {
+ System.out.println("BH_TYPE_INTERNAL - bHull : " + bh);
+ System.out.println(bh.bHull);
+ System.out.println("rChild : " + ((BHInternalNode)bh).rChild +
+ " lChild : " + ((BHInternalNode)bh).lChild);
+ printTree(((BHInternalNode)bh).rChild);
+ printTree(((BHInternalNode)bh).lChild);
+ }
+ else if(bh.nodeType == BHNode.BH_TYPE_LEAF) {
+ System.out.println("BH_TYPE_LEAF - bHull : " + bh);
+ System.out.println(bh.bHull);
+ }
+
+ }
+
+
+ }
+}
+
+
+
+
+
+
diff --git a/src/classes/share/javax/media/j3d/Background.java b/src/classes/share/javax/media/j3d/Background.java
new file mode 100644
index 0000000..f22f100
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Background.java
@@ -0,0 +1,704 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The Background leaf node defines a solid background color
+ * and a background image that are used to fill the window at the
+ * beginning of each new frame. The background image may be null.
+ * It optionally allows background
+ * geometry---which is pre-tessellated onto a unit sphere and is drawn
+ * at infinity---to be referenced. It also specifies an application
+ * region in which this background is active. A Background node is
+ * active when its application region intersects the ViewPlatform's
+ * activation volume. If multiple Background nodes are active, the
+ * Background node that is "closest" to the eye will be used. If no
+ * Background nodes are active, then the window is cleared to black.
+ *
+ * <p>
+ * The set of nodes that can be added to a BranchGroup associated with
+ * a Background node is limited. All Group nodes except
+ * ViewSpecificGroup are legal in a background geometry branch
+ * graph. The only Leaf nodes that are legal are Shape3D (except
+ * OrientedShape3D), Morph, Light, and Fog. The presence of any other
+ * Leaf node, including OrientedShape3D, or of a ViewSpecificGroup
+ * node will cause an IllegalSceneGraphException to be thrown. Note
+ * that Link nodes are not allowed; a background geometry branch graph
+ * must not reference shared subgraphs. NodeComponent objects can be
+ * shared between background branches and ordinary (non-background)
+ * branches or among different background branches, however.
+ *
+ * <p>
+ * Light and Fog nodes in a background geometry branch graph do not
+ * affect nodes outside of the background geometry branch graph, and
+ * vice versa. Light and Fog nodes that appear in a background
+ * geometry branch graph must not be hierarchically scoped to any
+ * group node outside of that background geometry branch graph.
+ * Conversely, Light and Fog nodes that appear outside of a particular
+ * background geometry branch graph must not be hierarchically scoped
+ * to any group node in that background geometry branch graph. Any
+ * attempt to do so will be ignored.
+ *
+ * <p>
+ * The influencing bounds of any Light or Fog node in a background
+ * geometry branch graph is effectively infinite (meaning that all
+ * lights can affect all geometry objects nodes within the background
+ * geometry graph, and that an arbitrary fog is selected). An
+ * application wishing to limit the scope of a Light or Fog node must
+ * use hierarchical scoping.
+ *
+ * <p>
+ * Picking and collision is ignored for nodes inside a background
+ * geometry branch graph.
+ */
+public class Background extends Leaf {
+ /**
+ * Specifies that the Background allows read access to its application
+ * bounds and bounding leaf at runtime.
+ */
+ public static final int
+ ALLOW_APPLICATION_BOUNDS_READ = CapabilityBits.BACKGROUND_ALLOW_APPLICATION_BOUNDS_READ;
+
+ /**
+ * Specifies that the Background allows write access to its application
+ * bounds and bounding leaf at runtime.
+ */
+ public static final int
+ ALLOW_APPLICATION_BOUNDS_WRITE = CapabilityBits.BACKGROUND_ALLOW_APPLICATION_BOUNDS_WRITE;
+
+ /**
+ * Specifies that the Background allows read access to its image
+ * at runtime.
+ */
+ public static final int
+ ALLOW_IMAGE_READ = CapabilityBits.BACKGROUND_ALLOW_IMAGE_READ;
+
+ /**
+ * Specifies that the Background allows write access to its image
+ * at runtime.
+ */
+ public static final int
+ ALLOW_IMAGE_WRITE = CapabilityBits.BACKGROUND_ALLOW_IMAGE_WRITE;
+
+ /**
+ * Specifies that the Background allows read access to its color
+ * at runtime.
+ */
+ public static final int
+ ALLOW_COLOR_READ = CapabilityBits.BACKGROUND_ALLOW_COLOR_READ;
+
+ /**
+ * Specifies that the Background allows write access to its color
+ * at runtime.
+ */
+ public static final int
+ ALLOW_COLOR_WRITE = CapabilityBits.BACKGROUND_ALLOW_COLOR_WRITE;
+
+ /**
+ * Specifies that the Background allows read access to its
+ * background geometry at runtime.
+ */
+ public static final int
+ ALLOW_GEOMETRY_READ = CapabilityBits.BACKGROUND_ALLOW_GEOMETRY_READ;
+
+ /**
+ * Specifies that the Background allows write access to its
+ * background geometry at runtime.
+ */
+ public static final int
+ ALLOW_GEOMETRY_WRITE = CapabilityBits.BACKGROUND_ALLOW_GEOMETRY_WRITE;
+
+ /**
+ * Specifies that the Background allows read access to its image
+ * scale mode at runtime.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_IMAGE_SCALE_MODE_READ =
+ CapabilityBits.BACKGROUND_ALLOW_IMAGE_SCALE_MODE_READ;
+
+ /**
+ * Specifies that the Background allows write access to its image
+ * scale mode at runtime.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_IMAGE_SCALE_MODE_WRITE =
+ CapabilityBits.BACKGROUND_ALLOW_IMAGE_SCALE_MODE_WRITE;
+
+
+ /**
+ * Indicates that no scaling of the background image is done. The
+ * image will be drawn in its actual size. If the window is
+ * smaller than the image, the image will be clipped. If the
+ * window is larger than the image, the portion of the window not
+ * filled by the image will be filled with the background color.
+ * In all cases, the upper left corner of the image is anchored at
+ * the upper-left corner of the window.
+ * This is the default mode.
+ *
+ * @see #setImageScaleMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int SCALE_NONE = 0;
+
+ /**
+ * Indicates that the background image is uniformly scaled to fit
+ * the window such that the entire image is visible. The image is
+ * scaled by the smaller of <code>window.width/image.width</code>
+ * and <code>window.height/image.height</code>. The image will
+ * exactly fill either the width or height of the window, but not
+ * necessarily both. The portion of the window not filled by the
+ * image will be filled with the background color.
+ * The upper left corner of the image is anchored at the
+ * upper-left corner of the window.
+ *
+ * @see #setImageScaleMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int SCALE_FIT_MIN = 1;
+
+ /**
+ * Indicates that the background image is uniformly scaled to fit
+ * the window such that the entire window is filled. The image is
+ * scaled by the larger of <code>window.width/image.width</code>
+ * and <code>window.height/image.height</code>. The image will
+ * entirely fill the window, but may by clipped either in <i>X</i>
+ * or <i>Y</i>.
+ * The upper left corner of the image is anchored at the
+ * upper-left corner of the window.
+ *
+ * @see #setImageScaleMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int SCALE_FIT_MAX = 2;
+
+
+ /**
+ * Indicates that the background image is scaled to fit the
+ * window. The image is scaled non-uniformly in <i>x</i> and
+ * <i>y</i> by <code>window.width/image.width</code> and and
+ * <code>window.height/image.height</code>, respectively. The
+ * image will entirely fill the window.
+ *
+ * @see #setImageScaleMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int SCALE_FIT_ALL = 3;
+
+ /**
+ * Indicates that the background image is tiled to fill the entire
+ * window. The image is not scaled.
+ * The upper left corner of the image is anchored at the
+ * upper-left corner of the window.
+ *
+ * @see #setImageScaleMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int SCALE_REPEAT = 4;
+
+ /**
+ * Indicates that the background image is centered in the window
+ * and that no scaling of the image is done. The image will be
+ * drawn in its actual size. If the window is smaller than the
+ * image, the image will be clipped. If the window is larger than
+ * the image, the portion of the window not filled by the image
+ * will be filled with the background color.
+ *
+ * @see #setImageScaleMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int SCALE_NONE_CENTER = 5;
+
+ /**
+ * Constructs a Background node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * color : black (0,0,0)<br>
+ * image : null<br>
+ * geometry : null<br>
+ * image scale mode : SCALE_NONE<br>
+ * application bounds : null<br>
+ * application bounding leaf : null<br>
+ * </ul>
+ */
+ public Background () {
+ // Just use the defaults
+ }
+
+ /**
+ * Constructs a Background node with the specified color.
+ * This color is used to fill the window prior to drawing any
+ * objects in the scene.
+ */
+ public Background(Color3f color) {
+ ((BackgroundRetained)this.retained).setColor(color);
+ }
+
+ /**
+ * Constructs a Background node with the specified color.
+ * This color is used to fill the window prior to drawing any
+ * objects in the scene.
+ */
+ public Background(float r, float g, float b) {
+ ((BackgroundRetained)this.retained).setColor(r, g, b);
+ }
+
+ /**
+ * Constructs a Background node with the specified image. If this
+ * image is non-null, it is rendered to the window prior to
+ * drawing any objects in the scene. If the image is smaller
+ * than the window,
+ * then that portion of the window not covered by the image is
+ * filled with the background color.
+ *
+ * @param image pixel array object used as the background image
+ */
+ public Background(ImageComponent2D image) {
+ ((BackgroundRetained)this.retained).setImage(image);
+ }
+
+ /**
+ * Constructs a Background node with the specified geometry.
+ * If non-null, this background geometry is drawn on top of
+ * the background color and image using a projection
+ * matrix that essentially puts the geometry at infinity. The geometry
+ * should be pre-tessellated onto a unit sphere.
+ * @param branch the root of the background geometry
+ * @exception IllegalSharingException if the BranchGroup node
+ * is a child of any Group node, or is already attached to a Locale,
+ * or is already referenced by another Background node.
+ * @exception IllegalSceneGraphException if specified branch graph
+ * contains an illegal node.
+ */
+ public Background(BranchGroup branch) {
+ ((BackgroundRetained)this.retained).setGeometry(branch);
+ }
+
+ /**
+ * Sets the background color to the specified color.
+ * This color is used to fill the window prior to drawing any
+ * objects in the scene.
+ * @param color the new background color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background0"));
+
+ if (isLive())
+ ((BackgroundRetained)this.retained).setColor(color);
+ else
+ ((BackgroundRetained)this.retained).initColor(color);
+
+ }
+
+ /**
+ * Sets the background color to the specified color.
+ * This color is used to fill the window prior to drawing any
+ * objects in the scene.
+ * @param r the red component of the background color
+ * @param g the green component of the background color
+ * @param b the blue component of the background color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setColor(float r, float g, float b) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background0"));
+
+ if (isLive())
+ ((BackgroundRetained)this.retained).setColor(r, g, b);
+ else
+ ((BackgroundRetained)this.retained).initColor(r, g, b);
+ }
+
+ /**
+ * Retrieves the background color.
+ * @param color the vector that will receive the current background color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background2"));
+
+ ((BackgroundRetained)this.retained).getColor(color);
+ }
+
+ /**
+ * Sets the background image to the specified image. If this
+ * image is non-null, it is rendered to the window prior to
+ * drawing any objects in the scene. If the image is smaller
+ * than the window,
+ * then that portion of the window not covered by the image is
+ * filled with the background color.
+ * @param image new pixel array object used as the background image
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setImage(ImageComponent2D image) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background3"));
+
+ if (isLive())
+ ((BackgroundRetained)this.retained).setImage(image);
+ else
+ ((BackgroundRetained)this.retained).initImage(image);
+ }
+
+ /**
+ * Retrieves the background image.
+ * @return the current background image
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public ImageComponent2D getImage() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background4"));
+
+ return ((BackgroundRetained)this.retained).getImage();
+ }
+
+ /**
+ * Sets the image scale mode for this Background node.
+ *
+ * @param imageScaleMode the new image scale mode, one of:
+ * SCALE_NONE, SCALE_FIT_MIN, SCALE_FIT_MAX, SCALE_FIT_ALL,
+ * SCALE_REPEAT, or SCALE_NONE_CENTER.
+ *
+ * @exception IllegalArgumentException if <code>imageScaleMode</code>
+ * is a value other than SCALE_NONE, SCALE_FIT_MIN, SCALE_FIT_MAX,
+ * SCALE_FIT_ALL, SCALE_REPEAT, or SCALE_NONE_CENTER.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setImageScaleMode(int imageScaleMode) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IMAGE_SCALE_MODE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background9"));
+
+ switch (imageScaleMode) {
+ case SCALE_NONE:
+ case SCALE_FIT_MIN:
+ case SCALE_FIT_MAX:
+ case SCALE_FIT_ALL:
+ case SCALE_REPEAT:
+ case SCALE_NONE_CENTER:
+ break;
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("Background11"));
+ }
+
+ if (isLive())
+ ((BackgroundRetained)this.retained).setImageScaleMode(imageScaleMode);
+ else
+ ((BackgroundRetained)this.retained).initImageScaleMode(imageScaleMode);
+
+ }
+
+ /**
+ * Retrieves the current image scale mode.
+ * @return the current image scale mode, one of:
+ * SCALE_NONE, SCALE_FIT_MIN, SCALE_FIT_MAX, SCALE_FIT_ALL,
+ * SCALE_REPEAT, or SCALE_NONE_CENTER.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getImageScaleMode() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IMAGE_SCALE_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background10"));
+ return ((BackgroundRetained)this.retained).getImageScaleMode();
+ }
+
+ /**
+ * Sets the background geometry to the specified BranchGroup node.
+ * If non-null, this background geometry is drawn on top of
+ * the background color and image using a projection
+ * matrix that essentially puts the geometry at infinity. The geometry
+ * should be pre-tessellated onto a unit sphere.
+ * @param branch the root of the background geometry
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalSharingException if the BranchGroup node
+ * is a child of any Group node, or is already attached to a Locale,
+ * or is already referenced by another Background node.
+ * @exception IllegalSceneGraphException if specified branch graph
+ * contains an illegal node.
+ */
+ public void setGeometry(BranchGroup branch) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background5"));
+
+ if (isLive())
+ ((BackgroundRetained)this.retained).setGeometry(branch);
+ else
+ ((BackgroundRetained)this.retained).initGeometry(branch);
+ }
+
+ /**
+ * Retrieves the background geometry.
+ * @return the BranchGroup node that is the root of the background
+ * geometry
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BranchGroup getGeometry() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background6"));
+
+ return ((BackgroundRetained)this.retained).getGeometry();
+ }
+
+ /**
+ * Set the Background's application region to the specified bounds.
+ * This is used when the application bounding leaf is set to null.
+ * @param region the bounds that contains the Background's new application
+ * region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setApplicationBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background7"));
+
+ if (isLive())
+ ((BackgroundRetained)this.retained).setApplicationBounds(region);
+ else
+ ((BackgroundRetained)this.retained).initApplicationBounds(region);
+ }
+
+ /**
+ * Retrieves the Background node's application bounds.
+ * @return this Background's application bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getApplicationBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background8"));
+
+ return ((BackgroundRetained)this.retained).getApplicationBounds();
+ }
+
+ /**
+ * Set the Background's application region to the specified bounding leaf.
+ * When set to a value other than null, this overrides the application
+ * bounds object.
+ * @param region the bounding leaf node used to specify the Background
+ * node's new application region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setApplicationBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background7"));
+
+ if (isLive())
+ ((BackgroundRetained)this.retained).setApplicationBoundingLeaf(region);
+ else
+ ((BackgroundRetained)this.retained).initApplicationBoundingLeaf(region);
+ }
+
+ /**
+ * Retrieves the Background node's application bounding leaf.
+ * @return this Background's application bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getApplicationBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Background8"));
+
+ return ((BackgroundRetained)this.retained).getApplicationBoundingLeaf();
+ }
+
+ /**
+ * Creates the retained mode BackgroundRetained object that this
+ * Background component object will point to.
+ */
+ void createRetained() {
+ this.retained = new BackgroundRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Creates 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.<br>
+ * Background geometry will not clone in this operation.
+ * It is the user's responsibility
+ * to call <code>cloneTree</code> on that branchGroup.
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see Node#duplicateNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ Background b = new Background();
+ 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ *
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>Background</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean
+ forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+
+
+ /**
+ * Copies all Background 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ BackgroundRetained attr = (BackgroundRetained) originalNode.retained;
+ BackgroundRetained rt = (BackgroundRetained) retained;
+
+ Color3f c = new Color3f();
+ attr.getColor(c);
+ rt.initColor(c);
+ rt.initApplicationBounds(attr.getApplicationBounds());
+ rt.initGeometry(attr.getGeometry());
+ rt.initImage((ImageComponent2D) getNodeComponent(
+ attr.getImage(),
+ forceDuplicate,
+ originalNode.nodeHashtable));
+
+ // this will be updated in updateNodeReferences
+ rt.initApplicationBoundingLeaf(attr.getApplicationBoundingLeaf());
+ }
+
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances
+ *
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ BackgroundRetained rt = (BackgroundRetained) retained;
+ BoundingLeaf bl= rt.getApplicationBoundingLeaf();
+
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.initApplicationBoundingLeaf((BoundingLeaf) o);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BackgroundRetained.java b/src/classes/share/javax/media/j3d/BackgroundRetained.java
new file mode 100644
index 0000000..38edb3f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BackgroundRetained.java
@@ -0,0 +1,817 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+
+
+/**
+ * The Background leaf node defines either a solid background color
+ * or a background image that is used to fill the window at the
+ * beginning of each new frame. It also specifies an application
+ * region in which this background is active.
+ */
+class BackgroundRetained extends LeafRetained {
+
+ static final int COLOR_CHANGED = 0x00001;
+ static final int IMAGE_CHANGED = 0x00002;
+ static final int GEOMETRY_CHANGED = 0x00004;
+ static final int BOUNDS_CHANGED = 0x00008;
+ static final int BOUNDINGLEAF_CHANGED = 0x00010;
+ static final int IMAGE_SCALE_CHANGED = 0x00020;
+ // Background color or image. If non-null, the image overrides the
+ // color.
+ Color3f color = new Color3f(0.0f, 0.0f, 0.0f);
+ ImageComponent2DRetained image = null;
+
+ // the image scale mode if image is used.
+ int imageScaleMode = Background.SCALE_NONE;
+
+ /**
+ * The Boundary object defining the lights's application region.
+ */
+ Bounds applicationRegion = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * Background geometry branch group
+ */
+ BranchGroup geometryBranch = null;
+
+ /**
+ * The transformed value of the applicationRegion.
+ */
+ Bounds transformedRegion = null;
+
+ /**
+ * The state structure used for Background Geometry
+ */
+ SetLiveState setLiveState = null;
+
+ /**
+ * The locale of this Background node since we don't have mirror object
+ * when clearLive is called
+ * locale is set to null, we still want locale to have a
+ * non-null value, since renderingEnv structure may be using the
+ * locale
+ */
+ Locale cachedLocale = null;
+
+ // This is true when this background is referenced in an immediate mode context
+ boolean inImmCtx = false;
+
+ // list of light nodes for background geometry
+ ArrayList lights = new ArrayList();
+
+ // list of fog nodes for background geometry
+ ArrayList fogs = new ArrayList();
+
+ // a list of background geometry atoms
+ ArrayList bgGeometryAtomList = new ArrayList();
+
+ // false is background geometry atoms list has changed
+ boolean bgGeometryAtomListDirty = true;
+
+ // an array of background geometry atoms
+ GeometryAtom[] bgGeometryAtoms = null;
+
+ // Target threads to be notified when light changes
+ // Note, the rendering env structure only get notified
+ // when there is a bounds related change
+ final static int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+ // Is true, if the background is viewScoped
+ boolean isViewScoped = false;
+
+ // for texture mapping the background
+ ImageComponent2DRetained texImage = null;
+ int xmax = 0;
+ int ymax = 0;
+
+ BackgroundRetained () {
+ this.nodeType = NodeRetained.BACKGROUND;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+
+ /**
+ * Initializes the background color to the specified color.
+ * This color is used
+ * if the image is null.
+ * @param color the new background color
+ */
+ final void initColor(Color3f color) {
+ this.color.set(color);
+ }
+
+
+ /**
+ * Sets the background color to the specified color. This color is used
+ * if the image is null.
+ * @param color the new background color
+ */
+ final void setColor(Color3f color) {
+ initColor(color);
+ if (source.isLive()) {
+ sendMessage(COLOR_CHANGED, new Color3f(color));
+ }
+ }
+
+ /**
+ * Initializes the background color to the specified color.
+ * This color is used
+ * if the image is null.
+ * @param r the red component of the background color
+ * @param g the green component of the background color
+ * @param b the blue component of the background color
+ */
+ final void initColor(float r, float g, float b) {
+ this.color.x = r;
+ this.color.y = g;
+ this.color.z = b;
+ }
+
+
+
+ /**
+ * Sets the background color to the specified color. This color is used
+ * if the image is null.
+ * @param r the red component of the background color
+ * @param g the green component of the background color
+ * @param b the blue component of the background color
+ */
+ final void setColor(float r, float g, float b) {
+ setColor(new Color3f(r, g, b));
+ }
+
+
+ /**
+ * Retrieves the background color.
+ * @param color the vector that will receive the current background color
+ */
+ final void getColor(Color3f color) {
+ color.set(this.color);
+ }
+
+ /**
+ * Initialize the image scale mode to the specified mode
+ * @imageScaleMode the image scale mode to the used
+ */
+ final void initImageScaleMode(int imageScaleMode){
+ this.imageScaleMode = imageScaleMode;
+ }
+
+ /**
+ * Sets the image scale mode for this Background node.
+ * @param imageScaleMode the image scale mode
+ */
+ final void setImageScaleMode(int imageScaleMode){
+ initImageScaleMode(imageScaleMode);
+ if(source.isLive()){
+ sendMessage(IMAGE_SCALE_CHANGED, new Integer(imageScaleMode));
+ }
+ }
+
+ /**
+ * gets the image scale mode for this Background node.
+ */
+ final int getImageScaleMode(){
+ return imageScaleMode;
+ }
+
+ /**
+ * Initializes the background image to the specified image.
+ * @param image new ImageCompoent3D object used as the background image
+ */
+ final void initImage(ImageComponent2D img) {
+ if (img != null) {
+ // scale to power of 2 for texture mapping
+ ImageComponent2DRetained rimage = (ImageComponent2DRetained) img.retained;
+
+ if (!VirtualUniverse.mc.isBackgroundTexture) {
+ rimage.setRasterRef();
+ }
+ else {
+// rimage.setTextureRef();
+
+ xmax = rimage.width;
+ ymax = rimage.height;
+ int width = getClosestPowerOf2(xmax);
+ int height = getClosestPowerOf2(ymax);
+ float xScale = (float)width/(float)xmax;
+ float yScale = (float)height/(float)ymax;
+
+ // scale if scales aren't 1.0
+ if (!(xScale == 1.0f && yScale == 1.0f)) {
+ BufferedImage origImg = (BufferedImage) rimage.getImage();
+ AffineTransform at = AffineTransform.getScaleInstance(xScale,
+ yScale);
+ AffineTransformOp atop = new AffineTransformOp(at,
+ AffineTransformOp.TYPE_BILINEAR);
+ BufferedImage scaledImg = atop.filter(origImg, null);
+ int format = rimage.getFormat();
+ boolean yUp = rimage.isYUp();
+ boolean byRef = rimage.isByReference();
+ ImageComponent2D ic = new ImageComponent2D(format,
+ scaledImg,
+ byRef, yUp);
+ texImage = (ImageComponent2DRetained)ic.retained;
+ texImage.setTextureRef();
+ //rimage.setTextureRef();
+ //texImage.setRasterRef();
+ }
+ else {
+ texImage = rimage;
+ texImage.setTextureRef();
+ //rimage.setTextureRef();
+ //texImage.setRasterRef();
+ }
+ }
+
+ this.image = rimage;
+ } else {
+ this.image = null;
+ this.texImage = null;
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+
+
+
+ /**
+ * Sets the background image to the specified image.
+ * @param image new ImageCompoent3D object used as the background image
+ */
+ final void setImage(ImageComponent2D img) {
+ if (source.isLive()) {
+ if (this.image != null) {
+ this.image.clearLive(refCount);
+ }
+ }
+ initImage(img);
+ if (source.isLive()) {
+ if (img != null) {
+ ((ImageComponent2DRetained)
+ img.retained).setLive(inBackgroundGroup, refCount);
+ }
+ sendMessage(IMAGE_CHANGED,
+ (image != null ? image.clone() : null));
+ }
+ }
+
+ /**
+ * Retrieves the background image.
+ * @return the current background image
+ */
+ final ImageComponent2D getImage() {
+ return (image == null ? null :
+ (ImageComponent2D)image.source);
+ }
+
+ /**
+ * Initializes the background geometry branch group to the specified branch.
+ * @param branch new branch group object used for background geometry
+ */
+ final void initGeometry(BranchGroup branch) {
+ geometryBranch = branch;
+ }
+
+
+ /**
+ * Sets the background geometry branch group to the specified branch.
+ * @param branch new branch group object used for background geometry
+ */
+ final void setGeometry(BranchGroup branch) {
+ int numMessages = 0;
+ int i;
+
+ if (source.isLive()) {
+ J3dMessage m[];
+ if (geometryBranch != null)
+ numMessages+=2; // REMOVE_NODES, ORDERED_GROUP_REMOVED
+ if (branch != null)
+ numMessages+=2; // INSERT_NODES, ORDERED_GROUP_INSERTED
+ m = new J3dMessage[numMessages];
+ for (i=0; i<numMessages; i++) {
+ m[i] = VirtualUniverse.mc.getMessage();
+ }
+ i = 0;
+ if (geometryBranch != null) {
+ clearGeometryBranch((BranchGroupRetained)geometryBranch.retained);
+ m[i].threads = (J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT);
+ m[i].type = J3dMessage.ORDERED_GROUP_REMOVED;
+ m[i].universe = universe;
+ m[i].args[0] = setLiveState.ogList.toArray();
+ m[i].args[1] = setLiveState.ogChildIdList.toArray();
+ m[i].args[3] = setLiveState.ogCIOList.toArray();
+ m[i].args[4] = setLiveState.ogCIOTableList.toArray();
+ i++;
+
+ m[i].threads = setLiveState.notifyThreads;
+ m[i].type = J3dMessage.REMOVE_NODES;
+ m[i].universe = universe;
+ m[i].args[0] = setLiveState.nodeList.toArray();
+ i++;
+
+ }
+ if (branch != null) {
+ setGeometryBranch((BranchGroupRetained)branch.retained);
+ m[i].threads = (J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT);
+ m[i].type = J3dMessage.ORDERED_GROUP_INSERTED;
+ m[i].universe = universe;
+ m[i].args[0] = setLiveState.ogList.toArray();
+ m[i].args[1] = setLiveState.ogChildIdList.toArray();
+ m[i].args[2] = setLiveState.ogOrderedIdList.toArray();
+ m[i].args[3] = setLiveState.ogCIOList.toArray();
+ m[i].args[4] = setLiveState.ogCIOTableList.toArray();
+ i++;
+
+ m[i].threads = setLiveState.notifyThreads;
+ m[i].type = J3dMessage.INSERT_NODES;
+ m[i].universe = universe;
+ m[i].args[0] = setLiveState.nodeList.toArray();
+ }
+ VirtualUniverse.mc.processMessage(m);
+ // Free up memory
+ setLiveState.reset(null);
+ }
+ initGeometry(branch);
+ }
+
+ /**
+ * Retrieves the background geometry branch group.
+ * @return the current background geometry branch group
+ */
+ final BranchGroup getGeometry() {
+ return geometryBranch;
+ }
+
+ /**
+ * Initializes the Background's application region.
+ * @param region a region that contains the Backgound's new application bounds
+ */
+ final void initApplicationBounds(Bounds region) {
+ if (region != null) {
+ applicationRegion = (Bounds) region.clone();
+ } else {
+ applicationRegion = null;
+ }
+ }
+
+ /**
+ * Set the Background's application region.
+ * @param region a region that contains the Backgound's new application bounds
+ */
+ final void setApplicationBounds(Bounds region) {
+ initApplicationBounds(region);
+ // Don't send the message if there is a valid boundingleaf
+ if (boundingLeaf == null) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.type = J3dMessage.BACKGROUND_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(BOUNDS_CHANGED);
+ if (region != null)
+ createMessage.args[2] = region.clone();
+ else
+ createMessage.args[2] = null;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ /**
+ * Get the Backgound's application region.
+ * @return this Backgound's application region information
+ */
+ final Bounds getApplicationBounds() {
+ return (applicationRegion != null ? (Bounds) applicationRegion.clone() : null);
+ }
+
+ /**
+ * Initializes the Background's application region
+ * to the specified Leaf node.
+ */
+ void initApplicationBoundingLeaf(BoundingLeaf region) {
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ } else {
+ boundingLeaf = null;
+ }
+ }
+
+ /**
+ * Set the Background's application region to the specified Leaf node.
+ */
+ void setApplicationBoundingLeaf(BoundingLeaf region) {
+ if (boundingLeaf != null)
+ boundingLeaf.mirrorBoundingLeaf.removeUser(this);
+
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ boundingLeaf.mirrorBoundingLeaf.addUser(this);
+ } else {
+ boundingLeaf = null;
+ }
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.type = J3dMessage.BACKGROUND_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(BOUNDINGLEAF_CHANGED);
+ if (boundingLeaf != null) {
+ createMessage.args[2] = boundingLeaf.mirrorBoundingLeaf;
+ createMessage.args[3] = null;
+ } else {
+ createMessage.args[2] = null;
+ if (applicationRegion != null)
+ createMessage.args[3] = applicationRegion.clone();
+ else
+ createMessage.args[3] = null;
+ }
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+
+ /**
+ * Get the Background's application region
+ */
+ BoundingLeaf getApplicationBoundingLeaf() {
+ return (boundingLeaf != null ?
+ (BoundingLeaf)boundingLeaf.source : null);
+ }
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+ void setGeometryBranch(BranchGroupRetained branch) {
+ setLiveState.reset(locale);
+ setLiveState.inBackgroundGroup = true;
+
+ setLiveState.geometryBackground = this;
+ setLiveState.currentTransforms[0] = new Transform3D[2];
+ setLiveState.currentTransforms[0][0] = new Transform3D();
+ setLiveState.currentTransforms[0][1] = new Transform3D();
+ setLiveState.currentTransformsIndex[0] = new int[2];
+ setLiveState.currentTransformsIndex[0][0] = 0;
+ setLiveState.currentTransformsIndex[0][1] = 0;
+
+ setLiveState.localToVworld = setLiveState.currentTransforms;
+ setLiveState.localToVworldIndex = setLiveState.currentTransformsIndex;
+
+ setLiveState.branchGroupPaths = new ArrayList();
+ setLiveState.branchGroupPaths.add(new BranchGroupRetained[0]);
+
+ setLiveState.orderedPaths = new ArrayList(1);
+ setLiveState.orderedPaths.add(new OrderedPath());
+
+ setLiveState.switchStates = new ArrayList(1);
+ setLiveState.switchStates.add(new SwitchState(false));
+
+
+ branch.setLive(setLiveState);
+
+
+ }
+
+ void clearGeometryBranch(BranchGroupRetained branch) {
+ setLiveState.reset(locale);
+ setLiveState.inBackgroundGroup = true;
+ setLiveState.geometryBackground = this;
+ branch.clearLive(setLiveState);
+ branch.setParent(null);
+ branch.setLocale(null);
+
+ }
+
+
+ /**
+ * This setLive routine first calls the superclass's method, then
+ * it adds itself to the list of lights
+ */
+ void setLive(SetLiveState s) {
+ TransformGroupRetained[] tlist;
+ int i;
+
+ super.doSetLive(s);
+
+ if (inImmCtx) {
+ throw new IllegalSharingException(
+ J3dI18N.getString("BackgroundRetained1"));
+ }
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("BackgroundRetained5"));
+ }
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("BackgroundRetained6"));
+ }
+
+
+ if (geometryBranch != null) {
+ BranchGroupRetained branch =
+ (BranchGroupRetained)geometryBranch.retained;
+ if (branch.inBackgroundGroup == true)
+ throw new IllegalSharingException(
+ J3dI18N.getString("BackgroundRetained0"));
+
+ if (branch.parent != null)
+ throw new IllegalSharingException(
+ J3dI18N.getString("BackgroundRetained3"));
+
+ if (branch.locale != null)
+ throw new IllegalSharingException(
+ J3dI18N.getString("BackgroundRetained4"));
+
+ if (setLiveState == null) {
+ setLiveState = new SetLiveState(universe);
+ setLiveState.universe = universe;
+ }
+ setGeometryBranch((BranchGroupRetained)geometryBranch.retained);
+ // add background geometry nodes to setLiveState's nodeList
+ s.nodeList.addAll(setLiveState.nodeList);
+ s.notifyThreads |= setLiveState.notifyThreads;
+ s.ogList.addAll(setLiveState.ogList);
+ s.ogChildIdList.addAll(setLiveState.ogChildIdList);
+ s.ogOrderedIdList.addAll(setLiveState.ogOrderedIdList);
+ // Free up memory.
+ setLiveState.reset(null);
+ }
+
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(this);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(this);
+ }
+ // System.out.println("bkg.setlive nodeList " + s.nodeList);
+
+ // process switch leaf
+ if (s.switchTargets != null && s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.ENV_TARGETS);
+ }
+ switchState = (SwitchState)s.switchStates.get(0);
+
+ // Initialize some mirror values
+ if (boundingLeaf != null) {
+ transformedRegion =
+ (Bounds)boundingLeaf.mirrorBoundingLeaf.transformedRegion;
+ }
+ else { // Evaluate applicationRegion if not null
+ if (applicationRegion != null) {
+ transformedRegion = (Bounds)applicationRegion.clone();
+ transformedRegion.transform(
+ applicationRegion,
+ getLastLocalToVworld());
+ }
+ else {
+ transformedRegion = null;
+ }
+
+ }
+ cachedLocale = s.locale;
+
+ // add this node to the transform target
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+
+ if (image != null) {
+ image.setLive(inBackgroundGroup, refCount);
+ }
+ super.markAsLive();
+
+ }
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it removes itself to the list of lights
+ */
+ void clearLive(SetLiveState s) {
+ super.clearLive(s);
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(this);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(this);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+ if (s.switchTargets != null && s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.ENV_TARGETS);
+ }
+
+ if (geometryBranch != null) {
+ BranchGroupRetained branch =
+ (BranchGroupRetained)geometryBranch.retained;
+ clearGeometryBranch((BranchGroupRetained)geometryBranch.retained);
+ // add background geometry nodes to setLiveState's nodeList
+ s.nodeList.addAll(setLiveState.nodeList);
+ s.ogList.addAll(setLiveState.ogList);
+ s.ogChildIdList.addAll(setLiveState.ogChildIdList);
+ s.notifyThreads |= setLiveState.notifyThreads;
+ // Free up memory.
+ setLiveState.reset(null);
+ lights.clear();
+ fogs.clear();
+ }
+
+ if (image != null) {
+ image.clearLive(refCount);
+ }
+
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+ }
+
+
+
+
+ // The update Object function.
+ synchronized void updateImmediateMirrorObject(Object[] objs) {
+ int component = ((Integer)objs[1]).intValue();
+ Transform3D trans;
+ // If initialization
+
+ // Bounds message only sent when boundingleaf is null
+ if ((component & BOUNDS_CHANGED) != 0) {
+ if (objs[2] != null) {
+ transformedRegion = ((Bounds)((Bounds) objs[2])).copy(transformedRegion);
+ transformedRegion.transform(
+ (Bounds) objs[2], getCurrentLocalToVworld());
+ }
+ else {
+ transformedRegion = null;
+ }
+ }
+ else if ((component & BOUNDINGLEAF_CHANGED) != 0) {
+ if (objs[2] != null) {
+ transformedRegion = ((BoundingLeafRetained)objs[2]).transformedRegion;
+ }
+ else { // Evaluate applicationRegion if not null
+ Bounds appRegion = (Bounds)objs[3];
+ if (appRegion != null) {
+ transformedRegion = appRegion.copy(transformedRegion);
+ transformedRegion.transform(
+ appRegion, getCurrentLocalToVworld());
+ }
+ else {
+ transformedRegion = null;
+ }
+
+ }
+ }
+
+ }
+
+ /** Note: This routine will only be called
+ * to update the object's
+ * transformed region
+ */
+ void updateBoundingLeaf() {
+ if (boundingLeaf != null &&
+ boundingLeaf.mirrorBoundingLeaf.switchState.currentSwitchOn) {
+ transformedRegion =
+ boundingLeaf.mirrorBoundingLeaf.transformedRegion;
+ } else { // Evaluate applicationRegion if not null
+ if (applicationRegion != null) {
+ transformedRegion = applicationRegion.copy(transformedRegion);
+ transformedRegion.transform(
+ applicationRegion, getCurrentLocalToVworld());
+ } else {
+ transformedRegion = null;
+ }
+ }
+ }
+
+ void updateImmediateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (boundingLeaf == null) {
+ if (applicationRegion != null) {
+ transformedRegion = applicationRegion.copy(transformedRegion);
+ transformedRegion.transform(
+ applicationRegion, getCurrentLocalToVworld());
+ }
+ }
+ }
+
+
+ final void sendMessage(int attrMask, Object attr) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.BACKGROUND_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void addBgGeometryAtomList(GeometryAtom geomAtom) {
+ bgGeometryAtomList.add(geomAtom);
+ bgGeometryAtomListDirty = true;
+ }
+
+ void removeBgGeometryAtomList(GeometryAtom geomAtom) {
+ bgGeometryAtomList.remove(bgGeometryAtomList.indexOf(geomAtom));
+ bgGeometryAtomListDirty = true;
+ }
+
+ GeometryAtom[] getBackgroundGeometryAtoms() {
+ if (bgGeometryAtomListDirty) {
+ int nAtoms = bgGeometryAtomList.size();
+ if (nAtoms == 0) {
+ bgGeometryAtoms = null;
+ } else {
+ bgGeometryAtoms = new GeometryAtom[nAtoms];
+ for (int i=0; i<bgGeometryAtoms.length; i++) {
+ bgGeometryAtoms[i] = (GeometryAtom)bgGeometryAtomList.get(i) ;
+ }
+ bgGeometryAtomListDirty = false;
+ }
+ }
+ return(bgGeometryAtoms);
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (applicationRegion != null) {
+ applicationRegion.transform(xform.transform);
+ }
+ }
+
+ // notifies the Background object that the image data in a referenced
+ // ImageComponent object is changed.
+ // Currently we are not making use of this information.
+
+ void notifyImageComponentImageChanged(ImageComponentRetained image,
+ ImageComponentUpdateInfo value) {
+ }
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ leafList.add(this); // No Mirror in this case
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BackgroundSound.java b/src/classes/share/javax/media/j3d/BackgroundSound.java
new file mode 100644
index 0000000..0eff076
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BackgroundSound.java
@@ -0,0 +1,136 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A BackgroundSound node defines an unattenuated, nonspatialized sound
+ * source that has no position or direction. It has the same attributes as a
+ * Sound node. This type of sound is simply added to the sound mix without
+ * modification and is useful for playing a mono or stereo music track, or an
+ * ambient sound effect. Unlike a Background (visual) node, more than one
+ * BackgroundSound node can be simultaneously enabled and active.
+ */
+public class BackgroundSound extends Sound {
+ /**
+ * Constructs a new BackgroundSound node using the default parameters
+ * for Sound nodes.
+ */
+ public BackgroundSound() {
+ /**
+ * Uses default values defined in SoundRetained.java
+ */
+ }
+
+ /**
+ * Constructs a BackgroundSound node object using only the provided
+ * parameter values for sound data and sample gain. The remaining fields
+ * are set to the default values for a Sound node.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source
+ */
+ public BackgroundSound(MediaContainer soundData, float initialGain ) {
+ super(soundData, initialGain);
+ }
+
+ /**
+ * Constructs a BackgroundSound object accepting all the parameters
+ * associated with a Sound node.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source
+ * @param loopCount number of times loop is looped
+ * @param release flag denoting playing sound data to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ */
+ public BackgroundSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority) {
+
+ super(soundData, initialGain, loopCount, release, continuous,
+ enable, region, priority );
+ }
+
+
+ /**
+ * Creates the retained mode BackgroundSoundRetained object that this
+ * BackgroundSound component object will point to.
+ */
+ void createRetained() {
+ this.retained = new BackgroundSoundRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Creates 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) {
+ BackgroundSound b = new BackgroundSound();
+ 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ *
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>Sound</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BackgroundSoundRetained.java b/src/classes/share/javax/media/j3d/BackgroundSoundRetained.java
new file mode 100644
index 0000000..389d9c0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BackgroundSoundRetained.java
@@ -0,0 +1,28 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * BackgroundSound is a class for sounds that are not spatially rendered.
+ * These sounds are simply added to the stereo sound mix without modification.
+ * These could be used to play mono or stereo music, or ambient sound effects.
+ */
+class BackgroundSoundRetained extends SoundRetained {
+
+ BackgroundSoundRetained() {
+ this.nodeType = NodeRetained.BACKGROUNDSOUND;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BadTransformException.java b/src/classes/share/javax/media/j3d/BadTransformException.java
new file mode 100644
index 0000000..bf53ae5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BadTransformException.java
@@ -0,0 +1,55 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates an attempt to use a Tranform3D object that is
+ * inappropriate for the object in which it is being used.
+ * For example:
+ * <ul>
+ * <li>
+ * Transforms that are used in the scene graph, within a TransformGroup
+ * node, must be affine. They may optionally contain a non-uniform
+ * scale and/or a shear, subject to other listed restrictions.
+ * <li>
+ * All transforms in the TransformGroup nodes above a ViewPlatform
+ * object must be congruent. This ensures that the Vworld coordinates to
+ * ViewPlatform coordinates transform is angle and length-preserving with
+ * no shear and only uniform scale.
+ * <li>
+ * Most viewing transforms other than those in the scene graph can
+ * only contain translation and rotation.
+ * <li>
+ * The projection transform is allowed to be non-affine, but it
+ * must either be a single point perspective projection or a parallel
+ * projection.
+ * </ul>
+ */
+public class BadTransformException extends RuntimeException{
+
+/**
+ * Create the exception object with default values.
+ */
+ public BadTransformException(){
+ }
+
+/**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public BadTransformException(String str){
+
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Behavior.java b/src/classes/share/javax/media/j3d/Behavior.java
new file mode 100644
index 0000000..22e2dfa
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Behavior.java
@@ -0,0 +1,500 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+import java.util.Enumeration;
+
+/**
+ * The Behavior leaf node provides a framework for adding user-defined
+ * actions into the scene graph. Behavior is an abstract class that
+ * defines two methods that must be overridden by a subclass: An
+ * <code>initialization</code> method, called once when the behavior
+ * becomes "live," and a <code>processStimulus</code> method called
+ * whenever appropriate by the Java 3D behavior scheduler. The
+ * Behavior node also contains an enable flag, a scheduling region,
+ * a scheduling interval, and a wakeup condition.
+ *
+ * <P>
+ * The <i>scheduling region</i> defines a spatial volume that serves
+ * to enable the scheduling of Behavior nodes. A Behavior node is
+ * <i>active</i> (can receive stimuli) whenever an active ViewPlatform's
+ * activation volume intersects a Behavior object's scheduling
+ * region. Only active behaviors can receive stimuli.
+ *
+ * <P>
+ * The <i>scheduling interval</i> defines a partial order of execution
+ * for behaviors that wake up in response to the same wakeup condition
+ * (that is, those behaviors that are processed at the same "time").
+ * Given a set of behaviors whose wakeup conditions are satisfied at
+ * the same time, the behavior scheduler will execute all behaviors in
+ * a lower scheduling interval before executing any behavior in a
+ * higher scheduling interval. Within a scheduling interval,
+ * behaviors can be executed in any order, or in parallel. Note that
+ * this partial ordering is only guaranteed for those behaviors that
+ * wake up at the same time in response to the same wakeup condition,
+ * for example, the set of behaviors that wake up every frame in
+ * response to a WakeupOnElapsedFrames(0) wakeup condition.
+ *
+ * <P>
+ * The <code>initialize</code> method allows a Behavior object to
+ * initialize its internal state and specify its initial wakeup
+ * condition(s). Java 3D invokes a behavior's initialize code when the
+ * behavior's containing BranchGroup node is added to the virtual
+ * universe. Java 3D does not invoke the initialize method in a new
+ * thread. Thus, for Java 3D to regain control, the initialize method
+ * must not execute an infinite loop; it must return. Furthermore, a
+ * wakeup condition must be set or else the behavior's processStimulus
+ * method is never executed.
+ *
+ * <P>
+ * The <code>processStimulus</code> method receives and processes a
+ * behavior's ongoing messages. The Java 3D behavior scheduler invokes
+ * a Behavior node's processStimulus method when an active ViewPlatform's
+ * activation volume intersects a Behavior object's scheduling region
+ * and all of that behavior's wakeup criteria are satisfied. The
+ * processStimulus method performs its computations and actions
+ * (possibly including the registration of state change information
+ * that could cause Java 3D to wake other Behavior objects),
+ * establishes its next wakeup condition, and finally exits.
+ * A typical behavior will modify one or more nodes or node components
+ * in the scene graph. These modifications can happen in parallel
+ * with rendering. In general, applications cannot count on behavior
+ * execution being synchronized with rendering. There are two
+ * exceptions to this general rule:
+ * <ol>
+ * <li>All modifications to scene graph objects (not including geometry
+ * by-reference or texture by-reference) made from the
+ * <code>processStimulus</code> method of a single behavior instance
+ * are guaranteed to take effect in the same rendering frame.</li>
+ * <li>All modifications to scene graph objects (not including geometry
+ * by-reference or texture by-reference) made from the
+ * <code>processStimulus</code> methods of the set of behaviors that
+ * wake up in response to a WakeupOnElapsedFrames(0) wakeup condition
+ * are guaranteed to take effect in the same rendering frame.</li>
+ * </ol>
+ *
+ * Note that modifications to geometry by-reference or texture
+ * by-reference are not guaranteed to show up in the same frame as
+ * other scene graph changes.
+ *
+ * <P>
+ * <b>Code Structure</b>
+ * <P>
+ * When the Java 3D behavior scheduler invokes a Behavior object's
+ * processStimulus method, that method may perform any computation it
+ * wishes. Usually, it will change its internal state and specify its
+ * new wakeup conditions. Most probably, it will manipulate scene
+ * graph elements. However, the behavior code can only change those
+ * aspects of a scene graph element permitted by the capabilities
+ * associated with that scene graph element. A scene graph's
+ * capabilities restrict behavioral manipulation to those
+ * manipulations explicitly allowed.
+ *
+ * <P>
+ * The application must provide the Behavior object with references to
+ * those scene graph elements that the Behavior object will
+ * manipulate. The application provides those references as arguments
+ * to the behavior's constructor when it creates the Behavior
+ * object. Alternatively, the Behavior object itself can obtain access
+ * to the relevant scene graph elements either when Java 3D invokes
+ * its initialize method or each time Java 3D invokes its
+ * processStimulus method.
+ *
+ * <P>
+ * Behavior methods have a very rigid structure. Java 3D assumes that
+ * they always run to completion (if needed, they can spawn
+ * threads). Each method's basic structure consists of the following:
+ *
+ * <P>
+ * <UL>
+ * <LI>Code to decode and extract references from the WakeupCondition
+ * enumeration that caused the object's awakening.</LI>
+ * <LI>Code to perform the manipulations associated with the
+ * WakeupCondition</LI>
+ * <LI>Code to establish this behavior's new WakeupCondition</LI>
+ * <LI>A path to Exit (so that execution returns to the Java 3D
+ * behavior scheduler)</LI>
+ * </UL>
+ *
+ * <P>
+ * <b>WakeupCondition Object</b>
+ * <P>
+ * A WakeupCondition object is an abstract class specialized to
+ * fourteen different WakeupCriterion objects and to four combining
+ * objects containing multiple WakeupCriterion objects. A Behavior
+ * node provides the Java 3D behavior scheduler with a WakeupCondition
+ * object. When that object's WakeupCondition has been satisfied, the
+ * behavior scheduler hands that same WakeupCondition back to the
+ * Behavior via an enumeration.
+ *
+ * <P>
+ * <b>WakeupCriterion Object</b>
+ * <P>
+ * Java 3D provides a rich set of wakeup criteria that Behavior
+ * objects can use in specifying a complex WakeupCondition. These
+ * wakeup criteria can cause Java 3D's behavior scheduler to invoke a
+ * behavior's processStimulus method whenever
+ *
+ * <UL>
+ * <LI>The center of a ViewPlatform enters a specified region</LI>
+ * <LI>The center of a ViewPlatform exits a specified region</LI>
+ * <LI>A behavior is activated</LI>
+ * <LI>A behavior is deactivated</LI>
+ * <LI>A specified TransformGroup node's transform changes</LI>
+ * <LI>Collision is detected between a specified Shape3D node's
+ * Geometry object and any other object</LI>
+ * <LI>Movement occurs between a specified Shape3D node's Geometry
+ * object and any other object with which it collides</LI>
+ * <LI>A specified Shape3D node's Geometry object no longer collides
+ * with any other object</LI>
+ * <LI>A specified Behavior object posts a specific event</LI>
+ * <LI>A specified AWT event occurs</LI>
+ * <LI>A specified time interval elapses</LI>
+ * <LI>A specified number of frames have been drawn</LI>
+ * <LI>The center of a specified Sensor enters a specified region</LI>
+ * <LI>The center of a specified Sensor exits a specified region</LI>
+ * </UL>
+ *
+ * <p>
+ * A Behavior object constructs a WakeupCriterion by constructing the
+ * appropriate criterion object. The Behavior object must provide the
+ * appropriate arguments (usually a reference to some scene graph
+ * object and possibly a region of interest). Thus, to specify a
+ * WakeupOnViewPlatformEntry, a behavior would specify the region that
+ * will cause the behavior to execute if an active ViewPlatform enters it.
+ *
+ * <p>
+ * Note that a unique WakeupCriterion object must be used with each
+ * instance of a Behavior. Sharing wakeup criteria among different
+ * instances of a Behavior is illegal.
+ *
+ * @see WakeupCondition
+ */
+
+public abstract class Behavior extends Leaf {
+
+ /**
+ * Constructs a Behavior node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * enable flag : true<br>
+ * scheduling bounds : null<br>
+ * scheduling bounding leaf : null<br>
+ * scheduling interval : numSchedulingIntervals / 2<br>
+ * </ul>
+ */
+ public Behavior() {
+ }
+
+ /**
+ * Initialize this behavior. Classes that extend Behavior must
+ * provide their own initialize method.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method. It is called
+ * by the Java 3D behavior scheduler.
+ */
+ public abstract void initialize();
+
+ /**
+ * Process a stimulus meant for this behavior. This method is invoked
+ * if the Behavior's wakeup criteria are satisfied and an active
+ * ViewPlatform's
+ * activation volume intersects with the Behavior's scheduling region.
+ * Classes that extend Behavior must provide their own processStimulus
+ * method.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method. It is called
+ * by the Java 3D behavior scheduler.
+ * @param criteria an enumeration of triggered wakeup criteria for this
+ * behavior
+ */
+ public abstract void processStimulus(Enumeration criteria);
+
+ /**
+ * Set the Behavior's scheduling region to the specified bounds.
+ * This is used when the scheduling bounding leaf is set to null.
+ * @param region the bounds that contains the Behavior's new scheduling
+ * region
+ */
+ public void setSchedulingBounds(Bounds region) {
+ ((BehaviorRetained)this.retained).setSchedulingBounds(region);
+ }
+
+ /**
+ * Retrieves the Behavior node's scheduling bounds.
+ * @return this Behavior's scheduling bounds information
+ */
+ public Bounds getSchedulingBounds() {
+ return ((BehaviorRetained)this.retained).getSchedulingBounds();
+ }
+
+ /**
+ * Set the Behavior's scheduling region to the specified bounding leaf.
+ * When set to a value other than null, this overrides the scheduling
+ * bounds object.
+ * @param region the bounding leaf node used to specify the Behavior
+ * node's new scheduling region
+ */
+ public void setSchedulingBoundingLeaf(BoundingLeaf region) {
+ ((BehaviorRetained)this.retained).setSchedulingBoundingLeaf(region);
+ }
+
+ /**
+ * Retrieves the Behavior node's scheduling bounding leaf.
+ * @return this Behavior's scheduling bounding leaf information
+ */
+ public BoundingLeaf getSchedulingBoundingLeaf() {
+ return ((BehaviorRetained)this.retained).getSchedulingBoundingLeaf();
+ }
+
+ /**
+ * Creates the retained mode BehaviorRetained object that this
+ * Behavior object will point to.
+ */
+ void createRetained() {
+ this.retained = new BehaviorRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Defines this behavior's wakeup criteria. This method
+ * may only be called from a Behavior object's initialize
+ * or processStimulus methods to (re)arm the next wakeup.
+ * It should be the last thing done by those methods.
+ * @param criteria the wakeup criteria for this behavior
+ * @exception IllegalStateException if this method is called by
+ * a method <i>other than</i> initialize or processStimulus
+ */
+ protected void wakeupOn(WakeupCondition criteria) {
+ BehaviorRetained behavret = (BehaviorRetained) this.retained;
+ synchronized (behavret) {
+ if (!behavret.inCallback) {
+ throw new IllegalStateException(J3dI18N.getString("Behavior0"));
+ }
+ }
+ behavret.wakeupOn(criteria);
+ }
+
+ /**
+ * Retrieves this behavior's current wakeup condition as set by
+ * the wakeupOn method. If no wakeup condition is currently
+ * active, null will be returned. In particular, this means that
+ * null will be returned if Java 3D is executing this behavior's
+ * processStimulus routine and wakeupOn has not yet been called to
+ * re-arm the wakeup condition for next time.
+ *
+ * @return the current wakeup condition for this behavior
+ *
+ * @since Java 3D 1.3
+ */
+ protected WakeupCondition getWakeupCondition() {
+ return ((BehaviorRetained)this.retained).getWakeupCondition();
+ }
+
+ /**
+ * Posts the specified postId to the Behavior Scheduler. All behaviors
+ * that have registered WakeupOnBehaviorPost with this postId, or a postId
+ * of 0, and with this behavior, or a null behavior, will have that wakeup
+ * condition met.
+ * <p>
+ * This feature allows applications to send arbitrary events into the
+ * behavior scheduler stream. It can be used as a notification scheme
+ * for communicating events to behaviors in the system.
+ * </p>
+ * @param postId the Id being posted
+ *
+ * @see WakeupOnBehaviorPost
+ */
+ public void postId(int postId){
+ ((BehaviorRetained)this.retained).postId(postId);
+ }
+
+ /**
+ * Enables or disables this Behavior. The default state is enabled.
+ * @param state true or false to enable or disable this Behavior
+ */
+ public void setEnable(boolean state) {
+ ((BehaviorRetained)this.retained).setEnable(state);
+ }
+
+ /**
+ * Retrieves the state of the Behavior enable flag.
+ * @return the Behavior enable state
+ */
+ public boolean getEnable() {
+ return ((BehaviorRetained)this.retained).getEnable();
+ }
+
+ /**
+ * Returns the number of scheduling intervals supported by this
+ * implementation of Java 3D. The minimum number of supported
+ * intervals must be at least 10. The default scheduling interval
+ * for each behavior instance is set to
+ * <code>numSchedulingIntervals / 2</code>.
+ *
+ * @return the number of supported scheduling intervals
+ *
+ * @since Java 3D 1.3
+ */
+ public static int getNumSchedulingIntervals() {
+ return BehaviorRetained.NUM_SCHEDULING_INTERVALS;
+ }
+
+
+ /**
+ * Sets the scheduling interval of this Behavior node to the
+ * specified value.
+ *
+ * The scheduling interval defines a partial order of execution
+ * for behaviors that wake up in response to the same wakeup
+ * condition (that is, those behaviors that are processed at the
+ * same "time"). Given a set of behaviors whose wakeup conditions
+ * are satisfied at the same time, the behavior scheduler will
+ * execute all behaviors in a lower scheduling interval before
+ * executing any behavior in a higher scheduling interval. Within
+ * a scheduling interval, behaviors can be executed in any order,
+ * or in parallel. Note that this partial ordering is only
+ * guaranteed for those behaviors that wake up at the same time in
+ * response to the same wakeup condition, for example, the set of
+ * behaviors that wake up every frame in response to a
+ * WakeupOnElapsedFrames(0) wakeup condition.
+ *
+ * The default value is <code>numSchedulingIntervals / 2</code>.
+ *
+ * @param schedulingInterval the new scheduling interval
+ *
+ * @exception IllegalArgumentException if
+ * <code>schedulingInterval</code> < 0 or
+ * <code>schedulingInterval</code> >=
+ * <code>numSchedulingIntervals</code>
+ *
+ * @since Java 3D 1.3
+ */
+ public void setSchedulingInterval(int schedulingInterval) {
+ if (schedulingInterval < 0 ||
+ schedulingInterval >= getNumSchedulingIntervals()) {
+
+ throw new IllegalStateException(J3dI18N.getString("Behavior1"));
+ }
+
+ ((BehaviorRetained)this.retained).
+ setSchedulingInterval(schedulingInterval);
+ }
+
+ /**
+ * Retrieves the current scheduling interval of this Behavior
+ * node.
+ *
+ * @return the current scheduling interval
+ *
+ * @since Java 3D 1.3
+ */
+ public int getSchedulingInterval() {
+ return ((BehaviorRetained)this.retained).getSchedulingInterval();
+ }
+
+ /**
+ * Returns the primary view associated with this behavior. This method
+ * is useful with certain types of behaviors (e.g., Billboard, LOD) that
+ * rely on per-View information and with behaviors in general in regards
+ * to scheduling (the distance from the view platform determines the
+ * active behaviors). The "primary" view is defined to be the first
+ * View attached to a live ViewPlatform, if there is more than one active
+ * View. So, for instance, Billboard behaviors would be oriented toward
+ * this primary view, in the case of multiple active views into the same
+ * scene graph.
+ */
+ protected View getView() {
+ return ((BehaviorRetained)this.retained).getView();
+ }
+
+
+ /**
+ * Copies all Behavior 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ BehaviorRetained attr = (BehaviorRetained) originalNode.retained;
+ BehaviorRetained rt = (BehaviorRetained) retained;
+
+ rt.setEnable(attr.getEnable());
+ rt.setSchedulingBounds(attr.getSchedulingBounds());
+ rt.setSchedulingInterval(attr.getSchedulingInterval());
+ // will set to the correct one in updateNodeReferences
+ rt.setSchedulingBoundingLeaf(attr.getSchedulingBoundingLeaf());
+
+ }
+
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ *
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ BehaviorRetained rt = (BehaviorRetained) retained;
+ BoundingLeaf bl= rt.getSchedulingBoundingLeaf();
+
+ // check for schedulingBoundingLeaf
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.setSchedulingBoundingLeaf((BoundingLeaf) o);
+
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/BehaviorRetained.java b/src/classes/share/javax/media/j3d/BehaviorRetained.java
new file mode 100644
index 0000000..54e0635
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BehaviorRetained.java
@@ -0,0 +1,508 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.ArrayList;
+
+/**
+ * Behavior is an abstract class that contains the framework for all
+ * behavioral components in Java 3D.
+ */
+
+class BehaviorRetained extends LeafRetained {
+ // These bitmasks are used to quickly tell what conditions this behavior
+ // is waiting for. Currently BehaviorStructure only used 4 of them.
+ static final int WAKEUP_ACTIVATE_INDEX = 0;
+ static final int WAKEUP_DEACTIVATE_INDEX = 1;
+ static final int WAKEUP_VP_ENTRY_INDEX = 2;
+ static final int WAKEUP_VP_EXIT_INDEX = 3;
+ static final int WAKEUP_TIME_INDEX = 4;
+
+ static final int NUM_WAKEUPS = 5;
+
+ static final int WAKEUP_ACTIVATE = 0x0001;
+ static final int WAKEUP_DEACTIVATE = 0x0002;
+ static final int WAKEUP_VP_ENTRY = 0x0004;
+ static final int WAKEUP_VP_EXIT = 0x0008;
+ static final int WAKEUP_TIME = 0x0010;
+
+ /**
+ * The number of scheduling intervals supported by this
+ * implementation. This is fixed for a particular implementation
+ * and must be at least 10.
+ */
+ static final int NUM_SCHEDULING_INTERVALS = 10;
+
+ // different types of IndexedUnorderedSet that use in BehaviorStructure
+ static final int BEHAIVORS_IN_BS_LIST = 0;
+ static final int SCHEDULE_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ /**
+ * The Boundary object defining the behavior's scheduling region.
+ */
+ Bounds schedulingRegion = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * The current wakeup condition.
+ */
+ WakeupCondition wakeupCondition = null;
+
+ /**
+ * This is the new WakeupCondition to be set in
+ * initialize wakeupOn()
+ */
+ WakeupCondition newWakeupCondition = null;
+
+ /**
+ * The current view platform for this behavior; this value is
+ * false until it comes into range of a view platform.
+ */
+ ViewPlatformRetained vp = null;
+
+ /**
+ * The current activation status for this behavior; this value
+ * is false until it comes into range of a view platform.
+ */
+ boolean active = false;
+
+ /**
+ * Flag indicating whether the behavior is enabled.
+ */
+ boolean enable = true;
+
+ /**
+ * Current scheduling interval.
+ */
+ int schedulingInterval = NUM_SCHEDULING_INTERVALS / 2;
+
+ /**
+ * This is a flag that tells the behavior scheduler whether the
+ * user-programmed process stimulus called wakeupOn, if it did
+ * not, then the wakeupCondition will be set to null.
+ */
+ boolean conditionSet = false;
+
+ /**
+ * This is a flag that indicates whether we are in an initialize or
+ * processStimulus callback. If wakeupOn is called for this behavior
+ * when this flag is not set, an exception will be thrown.
+ */
+ boolean inCallback = false;
+
+ /**
+ * This is a flag that indicates whether we are in initialize
+ * callback. If wakeupOn is called for this behavior when
+ * this flag is true, then its
+ * buildTree() will delay until insert nodes message
+ * is get. This is because some localToVworld[] that wakeup
+ * depends may not initialize when this behavior setLive().
+ */
+ boolean inInitCallback = false;
+
+ /**
+ * The transformed schedulingRegion
+ */
+ Bounds transformedRegion = null;
+
+ // A bitmask that indicates that the scheduling region has changed.
+ int isDirty = 0xffff;
+
+ /**
+ * A bitmask that represents all conditions that this behavior is waiting on.
+ */
+ int wakeupMask = 0;
+
+ /**
+ * An array of ints that count how many of each wakup is present
+ */
+ int[] wakeupArray = new int[NUM_WAKEUPS];
+
+ // use to post message when bounds change, always point to this
+ Object targets[] = new Object[1];
+
+ BehaviorRetained() {
+ this.nodeType = NodeRetained.BEHAVIOR;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ targets[0] = this;
+ IndexedUnorderSet.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Get the Behavior's scheduling region.
+ * @return this Behavior's scheduling region information
+ */
+ Bounds getSchedulingBounds() {
+ Bounds b = null;
+
+ if (schedulingRegion != null) {
+ b = (Bounds) schedulingRegion.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Set the Behavior's scheduling region.
+ * @param region a region that contains the Behavior's new scheduling
+ * bounds
+ */
+ synchronized void setSchedulingBounds(Bounds region) {
+
+ if (region != null) {
+ schedulingRegion = (Bounds) region.clone();
+ if (staticTransform != null) {
+ schedulingRegion.transform(staticTransform.transform);
+ }
+ } else {
+ schedulingRegion = null;
+ }
+
+ if (source != null && source.isLive()) {
+ sendMessage(J3dMessage.REGION_BOUND_CHANGED);
+ }
+ }
+
+ /**
+ * Set the Sound's scheduling region to the specified Leaf node.
+ */
+ synchronized void setSchedulingBoundingLeaf(BoundingLeaf region) {
+
+ if (source != null && source.isLive()) {
+ if (boundingLeaf != null)
+ boundingLeaf.mirrorBoundingLeaf.removeUser(this);
+ }
+
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ } else {
+ boundingLeaf = null;
+ }
+
+ if (source != null && source.isLive()) {
+ if (boundingLeaf != null)
+ boundingLeaf.mirrorBoundingLeaf.addUser(this);
+ sendMessage(J3dMessage.REGION_BOUND_CHANGED);
+ }
+ }
+
+ /**
+ * Enables or disables this Behavior. The default state is enabled.
+ * @param state true or false to enable or disable this Behavior
+ */
+ void setEnable(boolean state) {
+ if (enable != state) {
+ enable = state;
+ if (source != null && source.isLive()) {
+ sendMessage(state ? J3dMessage.BEHAVIOR_ENABLE:
+ J3dMessage.BEHAVIOR_DISABLE);
+ }
+ }
+ }
+
+
+ /**
+ * Retrieves the state of the Behavior enable flag.
+ * @return the Behavior enable state
+ */
+ boolean getEnable() {
+ return enable;
+ }
+
+
+ /**
+ * Sets the scheduling interval of this Behavior node to the
+ * specified value.
+ * @param schedulingInterval the new scheduling interval
+ */
+ void setSchedulingInterval(int schedulingInterval) {
+
+ if ((source != null) && source.isLive()
+ && !inCallback) {
+ // avoid MT safe problem when user thread setting
+ // this while behavior scheduling using this.
+ sendMessage(J3dMessage.SCHEDULING_INTERVAL_CHANGED,
+ new Integer(schedulingInterval));
+ } else {
+ // garantee this setting reflect in next frame
+ this.schedulingInterval = schedulingInterval;
+ }
+ }
+
+
+ /**
+ * Retrieves the current scheduling interval of this Behavior
+ * node.
+ *
+ * @return the current scheduling interval
+ */
+ int getSchedulingInterval() {
+ return schedulingInterval;
+ }
+
+
+ /**
+ * Get the Behavior's scheduling region
+ */
+ BoundingLeaf getSchedulingBoundingLeaf() {
+ return (boundingLeaf != null ?
+ (BoundingLeaf)boundingLeaf.source : null);
+ }
+
+ /**
+ * This setLive routine first calls the superclass's method, then
+ * it activates all canvases that are associated with the attached
+ * view.
+ */
+ synchronized void setLive(SetLiveState s) {
+
+ super.doSetLive(s);
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("BehaviorRetained0"));
+ }
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("BehaviorRetained1"));
+ }
+
+ s.nodeList.add(this);
+ s.behaviorNodes.add(this);
+ s.notifyThreads |= J3dThread.UPDATE_BEHAVIOR;
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.BEH_TARGETS);
+ }
+ switchState = (SwitchState)s.switchStates.get(0);
+
+ if (boundingLeaf != null) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(this);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.BEH_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ super.markAsLive();
+ }
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it deactivates all canvases that are associated with the attached
+ * view.
+ */
+ synchronized void clearLive(SetLiveState s) {
+ super.clearLive(s);
+ s.nodeList.add(this);
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.BEH_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ s.notifyThreads |= J3dThread.UPDATE_BEHAVIOR;
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.BEH_TARGETS);
+ }
+ if (boundingLeaf != null) {
+ boundingLeaf.mirrorBoundingLeaf.removeUser(this);
+ }
+ // BehaviorStructure removeBehavior() will do the
+ // wakeupCondition.cleanTree() over there.
+ }
+
+ /**
+ * This routine execute the user's initialize method
+ */
+ void executeInitialize() {
+
+ synchronized (this) {
+ boolean inCallbackSaved = inCallback;
+ boolean inInitCallbackSaved = inInitCallback;
+
+ inCallback = true;
+ inInitCallback = true;
+ try {
+ ((Behavior)this.source).initialize();
+ }
+ catch (RuntimeException e) {
+ inCallback = inCallbackSaved;
+ inInitCallback = inInitCallbackSaved;
+ System.err.println("Exception occurred during Behavior initialization:");
+ e.printStackTrace();
+ }
+ inCallback = inCallbackSaved;
+ inInitCallback = inInitCallbackSaved;
+ }
+ }
+
+ /**
+ * Defines this behavior's wakeup criteria.
+ * @param criteria The wakeup criterion for this object
+ */
+ void wakeupOn(WakeupCondition criteria) {
+ // If not call by initialize(), buildTree will
+ // delay until insertNodes in BehaviorStructure
+ // Otherwise BehaviorScheduler will invoke
+ // handleLastWakeupOn()
+ if (criteria == null) {
+ throw new NullPointerException(J3dI18N.getString("BehaviorRetained2"));
+ }
+
+ if (!inInitCallback) {
+ conditionSet = true;
+ wakeupCondition = criteria;
+ } else {
+ // delay setting wakeup condition in BehaviorStructure
+ // activateBehaviors(). This is because there may have
+ // previously wakeupCondition attach to it and
+ // scheduling even after clearLive() due to message
+ // delay processing. It is not MT safe to set it
+ // in user thread.
+ newWakeupCondition = criteria;
+ }
+
+ }
+
+ // The above wakeupOn() just remember the reference
+ // We only need to handle (and ignore the rest) the
+ // last wakeupOn() condition set in the behavior.
+ // This handle the case when multiple wakeupOn()
+ // are invoked in the same processStimulus()
+ void handleLastWakeupOn(WakeupCondition prevWakeupCond,
+ BehaviorStructure bs) {
+
+ if (bs == universe.behaviorStructure) {
+ if (wakeupCondition == prevWakeupCond) {
+ // reuse the same wakeupCondition
+ wakeupCondition.resetTree();
+ } else {
+ if (prevWakeupCond != null) {
+ prevWakeupCond.cleanTree(bs);
+ }
+ wakeupCondition.buildTree(null, 0, this);
+ }
+ } else {
+ // No need to do prevWakeupCond.cleanTree(bs)
+ // since removeBehavior() will do so
+ }
+ }
+
+
+ /**
+ * Returns this behavior's wakeup criteria.
+ * @return criteria The wakeup criteria of this object
+ */
+ WakeupCondition getWakeupCondition() {
+ return wakeupCondition;
+ }
+
+ /**
+ * Post the specified Id. Behaviors use this method to cause sequential
+ * scheduling of other behavior object.
+ * @param postId The Id being posted
+ */
+
+ void postId(int postId){
+ if (source != null && source.isLive()) {
+ universe.behaviorStructure.handleBehaviorPost((Behavior) source, postId);
+ }
+ }
+
+ protected View getView() {
+ return (universe != null ?
+ universe.getCurrentView() : null);
+ }
+
+ synchronized void updateTransformRegion(Bounds bound) {
+ if (boundingLeaf == null) {
+ updateTransformRegion();
+ } else {
+ if (bound == null) {
+ transformedRegion = null;
+ } else {
+ transformedRegion = (Bounds) bound.clone();
+ transformedRegion.transform(
+ boundingLeaf.mirrorBoundingLeaf.getCurrentLocalToVworld());
+ }
+ }
+ }
+
+ synchronized void updateTransformRegion() {
+ if (boundingLeaf == null ||
+ !boundingLeaf.mirrorBoundingLeaf.switchState.currentSwitchOn) {
+ if (schedulingRegion == null) {
+ transformedRegion = null;
+ } else {
+ // use schedulingRegion
+ if (transformedRegion != null) {
+ transformedRegion.set(schedulingRegion);
+ } else {
+ transformedRegion = (Bounds) schedulingRegion.clone();
+ }
+ transformedRegion.transform(getCurrentLocalToVworld());
+
+ }
+ } else {
+ // use boundingLeaf
+ transformedRegion =
+ boundingLeaf.mirrorBoundingLeaf.transformedRegion;
+
+ }
+ }
+
+
+ // Note: This routine will only to update the object's
+ // transformed region
+ void updateBoundingLeaf(long refTime) {
+ transformedRegion = (Bounds)boundingLeaf.mirrorBoundingLeaf.transformedRegion;
+ }
+
+
+ void addWakeupCondition() {}
+
+ final void sendMessage(int mtype, Object arg) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_BEHAVIOR;
+ createMessage.type = mtype;
+ createMessage.universe = universe;
+ createMessage.args[0] = targets;
+ createMessage.args[1]= this;
+ createMessage.args[2]= arg;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ final void sendMessage(int mtype) {
+ sendMessage(mtype, null);
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (schedulingRegion != null) {
+ schedulingRegion.transform(xform.transform);
+ }
+ if (source instanceof DistanceLOD) {
+ ((DistanceLOD)source).mergeTransform(xform);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BehaviorScheduler.java b/src/classes/share/javax/media/j3d/BehaviorScheduler.java
new file mode 100644
index 0000000..a152408
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BehaviorScheduler.java
@@ -0,0 +1,218 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+class BehaviorScheduler extends J3dThread {
+
+ /**
+ * The virtual universe that owns this BehaviorScheduler
+ */
+ VirtualUniverse univ = null;
+
+ // reference to behaviourStructure processList
+ UnorderList processList[];
+
+ // reference to scheduleList;
+ IndexedUnorderSet scheduleList;
+
+ // reference to universe.behaviorStructure
+ BehaviorStructure behaviorStructure;
+
+ // A count for BehaviorScheduler start/stop
+ int stopCount = -1;
+
+ /**
+ * These are used for start/stop BehaviorScheduler
+ */
+ long lastStartTime;
+ long lastStopTime;
+
+ // lock to ensure consistency of interval values read
+ Object intervalTimeLock = new Object();
+
+ /**
+ * Some variables used to name threads correctly
+ */
+ private static int numInstances = 0;
+ private int instanceNum = -1;
+
+ private synchronized int newInstanceNum() {
+ return (++numInstances);
+ }
+
+ int getInstanceNum() {
+ if (instanceNum == -1)
+ instanceNum = newInstanceNum();
+ return instanceNum;
+ }
+
+
+ BehaviorScheduler(ThreadGroup t, VirtualUniverse universe) {
+ super(t);
+ setName("J3D-BehaviorScheduler-" + getInstanceNum());
+ this.univ = universe;
+ behaviorStructure = universe.behaviorStructure;
+ scheduleList = behaviorStructure.scheduleList;
+ processList = behaviorStructure.processList;
+ type = J3dThread.BEHAVIOR_SCHEDULER;
+ }
+
+ void stopBehaviorScheduler(long[] intervalTime) {
+
+ stopCount = 2;
+ VirtualUniverse.mc.sendRunMessage(univ, J3dThread.BEHAVIOR_SCHEDULER);
+ while (!userStop ) {
+ MasterControl.threadYield();
+ }
+ synchronized (intervalTimeLock) {
+ intervalTime[0] = lastStartTime;
+ intervalTime[1] = lastStopTime;
+ }
+ }
+
+ void startBehaviorScheduler() {
+ // don't allow scheduler start until intervalTime is read
+ synchronized (intervalTimeLock) {
+ stopCount = -1;
+ userStop = false;
+ VirtualUniverse.mc.setWork();
+ }
+ }
+
+ void deactivate() {
+ active = false;
+ if (stopCount >= 0) {
+ userStop = true;
+ }
+ }
+
+ /**
+ * The main loop for the Behavior Scheduler.
+ * Main method for firing off vector of satisfied conditions that
+ * are contained in the condMet vector. Method is synchronized
+ * because it is modifying the current wakeup vectors in the
+ * clean (emptying out satisfied conditions) and processStimulus
+ * (adding conditions again if wakeupOn called) calls.
+ */
+ void doWork(long referenceTime) {
+ BehaviorRetained arr[];
+ UnorderList list;
+ int i, size, interval;
+
+ lastStartTime = System.currentTimeMillis();
+
+ if (stopCount >= 0) {
+ VirtualUniverse.mc.sendRunMessage(univ, J3dThread.BEHAVIOR_SCHEDULER);
+ if (--stopCount == 0) {
+ userStop = true;
+ }
+ }
+
+
+ for (interval = 0;
+ interval < BehaviorRetained.NUM_SCHEDULING_INTERVALS;
+ interval++) {
+
+ list = processList[interval];
+
+ if (list.isEmpty()) {
+ continue;
+ }
+ arr = (BehaviorRetained []) list.toArray(false);
+
+ size = list.arraySize();
+
+ for (i = 0; i < size ; i++) {
+ BehaviorRetained behavret = arr[i];
+
+
+ synchronized (behavret) {
+ Behavior behav = (Behavior) behavret.source;
+
+ if (!behav.isLive() ||
+ !behavret.conditionSet ||
+ (behavret.wakeupCondition == null)) {
+ continue;
+ }
+
+ if (behavret.wakeupCondition.trigEnum == null) {
+ behavret.wakeupCondition.trigEnum =
+ new WakeupCriteriaEnumerator(behavret.wakeupCondition,
+ WakeupCondition.TRIGGERED_ELEMENTS);
+ } else {
+ behavret.wakeupCondition.trigEnum.reset(
+ behavret.wakeupCondition,
+ WakeupCondition.TRIGGERED_ELEMENTS);
+ }
+
+ // BehaviorRetained now cache the old
+ // wakeupCondition in order to
+ // reuse it without the heavyweight cleanTree()
+ // behavret.wakeupCondition.cleanTree();
+
+ behavret.conditionSet = false;
+ WakeupCondition wakeupCond = behavret.wakeupCondition;
+
+ synchronized (behavret) {
+ behavret.inCallback = true;
+ univ.inBehavior = true;
+ try {
+ behav.processStimulus(wakeupCond.trigEnum);
+ }
+ catch (RuntimeException e) {
+ if (wakeupCond != null) {
+ wakeupCond.cleanTree(behaviorStructure);
+ }
+ System.err.println("Exception occurred during Behavior execution:");
+ e.printStackTrace();
+ }
+ univ.inBehavior = false;
+ behavret.inCallback = false;
+ }
+ // note that if the behavior wasn't reset, we need to make the
+ // wakeupcondition equal to null
+ if (behavret.conditionSet == false) {
+ if (wakeupCond != null) {
+ wakeupCond.cleanTree(behaviorStructure);
+ }
+ behavret.wakeupCondition = null;
+ behavret.active = false;
+ scheduleList.remove(behavret);
+ } else {
+ behavret.handleLastWakeupOn(wakeupCond,
+ behaviorStructure);
+ }
+ }
+ }
+ list.clear();
+ }
+
+ behaviorStructure.handleAWTEvent();
+ behaviorStructure.handleBehaviorPost();
+ lastStopTime = System.currentTimeMillis();
+
+ }
+
+ void free() {
+ behaviorStructure = null;
+ getThreadData(null, null).thread = null;
+ univ = null;
+ for (int i=BehaviorRetained.NUM_SCHEDULING_INTERVALS-1;
+ i >= 0; i--) {
+ processList[i].clear();
+ }
+ scheduleList.clear();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BehaviorStructure.java b/src/classes/share/javax/media/j3d/BehaviorStructure.java
new file mode 100644
index 0000000..f03d9c9
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BehaviorStructure.java
@@ -0,0 +1,1640 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.Arrays;
+
+/**
+ * A behavior structure is a object that organizes behaviors,
+ * wakeup conditions, and other behavior scheduler entities.
+ */
+
+class BehaviorStructure extends J3dStructure {
+
+ /**
+ * The list of behaviors
+ */
+ IndexedUnorderSet behaviors;
+
+ /**
+ * The list of view platforms
+ */
+ IndexedUnorderSet viewPlatforms;
+
+ /**
+ * An array of schedulable behaviors, use in
+ * removeViewPlatform() to go through only active behaviors
+ */
+ IndexedUnorderSet scheduleList;
+
+ /**
+ * An array of process behaviors
+ */
+ UnorderList processList[] = new UnorderList[BehaviorRetained.NUM_SCHEDULING_INTERVALS];
+
+ /**
+ * A bounds used for getting a view platform scheduling BoundingSphere
+ */
+ // BoundingSphere tempSphere = new BoundingSphere();
+ // BoundingSphere vpsphere = new BoundingSphere();
+ Point3d vpCenter = new Point3d();
+ Point3d vpTransCenter = new Point3d();
+
+ /**
+ * A list of bounds WakeupOnViewPlatformEntry objects that
+ * have seen ViewPlatformEntry
+ */
+ WakeupIndexedList boundsEntryList;
+
+ /**
+ * A list of bounds WakeupOnViewPlatformExit objects that have
+ * seen ViewPlatformEntry
+ */
+ WakeupIndexedList boundsExitList;
+
+ /**
+ * A list of WakeupOnSensorEntry objects that have seen a sensor
+ */
+ WakeupIndexedList currentSensorEntryList;
+
+ /**
+ * A list of WakeupOnSensorExit objects that have seen a sensor
+ */
+ WakeupIndexedList currentSensorExitList;
+
+ /**
+ * The lists of the WakeupCriterion objects that the
+ * behavior scheduler keeps.
+ */
+ WakeupIndexedList wakeupOnAWTEvent;
+ WakeupIndexedList wakeupOnActivation;
+ WakeupIndexedList wakeupOnDeactivation;
+ WakeupIndexedList wakeupOnBehaviorPost;
+ WakeupIndexedList wakeupOnElapsedFrames;
+ WakeupIndexedList wakeupOnViewPlatformEntry;
+ WakeupIndexedList wakeupOnViewPlatformExit;
+ WakeupIndexedList wakeupOnSensorEntry;
+ WakeupIndexedList wakeupOnSensorExit;
+
+ // Temporary array for processTransformChanged()
+ UnorderList transformViewPlatformList = new UnorderList(ViewPlatformRetained.class);
+
+
+ // The number of active wakeup condition in wakeupOnElapsedFrames
+ int activeWakeupOnFrameCount = 0;
+
+ // The number of active wakeup condition in wakeupOnSensorEntry/Exit
+ int activeWakeupOnSensorCount = 0;
+
+ /**
+ * Buffers to hold events when user thread is in processStimulus()
+ * while this event is receiving. This avoid any lost of event.
+ * We did not remove individual element from the following list
+ * (except clear()) so the order is still preserve.
+ */
+ UnorderList awtEventsBuffer = new UnorderList(AWTEvent.class);
+
+ // Use generic integer array to avoid new Integer() for individual element
+ int postIDBuffer[] = new int[10]; // size of default UnorderList
+ int clonePostIDBuffer[] = new int[postIDBuffer.length];
+
+ UnorderList behaviorPostBuffer = new UnorderList(Behavior.class);
+
+ // temp values for transformed hotspot used in
+ // wakeupOnSensorEntry/ExitupdateSensorsHotspot
+ Transform3D sensorTransform = new Transform3D();
+ Vector3d sensorLoc = new Vector3d();
+ Point3d ptSensorLoc = new Point3d();
+
+ // list of active physical environments
+ UnorderList physicalEnvironments = new UnorderList(1, PhysicalEnvironment.class);
+
+
+ // list of Behavior waiting to be add to behavior list and buildTree()
+ UnorderList pendingBehaviors = new UnorderList(BehaviorRetained.class);
+
+ // true if branch detach
+ boolean branchDetach = false;
+
+ // This is used to notify WakeupOnAWTEvent re-enable Canvas3D events
+ long awtEventTimestamp = 1;
+
+ // used to process transform messages
+ boolean transformMsg = false;
+ UpdateTargets targets = null;
+
+ BehaviorStructure(VirtualUniverse u) {
+ super(u, J3dThread.UPDATE_BEHAVIOR);
+
+ for (int i=BehaviorRetained.NUM_SCHEDULING_INTERVALS-1;
+ i >= 0; i--) {
+ processList[i] = new UnorderList(BehaviorRetained.class);
+ }
+ behaviors = new IndexedUnorderSet(BehaviorRetained.class,
+ BehaviorRetained.BEHAIVORS_IN_BS_LIST, u);
+ viewPlatforms = new IndexedUnorderSet(ViewPlatformRetained.class,
+ ViewPlatformRetained.VP_IN_BS_LIST, u);
+ scheduleList = new IndexedUnorderSet(BehaviorRetained.class,
+ BehaviorRetained.SCHEDULE_IN_BS_LIST, u);
+ boundsEntryList = new WakeupIndexedList(WakeupOnViewPlatformEntry.class,
+ WakeupOnViewPlatformEntry.BOUNDSENTRY_IN_BS_LIST, u);
+ boundsExitList = new WakeupIndexedList(WakeupOnViewPlatformExit.class,
+ WakeupOnViewPlatformExit.BOUNDSEXIT_IN_BS_LIST, u);
+ currentSensorEntryList = new WakeupIndexedList(WakeupOnSensorEntry.class,
+ WakeupOnSensorEntry.SENSORENTRY_IN_BS_LIST, u);
+ currentSensorExitList = new WakeupIndexedList(WakeupOnSensorExit.class,
+ WakeupOnSensorExit.SENSOREXIT_IN_BS_LIST, u);
+ wakeupOnAWTEvent = new WakeupIndexedList(WakeupOnAWTEvent.class,
+ WakeupOnAWTEvent.COND_IN_BS_LIST, u);
+ wakeupOnActivation = new WakeupIndexedList(WakeupOnActivation.class,
+ WakeupOnActivation.COND_IN_BS_LIST, u);
+ wakeupOnDeactivation = new WakeupIndexedList(WakeupOnDeactivation.class,
+ WakeupOnDeactivation.COND_IN_BS_LIST, u);
+ wakeupOnBehaviorPost = new WakeupIndexedList(WakeupOnBehaviorPost.class,
+ WakeupOnBehaviorPost.COND_IN_BS_LIST, u);
+ wakeupOnElapsedFrames = new WakeupIndexedList(WakeupOnElapsedFrames.class,
+ WakeupOnElapsedFrames.COND_IN_BS_LIST, u);
+ wakeupOnViewPlatformEntry = new WakeupIndexedList(WakeupOnViewPlatformEntry.class,
+ WakeupOnViewPlatformEntry.COND_IN_BS_LIST, u);
+ wakeupOnViewPlatformExit = new WakeupIndexedList(WakeupOnViewPlatformExit.class,
+ WakeupOnViewPlatformExit.COND_IN_BS_LIST, u);
+ wakeupOnSensorEntry = new WakeupIndexedList(WakeupOnSensorEntry.class,
+ WakeupOnSensorEntry.COND_IN_BS_LIST, u);
+ wakeupOnSensorExit = new WakeupIndexedList(WakeupOnSensorExit.class,
+ WakeupOnSensorExit.COND_IN_BS_LIST, u);
+
+ }
+
+ void processMessages(long referenceTime) {
+
+ J3dMessage[] messages = getMessages(referenceTime);
+ int nMsg = getNumMessage();
+ J3dMessage m;
+
+ if (nMsg > 0) {
+ for (int i=0; i<nMsg; i++) {
+ m = messages[i];
+
+ switch (m.type) {
+ case J3dMessage.TRANSFORM_CHANGED: // Compress Message
+ transformMsg = true;
+ break;
+ case J3dMessage.COND_MET:
+ // No need to compress Message since wakeupCondition
+ // will make sure that only one message is sent.
+ processConditionMet((BehaviorRetained) m.args[0],
+ (Boolean) m.args[1]);
+ break;
+ case J3dMessage.INSERT_NODES:
+ insertNodes((Object[])m.args[0]);
+ break;
+ case J3dMessage.REMOVE_NODES:
+ removeNodes(m);
+ break;
+ case J3dMessage.BEHAVIOR_ACTIVATE:
+ activateBehaviors();
+ break;
+ case J3dMessage.BEHAVIOR_ENABLE:
+ addToScheduleList((BehaviorRetained) m.args[1]);
+ reEvaluateWakeupCount();
+ break;
+ case J3dMessage.BEHAVIOR_DISABLE:
+ removeFromScheduleList((BehaviorRetained) m.args[1]);
+ reEvaluateWakeupCount();
+ break;
+ case J3dMessage.SCHEDULING_INTERVAL_CHANGED:
+ ((BehaviorRetained) m.args[1]).schedulingInterval
+ = ((Integer) m.args[2]).intValue();
+ break;
+ case J3dMessage.SWITCH_CHANGED:
+ processSwitchChanged(m);
+ // may need to process dirty switched-on transform
+ if (universe.transformStructure.getLazyUpdate()) {
+ transformMsg = true;
+ }
+ break;
+ case J3dMessage.BOUNDINGLEAF_CHANGED:
+ processBoundingLeafChanged((Object []) m.args[3],
+ (Bounds) m.args[2]);
+ break;
+ case J3dMessage.UPDATE_VIEW:
+ reEvaluatePhysicalEnvironments();
+ ViewPlatform v = ((View)
+ m.args[0]).getViewPlatform();
+ if (v != null) {
+ // ViewPlatform may set to null when deactivate()
+ processViewPlatformTransform((ViewPlatformRetained) v.retained);
+ }
+ break;
+ case J3dMessage.UPDATE_VIEWPLATFORM:
+ ViewPlatformRetained vp = (ViewPlatformRetained) m.args[0];
+ // update cached scheduling region first
+ vp.updateActivationRadius(((Float) m.args[1]).floatValue());
+ // then process the VP transform
+ processViewPlatformTransform(vp);
+ break;
+ case J3dMessage.REGION_BOUND_CHANGED:
+ {
+ BehaviorRetained behav = (BehaviorRetained) m.args[1];
+ behav.updateTransformRegion();
+ processBehaviorTransform(behav);
+ }
+ break;
+ case J3dMessage.BEHAVIOR_REEVALUATE:
+ {
+ BehaviorRetained behav = (BehaviorRetained) m.args[0];
+ behav.active = false;
+ addToScheduleList(behav);
+ }
+ break;
+ }
+ m.decRefcount();
+ }
+
+ if (transformMsg) {
+ // get the targets from the transform structure
+ targets = universe.transformStructure.getTargetList();
+
+ // process the transform changed for each target
+ UnorderList arrList;
+
+ arrList = targets.targetList[Targets.BEH_TARGETS];
+ if (arrList != null) {
+ processBehXformChanged(arrList);
+ }
+
+ arrList = targets.targetList[Targets.VPF_TARGETS];
+ if (arrList != null) {
+ processVpfXformChanged(arrList);
+ }
+
+ transformMsg = false;
+ targets = null;
+ }
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ // wakeup even when message is null since wakeupOnElapsedFrame
+ // will wakeup this
+
+ if (activeWakeupOnSensorCount <= 0) {
+ if (activeWakeupOnFrameCount > 0) {
+ // Wakeup render thread when there is pending wakeupOnElapsedFrames
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.BEHAVIOR_SCHEDULER|
+ J3dThread.RENDER_THREAD);
+
+ } else {
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.BEHAVIOR_SCHEDULER);
+ }
+ } else {
+ checkSensorEntryExit();
+ // we have to invoke checkSensorEntryExit() next time
+ if (activeWakeupOnFrameCount > 0) {
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.UPDATE_BEHAVIOR|
+ J3dThread.BEHAVIOR_SCHEDULER|
+ J3dThread.RENDER_THREAD);
+
+ } else {
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.UPDATE_BEHAVIOR|
+ J3dThread.BEHAVIOR_SCHEDULER);
+ }
+ }
+ }
+
+ void insertNodes(Object[] nodes) {
+ for (int i=0; i<nodes.length; i++) {
+ Object node = (Object) nodes[i];
+
+ if (node instanceof BehaviorRetained) {
+ pendingBehaviors.add(node);
+ }
+ else if (node instanceof ViewPlatformRetained) {
+ addViewPlatform((ViewPlatformRetained) node);
+ }
+ }
+ }
+
+ void activateBehaviors() {
+ BehaviorRetained behav;
+ BehaviorRetained behavArr[] = (BehaviorRetained [])
+ pendingBehaviors.toArray(false);
+
+ for (int i=pendingBehaviors.arraySize()-1; i>=0; i--) {
+ behav = behavArr[i];
+ behav.wakeupCondition = behav.newWakeupCondition;
+ if (behav.wakeupCondition != null) {
+ behav.wakeupCondition.buildTree(null, 0, behav);
+ behav.conditionSet = true;
+ behaviors.add(behav);
+ behav.updateTransformRegion();
+ addToScheduleList(behav);
+ }
+ }
+ pendingBehaviors.clear();
+ }
+
+ void addViewPlatform(ViewPlatformRetained vp) {
+ int i;
+ BehaviorRetained behav;
+ BehaviorRetained behavArr[] = (BehaviorRetained []) behaviors.toArray(false);
+
+ viewPlatforms.add(vp);
+ vp.updateTransformRegion();
+
+ if (!vp.isActiveViewPlatform()) {
+ return;
+ }
+
+ // re-evaulate all behaviors to see if we need to put
+ // more behaviors in scheduleList
+
+ for (i=behaviors.arraySize()-1; i>=0; i--) {
+ addToScheduleList(behavArr[i]);
+ }
+
+ // handle ViewPlatform Entry
+ WakeupOnViewPlatformEntry wakeupOnViewPlatformEntryArr[] =
+ (WakeupOnViewPlatformEntry []) wakeupOnViewPlatformEntry.toArray(false);
+ WakeupOnViewPlatformEntry wentry;
+
+ for (i=wakeupOnViewPlatformEntry.arraySize()-1; i >=0; i--) {
+ wentry = wakeupOnViewPlatformEntryArr[i];
+ if (!boundsEntryList.contains(wentry) &&
+ wentry.transformedRegion.intersect(vp.center)) {
+ boundsEntryList.add(wentry);
+ wentry.triggeredVP = vp;
+ wentry.setTriggered();
+ }
+ }
+
+ // handle ViewPlatform Exit
+ WakeupOnViewPlatformExit wakeupOnViewPlatformExitArr[] =
+ (WakeupOnViewPlatformExit []) wakeupOnViewPlatformExit.toArray(false);
+ WakeupOnViewPlatformExit wexit;
+
+ for (i=wakeupOnViewPlatformExit.arraySize()-1; i >=0; i--) {
+ wexit = wakeupOnViewPlatformExitArr[i];
+ if (!boundsExitList.contains(wexit) &&
+ wexit.transformedRegion.intersect(vp.center)) {
+ wexit.triggeredVP = vp;
+ boundsExitList.add(wexit);
+ }
+ }
+
+ }
+
+ void removeNodes(J3dMessage m) {
+ Object[] nodes = (Object[]) m.args[0];
+ boolean behavRemove = false;
+
+ for (int i=0; i<nodes.length; i++) {
+ Object node = nodes[i];
+ if (node instanceof BehaviorRetained) {
+ behavRemove = true;
+ removeBehavior((BehaviorRetained) node);
+ }
+ else if (node instanceof ViewPlatformRetained) {
+ removeViewPlatform((ViewPlatformRetained) node);
+ }
+ }
+
+ // Since BehaviorScheduler will run after BehaviorStructure
+ // (not in parallel). It is safe to do cleanup here.
+ wakeupOnAWTEvent.clearMirror();
+ awtEventsBuffer.clearMirror();
+ wakeupOnBehaviorPost.clearMirror();
+ behaviorPostBuffer.clearMirror();
+ wakeupOnSensorEntry.clearMirror();
+ wakeupOnSensorExit.clearMirror();
+ branchDetach = true;
+
+ if (behavRemove) {
+ // disable AWT Event from Canvas3D
+ WakeupOnAWTEvent awtConds[] = (WakeupOnAWTEvent [])
+ wakeupOnAWTEvent.toArray();
+ int eventSize = wakeupOnAWTEvent.arraySize();
+
+ // Component Event always Enable
+ boolean focusEnable = false;
+ boolean keyEnable = false;
+ boolean mouseMotionEnable = false;
+ boolean mouseEnable = false;
+ WakeupOnAWTEvent awtCond;
+ int awtId;
+ long eventMask;
+ boolean incTimestamp = false;
+
+ for (int i=0; i < eventSize; i++) {
+ awtCond = awtConds[i];
+ awtId = awtCond.AwtId;
+ eventMask = awtCond.EventMask;
+
+ if ((awtId >= FocusEvent.FOCUS_FIRST && awtId <= FocusEvent.FOCUS_LAST) ||
+ (eventMask & AWTEvent.FOCUS_EVENT_MASK) != 0) {
+ focusEnable = true;
+ }
+ if ((awtId >= KeyEvent.KEY_FIRST && awtId <= KeyEvent.KEY_LAST) ||
+ (eventMask & AWTEvent.KEY_EVENT_MASK) != 0) {
+ keyEnable = true;
+ }
+ if ((awtId >= MouseEvent.MOUSE_FIRST) &&
+ (awtId <= MouseEvent.MOUSE_LAST)) {
+ if ((awtId == MouseEvent.MOUSE_DRAGGED) ||
+ (awtId == MouseEvent.MOUSE_MOVED)) {
+ mouseMotionEnable = true;
+ } else {
+ mouseEnable = true;
+ }
+ } else {
+ if ((eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0) {
+ mouseEnable = true;
+ }
+ if ((eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0) {
+ mouseMotionEnable = true;
+ }
+ }
+ }
+
+ if (!focusEnable && universe.enableFocus) {
+ incTimestamp = true;
+ universe.disableFocusEvents();
+ }
+ if (!VirtualUniverse.mc.isD3D() && !keyEnable && universe.enableKey) {
+ // key event use for toggle to fullscreen/window mode
+ incTimestamp = true;
+ universe.disableKeyEvents();
+ }
+ if (!mouseMotionEnable && universe.enableMouseMotion) {
+ incTimestamp = true;
+ universe.disableMouseMotionEvents();
+ }
+ if (!mouseEnable && universe.enableMouse) {
+ incTimestamp = true;
+ universe.disableMouseEvents();
+ }
+ if (incTimestamp) {
+ awtEventTimestamp++;
+ }
+ }
+ }
+
+ void removeViewPlatform(ViewPlatformRetained vp) {
+ BehaviorRetained behav;
+ int i;
+
+ viewPlatforms.remove(vp);
+
+ BehaviorRetained scheduleArr[] = (BehaviorRetained [])
+ scheduleList.toArray(false);
+
+ // handle Deactive
+ for (i=scheduleList.arraySize()-1; i >=0 ; i--) {
+ behav = scheduleArr[i];
+ // This vp may contribute to the reason that
+ // behavior is in schedule list
+ if (!intersectVPRegion(behav.transformedRegion)) {
+ removeFromScheduleList(behav);
+ }
+ }
+
+ // handle ViewPlatform Entry
+ WakeupOnViewPlatformEntry boundsEntryArr[] =
+ (WakeupOnViewPlatformEntry []) boundsEntryList.toArray(false);
+ WakeupOnViewPlatformEntry wentry;
+ ViewPlatformRetained triggeredVP;
+
+ for (i=boundsEntryList.arraySize()-1; i >=0; i--) {
+ wentry = boundsEntryArr[i];
+ // only this thread can modify wentry.transformedRegion, so
+ // no need to getWithLock()
+ triggeredVP = intersectVPCenter(wentry.transformedRegion);
+ if (triggeredVP == null) {
+ boundsEntryList.remove(wentry);
+ }
+ }
+
+ // handle ViewPlatform Exit
+ WakeupOnViewPlatformExit boundsExitArr[] =
+ (WakeupOnViewPlatformExit []) boundsExitList.toArray(false);
+ WakeupOnViewPlatformExit wexit;
+
+ for (i=boundsExitList.arraySize()-1; i >=0; i--) {
+ wexit = boundsExitArr[i];
+ // only this thread can modify wentry.transformedRegion, so
+ // no need to getWithLock()
+ triggeredVP = intersectVPCenter(wexit.transformedRegion);
+ if (triggeredVP == null) {
+ boundsExitList.remove(wexit);
+ wexit.setTriggered();
+ }
+ }
+ }
+
+ void removeBehavior(BehaviorRetained behav) {
+ behaviors.remove(behav);
+
+ if ((behav.wakeupCondition != null) &&
+ (behav.wakeupCondition.behav != null)) {
+ behav.wakeupCondition.cleanTree(this);
+ if (behav.universe == universe) {
+ behav.conditionSet = false;
+ }
+ }
+
+ // cleanup boundsEntryList
+ // since we didn't remove it on removeVPEntryCondition
+ WakeupOnViewPlatformEntry boundsEntryArr[] =
+ (WakeupOnViewPlatformEntry []) boundsEntryList.toArray(false);
+ WakeupOnViewPlatformEntry wentry;
+
+ for (int i=boundsEntryList.arraySize()-1; i>=0; i--) {
+ wentry = boundsEntryArr[i];
+ if (wentry.behav == behav) {
+ boundsEntryList.remove(wentry);
+ }
+ }
+
+ // cleanup boundsExitList
+ // since we didn't remove it on removeVPExitCondition
+ WakeupOnViewPlatformExit boundsExitArr[] =
+ (WakeupOnViewPlatformExit []) boundsExitList.toArray(false);
+ WakeupOnViewPlatformExit wexit;
+
+ for (int i=boundsExitList.arraySize()-1; i>=0; i--) {
+ wexit = boundsExitArr[i];
+ if (wexit.behav == behav) {
+ boundsExitList.remove(wexit);
+ }
+ }
+
+
+ // cleanup currentSensorEntryList
+ // since we didn't remove it on removeSensorEntryCondition
+ WakeupOnSensorEntry currentSensorEntryArr[] =
+ (WakeupOnSensorEntry []) currentSensorEntryList.toArray(false);
+ WakeupOnSensorEntry sentry;
+
+ for (int i=currentSensorEntryList.arraySize()-1; i>=0; i--) {
+ sentry = currentSensorEntryArr[i];
+ if (sentry.behav == behav) {
+ currentSensorEntryList.remove(sentry);
+ }
+ }
+
+
+ // cleanup currentSensorExitList
+ // since we didn't remove it on removeSensorExitCondition
+ WakeupOnSensorExit currentSensorExitArr[] =
+ (WakeupOnSensorExit []) currentSensorExitList.toArray(false);
+ WakeupOnSensorExit sexit;
+
+ for (int i=currentSensorExitList.arraySize()-1; i>=0; i--) {
+ sexit = currentSensorExitArr[i];
+ if (sexit.behav == behav) {
+ currentSensorExitList.remove(sexit);
+ }
+ }
+ removeFromScheduleList(behav);
+
+ }
+
+
+ void handleAWTEvent(AWTEvent evt) {
+ awtEventsBuffer.add(evt);
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.BEHAVIOR_SCHEDULER);
+ }
+
+ /**
+ * This routine takes the awt event list and gives then to the awt event
+ * conditions
+ */
+ void handleAWTEvent() {
+ WakeupOnAWTEvent awtConds[] = (WakeupOnAWTEvent [])
+ wakeupOnAWTEvent.toArray();
+ AWTEvent events[];
+ int eventSize = wakeupOnAWTEvent.arraySize();
+ int awtBufferSize;
+
+ synchronized (awtEventsBuffer) {
+ events = (AWTEvent []) awtEventsBuffer.toArray();
+ awtBufferSize = awtEventsBuffer.size();
+ awtEventsBuffer.clear();
+ }
+ WakeupOnAWTEvent awtCond;
+ AWTEvent evt;
+ int id;
+
+ for (int i=0; i < eventSize; i++) {
+ awtCond = awtConds[i];
+ for (int j=0; j < awtBufferSize; j++) {
+ evt = events[j];
+ id = evt.getID();
+
+ if (awtCond.AwtId != 0) {
+ if (awtCond.AwtId == id) {
+ // TODO: how do we clone this event (do we need to?)
+ // Bug: 4181321
+ awtCond.addAWTEvent(evt);
+ }
+ } else {
+ if (id >= ComponentEvent.COMPONENT_FIRST &&
+ id <= ComponentEvent.COMPONENT_LAST &&
+ (awtCond.EventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) {
+ awtCond.addAWTEvent(evt);
+ }
+ else if (id >= FocusEvent.FOCUS_FIRST &&
+ id <= FocusEvent.FOCUS_LAST &&
+ (awtCond.EventMask & AWTEvent.FOCUS_EVENT_MASK) != 0) {
+ awtCond.addAWTEvent(evt);
+ }
+ else if (id >= KeyEvent.KEY_FIRST &&
+ id <= KeyEvent.KEY_LAST &&
+ (awtCond.EventMask & AWTEvent.KEY_EVENT_MASK) != 0) {
+ awtCond.addAWTEvent(evt);
+ }
+ else if ((id == MouseEvent.MOUSE_CLICKED ||
+ id == MouseEvent.MOUSE_ENTERED ||
+ id == MouseEvent.MOUSE_EXITED ||
+ id == MouseEvent.MOUSE_PRESSED ||
+ id == MouseEvent.MOUSE_RELEASED) &&
+ (awtCond.EventMask & AWTEvent.MOUSE_EVENT_MASK) != 0) {
+ awtCond.addAWTEvent(evt);
+ }
+ else if ((id == MouseEvent.MOUSE_DRAGGED ||
+ id == MouseEvent.MOUSE_MOVED) &&
+ (awtCond.EventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0) {
+ awtCond.addAWTEvent(evt);
+ }
+ }
+ }
+ }
+
+
+
+ }
+
+
+ void handleBehaviorPost(Behavior behav, int postid) {
+
+ synchronized (behaviorPostBuffer) {
+ int size = behaviorPostBuffer.size();
+ if (postIDBuffer.length == size) {
+ int oldbuffer[] = postIDBuffer;
+ postIDBuffer = new int[size << 1];
+ System.arraycopy(oldbuffer, 0, postIDBuffer, 0, size);
+ }
+ postIDBuffer[size] = postid;
+ behaviorPostBuffer.add(behav);
+ }
+ VirtualUniverse.mc.sendRunMessage(universe, J3dThread.BEHAVIOR_SCHEDULER);
+ }
+
+ /**
+ * This goes through all of the criteria waiting for Behavior Posts
+ * and notifys them.
+ */
+ void handleBehaviorPost() {
+ Behavior behav;
+ int postid;
+ WakeupOnBehaviorPost wakeup;
+ WakeupOnBehaviorPost wakeupConds[] = (WakeupOnBehaviorPost [])
+ wakeupOnBehaviorPost.toArray();
+ Behavior behavArr[];
+ int behavBufferSize;
+
+ synchronized (behaviorPostBuffer) {
+ behavArr = (Behavior []) behaviorPostBuffer.toArray();
+ behavBufferSize = behaviorPostBuffer.size();
+ if (clonePostIDBuffer.length < behavBufferSize) {
+ clonePostIDBuffer = new int[behavBufferSize];
+ }
+ System.arraycopy(postIDBuffer, 0, clonePostIDBuffer, 0,
+ behavBufferSize);
+ behaviorPostBuffer.clear();
+ }
+
+ int size = wakeupOnBehaviorPost.arraySize();
+ for (int i=0; i < size; i++) {
+ wakeup = wakeupConds[i];
+ for (int j=0; j < behavBufferSize; j++) {
+ behav = behavArr[j];
+ postid = clonePostIDBuffer[j];
+ if ((wakeup.post == postid || wakeup.post == 0) &&
+ (behav == wakeup.armingBehavior || wakeup.armingBehavior == null)) {
+ wakeup.triggeringBehavior = behav;
+ wakeup.triggeringPost = postid;
+ wakeup.setTriggered();
+ }
+ }
+ }
+
+ }
+
+ /**
+ * This goes through all of the criteria waiting for Elapsed Frames
+ * and notified them.
+ */
+ void incElapsedFrames() {
+
+ WakeupOnElapsedFrames wakeupConds[] = (WakeupOnElapsedFrames [])
+ wakeupOnElapsedFrames.toArray(true);
+ int size = wakeupOnElapsedFrames.arraySize();
+ int i = 0;
+
+ while (i < size) {
+ wakeupConds[i++].newFrame();
+ }
+
+ if ( size > 0) {
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.BEHAVIOR_SCHEDULER|J3dThread.UPDATE_BEHAVIOR);
+ }
+
+ if (branchDetach) {
+ // Since this procedure may call by immediate mode user
+ // thread, we can't just clear it in removeNodes()
+ wakeupOnElapsedFrames.clearMirror();
+ branchDetach = false;
+ }
+
+ }
+
+ void removeVPEntryCondition(WakeupCondition w) {
+ wakeupOnViewPlatformEntry.remove(w);
+ // don't remove boundsEntryList, it is use next time
+ // when addVPExitCondition invoke to determine whether to
+ // trigger an event or not.
+
+ }
+
+ void addVPEntryCondition(WakeupOnViewPlatformEntry w) {
+ boolean needTrigger = true;
+
+ // see if the matching wakeupOnViewPlatformEntry
+ // condition exists & do cleanup
+ WakeupOnViewPlatformEntry boundsEntryArr[] =
+ (WakeupOnViewPlatformEntry []) boundsEntryList.toArray(false);
+ WakeupOnViewPlatformEntry wentry;
+
+ for (int i=boundsEntryList.arraySize()-1; i>=0; i--) {
+ wentry = boundsEntryArr[i];
+ if ((wentry.behav == w.behav) &&
+ (wentry.region.equals(w.region))) {
+ boundsEntryList.remove(i);
+ // Case where we wakeOr() both condition together.
+ // we should avoid calling setTrigger() every time.
+ needTrigger = false;
+ break;
+ }
+ }
+
+ wakeupOnViewPlatformEntry.add(w);
+
+ ViewPlatformRetained triggeredVP = intersectVPCenter(w.transformedRegion);
+ if (triggeredVP != null) {
+ boundsEntryList.add(w);
+ }
+
+ // we always trigger bound is inside during initialize
+ if (needTrigger && (triggeredVP != null)) {
+ w.triggeredVP = triggeredVP;
+ w.setTriggered();
+ }
+ }
+
+ void removeVPExitCondition(WakeupOnViewPlatformExit w) {
+ wakeupOnViewPlatformExit.remove(w);
+ // don't remove boundsExitList, it is use next time
+ // when addVPEntryCondition invoke to determine whether to
+ // trigger an event or not.
+ }
+
+ void addVPExitCondition(WakeupOnViewPlatformExit w) {
+ // Cleanup, since collideEntryList did not remove
+ // its condition in removeVPEntryCondition
+ boolean needTrigger = true;
+ WakeupOnViewPlatformExit boundsExitArr[] =
+ (WakeupOnViewPlatformExit []) boundsExitList.toArray(false);
+ WakeupOnViewPlatformExit wexit;
+ for (int i=boundsExitList.arraySize()-1; i>=0; i--) {
+ wexit = boundsExitArr[i];
+ if ((wexit.behav == w.behav) &&
+ (wexit.region.equals(w.region))) {
+ boundsExitList.remove(i);
+ needTrigger = false;
+ break;
+ }
+ }
+
+ ViewPlatformRetained triggeredVP = intersectVPCenter(w.transformedRegion);
+ wakeupOnViewPlatformExit.add(w);
+
+ if (triggeredVP != null) {
+ w.triggeredVP = triggeredVP;
+ boundsExitList.add(w);
+ }
+
+ if (!needTrigger) {
+ return;
+ }
+
+ // see if the matching wakeupOnViewPlatformEntry
+ // condition exists
+
+ WakeupOnViewPlatformEntry boundsEntryArr[] =
+ (WakeupOnViewPlatformEntry []) boundsEntryList.toArray(false);
+ WakeupOnViewPlatformEntry wentry;
+
+ for (int i=boundsEntryList.arraySize()-1; i>=0; i--) {
+ wentry = boundsEntryArr[i];
+ if ((wentry.behav == w.behav) &&
+ (wentry.region.equals(w.region))) {
+ // Don't remove this since if user wakeupOr()
+ // Entry and Exit condition together we may have trouble
+ // boundsEntryList.remove(i);
+ if (triggeredVP == null) {
+ w.setTriggered();
+ }
+ break;
+ }
+ }
+
+ }
+
+
+ void removeSensorEntryCondition(WakeupOnSensorEntry w) {
+ wakeupOnSensorEntry.remove(w);
+ // don't remove currentSensorEntryList, it is use next time
+ // when addSensorExitCondition invoke to determine whether to
+ // trigger an event or not.
+ }
+
+ void addSensorEntryCondition(WakeupOnSensorEntry w) {
+ boolean needTrigger = true;
+
+ // see if the matching wakeupOnSensorEntry
+ // condition exists
+ WakeupOnSensorEntry sensorEntryArr[] =
+ (WakeupOnSensorEntry []) currentSensorEntryList.toArray(false);
+ WakeupOnSensorEntry wentry;
+
+ for (int i=currentSensorEntryList.arraySize()-1; i>=0; i--) {
+ wentry = sensorEntryArr[i];
+ if ((wentry.behav == w.behav) &&
+ (wentry.region.equals(w.region))) {
+ currentSensorEntryList.remove(i);
+ needTrigger = false;
+ break;
+ }
+ }
+
+ wakeupOnSensorEntry.add(w);
+
+ w.updateTransformRegion();
+ Sensor target = sensorIntersect(w.transformedRegion);
+ if (target != null) {
+ w.setTarget(target);
+ currentSensorEntryList.add(w);
+ }
+
+ if (needTrigger && (target != null)) {
+ w.setTriggered();
+
+ }
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.UPDATE_BEHAVIOR);
+ }
+
+ void removeSensorExitCondition(WakeupOnSensorExit w) {
+ wakeupOnSensorExit.remove(w);
+ // don't remove currentSensorExitList, it is use next time
+ // when addSensorEntryCondition invoke to determine whether to
+ // trigger an event or not
+ }
+
+ void addSensorExitCondition(WakeupOnSensorExit w) {
+ // Cleanup
+ boolean needTrigger = true;
+
+ WakeupOnSensorExit currentSensorExitArr[] =
+ (WakeupOnSensorExit []) currentSensorExitList.toArray(false);
+ WakeupOnSensorExit wexit;
+ for (int i=currentSensorExitList.arraySize()-1; i>=0; i--) {
+ wexit = currentSensorExitArr[i];
+ if ((wexit.behav == w.behav) &&
+ (wexit.region.equals(w.region))) {
+ currentSensorExitList.remove(i);
+ needTrigger = false;
+ break;
+ }
+ }
+
+ w.updateTransformRegion();
+ Sensor target = sensorIntersect(w.transformedRegion);
+ wakeupOnSensorExit.add(w);
+
+ if (target != null) {
+ w.setTarget(target);
+ currentSensorExitList.add(w);
+ }
+
+ if (!needTrigger) {
+ return;
+ }
+ // see if the matching wakeupOnSensorEntry
+ // condition exists
+ WakeupOnSensorEntry sensorEntryArr[] =
+ (WakeupOnSensorEntry []) currentSensorEntryList.toArray(false);
+ WakeupOnSensorEntry wentry;
+
+ for (int i=currentSensorEntryList.arraySize()-1; i>=0; i--) {
+ wentry = sensorEntryArr[i];
+ if ((wentry.behav == w.behav) &&
+ (wentry.region.equals(w.region))) {
+ // No need to invoke currentSensorEntryList.remove(i);
+ if (target == null) {
+ w.setTriggered();
+ }
+ break;
+ }
+ }
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.UPDATE_BEHAVIOR);
+ }
+
+ void processConditionMet(BehaviorRetained behav,
+ Boolean checkSchedulingRegion) {
+
+ // Since we reuse wakeup condition, the old wakeupCondition
+ // will not reactivate again while processStimulus is running
+ // which may set another wakeupCondition.
+ // Previously we don't reuse wakeupCondition and cleanTree()
+ // everytime before calling processStimulus() so the flag
+ // inCallback is not necessary to check.
+ if (!behav.inCallback &&
+ ((checkSchedulingRegion == Boolean.FALSE) ||
+ behav.active)) {
+ processList[behav.schedulingInterval].add(behav);
+ } else {
+ if (((behav.wakeupMask &
+ BehaviorRetained.WAKEUP_TIME) != 0) &&
+ (behav.source != null) &&
+ (behav.source.isLive()) &&
+ (behav.wakeupCondition != null)) {
+ // need to add back wakeupOnElapsedTime condition
+ // to TimerThread
+ behav.wakeupCondition.reInsertElapseTimeCond();
+ }
+ }
+ }
+
+ final void processBehXformChanged(UnorderList arrList) {
+ BehaviorRetained beh;
+ Object[] nodes, nodesArr;
+
+ int size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (int i = 0; i < size; i++) {
+ nodes = (Object[])nodesArr[i];
+ for (int j=0; j<nodes.length; j++) {
+ beh = (BehaviorRetained)nodes[j];
+ beh.updateTransformRegion();
+ processBehaviorTransform(beh);
+ }
+ }
+ }
+
+ final void processVpfXformChanged(UnorderList arrList) {
+ ViewPlatformRetained vpf;
+ Object[] nodes, nodesArr;
+
+ int size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (int i = 0; i < size; i++) {
+ nodes = (Object[])nodesArr[i];
+ for (int j=0; j<nodes.length; j++) {
+ processViewPlatformTransform((ViewPlatformRetained)nodes[j]);
+ }
+ }
+ }
+
+ final void processTransformChanged(Object leaf[]) {
+ Object node;
+ int i;
+
+ // We have to process them in group rather then one by one,
+ // otherwise we may have both activation/deactivation
+ // conditions wakeup at the same time when both ViewPlatform
+ // and Behavior transform under a branch.
+
+ // Update transformRegion first
+ for (i=0; i < leaf.length; i++) {
+ node = leaf[i];
+ if (node instanceof BehaviorRetained) {
+ ((BehaviorRetained) node).updateTransformRegion();
+ processBehaviorTransform((BehaviorRetained) node);
+
+ } else if (node instanceof ViewPlatformRetained) {
+ ((ViewPlatformRetained) node).updateTransformRegion();
+ transformViewPlatformList.add(node);
+ }
+ }
+
+ // finally handle ViewPlatformRetained Transform change
+ if (transformViewPlatformList.size() > 0) {
+ ViewPlatformRetained vpArr[] = (ViewPlatformRetained [])
+ transformViewPlatformList.toArray(false);
+
+ int size = transformViewPlatformList.arraySize();
+ for (i=0; i < size; i++) {
+ processViewPlatformTransform((ViewPlatformRetained)
+ vpArr[i]);
+ }
+ transformViewPlatformList.clear();
+ }
+ }
+
+
+ // assume behav.updateTransformRegion() invoke before
+ final void processBehaviorTransform(BehaviorRetained behav) {
+ if ((behav.wakeupMask & BehaviorRetained.WAKEUP_VP_ENTRY) != 0) {
+ updateVPEntryTransformRegion(behav);
+ }
+
+ if ((behav.wakeupMask & BehaviorRetained.WAKEUP_VP_EXIT) != 0) {
+ updateVPExitTransformRegion(behav);
+ }
+
+ if (behav.active) {
+ if (!intersectVPRegion(behav.transformedRegion)) {
+ removeFromScheduleList(behav);
+ }
+ } else {
+ addToScheduleList(behav);
+ }
+ }
+
+
+ void processViewPlatformTransform(ViewPlatformRetained vp) {
+ int i;
+ BehaviorRetained behav;
+
+ vp.updateTransformRegion();
+
+ if (!vp.isActiveViewPlatform()) {
+ return;
+ }
+
+ BehaviorRetained behavArr[] = (BehaviorRetained []) behaviors.toArray(false);
+
+ // re-evaulate all behaviors affected by this vp
+ for (i=behaviors.arraySize()-1; i>=0; i--) {
+ behav = behavArr[i];
+ if (behav.active) {
+ if (!intersectVPRegion(behav.transformedRegion)) {
+ removeFromScheduleList(behav);
+ }
+ } else {
+ addToScheduleList(behav);
+ }
+ }
+
+ // handle wakeupOnViewPlatformEntry
+ WakeupOnViewPlatformEntry wakeupOnViewPlatformEntryArr[] =
+ (WakeupOnViewPlatformEntry []) wakeupOnViewPlatformEntry.toArray(false);
+ WakeupOnViewPlatformEntry wentry;
+ int idx;
+ ViewPlatformRetained triggeredVP;
+
+ for (i=wakeupOnViewPlatformEntry.arraySize()-1; i >=0; i--) {
+ wentry = wakeupOnViewPlatformEntryArr[i];
+ idx = boundsEntryList.indexOf(wentry);
+ if (idx < 0) {
+ if (wentry.transformedRegion.intersect(vp.center)) {
+ boundsEntryList.add(wentry);
+ wentry.triggeredVP = vp;
+ wentry.setTriggered();
+ }
+ } else {
+ triggeredVP = intersectVPCenter(wentry.transformedRegion);
+ if (triggeredVP == null) {
+ boundsEntryList.remove(idx);
+ }
+ }
+ }
+
+ // handle wakeupOnViewPlatformExit;
+ WakeupOnViewPlatformExit wakeupOnViewPlatformExitArr[] =
+ (WakeupOnViewPlatformExit []) wakeupOnViewPlatformExit.toArray(false);
+ WakeupOnViewPlatformExit wexit;
+
+ for (i=wakeupOnViewPlatformExit.arraySize()-1; i >=0; i--) {
+ wexit = wakeupOnViewPlatformExitArr[i];
+ idx = boundsExitList.indexOf(wexit);
+ if (idx < 0) {
+ if (wexit.transformedRegion.intersect(vp.center)) {
+ wexit.triggeredVP = vp;
+ boundsExitList.add(wexit);
+
+ }
+ } else {
+ triggeredVP = intersectVPCenter(wexit.transformedRegion);
+ if (triggeredVP == null) {
+ boundsExitList.remove(idx);
+ wexit.setTriggered();
+ }
+ }
+ }
+ }
+
+ void updateVPEntryTransformRegion(BehaviorRetained behav) {
+ WakeupOnViewPlatformEntry wakeupOnViewPlatformEntryArr[] =
+ (WakeupOnViewPlatformEntry []) wakeupOnViewPlatformEntry.toArray(false);
+ WakeupOnViewPlatformEntry wentry;
+ ViewPlatformRetained triggeredVP;
+
+ for (int i=wakeupOnViewPlatformEntry.arraySize()-1; i >=0; i--) {
+ wentry = wakeupOnViewPlatformEntryArr[i];
+ if (wentry.behav == behav) {
+ wentry.updateTransformRegion(behav);
+ int idx = boundsEntryList.indexOf(wentry);
+
+ triggeredVP = intersectVPCenter(wentry.transformedRegion);
+ if (triggeredVP != null) {
+ if (idx < 0) {
+ boundsEntryList.add(wentry);
+ wentry.triggeredVP = triggeredVP;
+ wentry.setTriggered();
+ }
+ } else {
+ if (idx >=0) {
+ boundsEntryList.remove(idx);
+ }
+ }
+ }
+
+ }
+ }
+
+
+
+ void updateVPExitTransformRegion(BehaviorRetained behav) {
+ WakeupOnViewPlatformExit wakeupOnViewPlatformExitArr[] =
+ (WakeupOnViewPlatformExit []) wakeupOnViewPlatformExit.toArray(false);
+ WakeupOnViewPlatformExit wexit;
+ ViewPlatformRetained triggeredVP;
+
+ for (int i=wakeupOnViewPlatformExit.arraySize()-1; i >=0; i--) {
+ wexit = wakeupOnViewPlatformExitArr[i];
+ if (wexit.behav == behav) {
+ wexit.updateTransformRegion(behav);
+ wexit = wakeupOnViewPlatformExitArr[i];
+ int idx = boundsExitList.indexOf(wexit);
+ triggeredVP = intersectVPCenter(wexit.transformedRegion);
+ if (triggeredVP != null) {
+ if (idx < 0) {
+ wexit.triggeredVP = triggeredVP;
+ boundsExitList.add(wexit);
+ }
+ } else {
+ if (idx >= 0) {
+ boundsExitList.remove(idx);
+ wexit.setTriggered();
+ }
+ }
+ }
+ }
+ }
+
+
+ void reEvaluatePhysicalEnvironments() {
+ // we can't just add or remove from the list since
+ // physicalEnvironment may be share by multiple view
+ View v;
+ View views[];
+ ViewPlatform vp;
+ ArrayList vpList = universe.viewPlatforms;
+
+ physicalEnvironments.clear();
+
+ for (int i=vpList.size()-1; i>=0; i--) {
+ views = ((ViewPlatformRetained) vpList.get(i)).getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ v = views[j];
+ if (v.active &&
+ !physicalEnvironments.contains(v.physicalEnvironment)) {
+ physicalEnvironments.add(v.physicalEnvironment);
+ }
+ }
+ }
+ }
+
+ void checkSensorEntryExit() {
+ int i, idx;
+ Sensor target;
+
+ // handle WakeupOnSensorEntry
+ WakeupOnSensorEntry wentry;
+ WakeupOnSensorEntry wentryArr[] = (WakeupOnSensorEntry [])
+ wakeupOnSensorEntry.toArray();
+
+ for (i=wakeupOnSensorEntry.arraySize()-1; i>=0; i--) {
+ wentry = wentryArr[i];
+ idx = currentSensorEntryList.indexOf(wentry);
+ wentry.updateTransformRegion();
+ target = sensorIntersect(wentry.transformedRegion);
+ if (target != null) {
+ if (idx < 0) {
+ currentSensorEntryList.add(wentry);
+ wentry.setTarget(target);
+ wentry.setTriggered();
+ }
+ } else {
+ if (idx >= 0) {
+ currentSensorEntryList.remove(idx);
+ }
+ }
+ }
+
+ // handle WakeupOnSensorExit
+ WakeupOnSensorExit wexit;
+ WakeupOnSensorExit wexitArr[] = (WakeupOnSensorExit [])
+ wakeupOnSensorExit.toArray();
+
+ for (i=wakeupOnSensorExit.arraySize()-1; i>=0; i--) {
+ wexit = wexitArr[i];
+ idx = currentSensorExitList.indexOf(wexit);
+ wexit.updateTransformRegion();
+ target = sensorIntersect(wexit.transformedRegion);
+ if (target != null) {
+ if (idx < 0) {
+ currentSensorExitList.add(wexit);
+ wexit.setTarget(target);
+ }
+ } else {
+ if (idx >= 0) {
+ currentSensorExitList.remove(idx);
+ wexit.setTriggered();
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * return the Senor that intersect with behregion or null
+ */
+ Sensor sensorIntersect(Bounds behregion) {
+
+ if (behregion == null)
+ return null;
+
+ PhysicalEnvironment env[] = (PhysicalEnvironment [])
+ physicalEnvironments.toArray(false);
+ Sensor sensors[];
+ Sensor s;
+ View v;
+ for (int i=physicalEnvironments.arraySize()-1; i>=0; i--) {
+ if (env[i].activeViewRef > 0) {
+ sensors = env[i].getSensorList();
+ if (sensors != null) {
+ for (int j= env[i].users.size()-1; j>=0; j--) {
+ v = (View) env[i].users.get(j);
+ synchronized (sensors) {
+ for (int k=sensors.length-1; k >=0; k--) {
+ s = sensors[k];
+ if (s != null) {
+ v.getSensorToVworld(s, sensorTransform);
+ sensorTransform.get(sensorLoc);
+ ptSensorLoc.set(sensorLoc);
+ if (behregion.intersect(ptSensorLoc)) {
+ return s;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * return true if one of ViewPlatforms intersect behregion
+ */
+ final boolean intersectVPRegion(Bounds behregion) {
+ if (behregion == null) {
+ return false;
+ }
+
+ ViewPlatformRetained vp;
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+
+ for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) {
+ vp = vpLists[i];
+ if (vp.isActiveViewPlatform() &&
+ vp.schedSphere.intersect(behregion)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * return true if one of ViewPlatforms center intersect behregion
+ */
+ final ViewPlatformRetained intersectVPCenter(Bounds behregion) {
+ if (behregion == null) {
+ return null;
+ }
+
+ ViewPlatformRetained vp;
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+
+
+ for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) {
+ vp = vpLists[i];
+ if (vp.isActiveViewPlatform() &&
+ behregion.intersect(vp.center)) {
+ return vp;
+ }
+ }
+ return null;
+ }
+
+ void notifyDeactivationCondition(BehaviorRetained behav) {
+ WakeupOnDeactivation wakeup;
+ WakeupOnDeactivation wakeupConds[] = (WakeupOnDeactivation [])
+ wakeupOnDeactivation.toArray(false);
+
+ for (int i=wakeupOnDeactivation.arraySize()-1; i>=0; i--) {
+ wakeup = wakeupConds[i];
+ if (wakeup.behav == behav) {
+ wakeup.setTriggered();
+ }
+ }
+ }
+
+ void notifyActivationCondition(BehaviorRetained behav) {
+ WakeupOnActivation wakeup;
+ WakeupOnActivation wakeupConds[] = (WakeupOnActivation [])
+ wakeupOnActivation.toArray(false);
+
+ for (int i=wakeupOnActivation.arraySize()-1; i>=0; i--) {
+ wakeup = wakeupConds[i];
+ if (wakeup.behav == behav) {
+ wakeup.setTriggered();
+ }
+ }
+ }
+
+
+ void processSwitchChanged(J3dMessage m) {
+
+ int i,j;
+ UnorderList arrList;
+ int size;
+ Object[] nodes, nodesArr;
+
+ UpdateTargets targets = (UpdateTargets)m.args[0];
+ arrList = targets.targetList[Targets.VPF_TARGETS];
+
+ if (arrList != null) {
+ ViewPlatformRetained vp;
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (j=0; j<size; j++) {
+ nodes = (Object[])nodesArr[j];
+ for (i=nodes.length-1; i>=0; i--) {
+ vp = (ViewPlatformRetained) nodes[i];
+ vp.processSwitchChanged();
+ }
+ }
+ }
+
+ arrList = targets.targetList[Targets.BEH_TARGETS];
+
+ if (arrList != null) {
+ BehaviorRetained behav;
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (j=0; j<size; j++) {
+ nodes = (Object[])nodesArr[j];
+ for (i=nodes.length-1; i>=0; i--) {
+ behav = (BehaviorRetained) nodes[i];
+ if (behav.switchState.currentSwitchOn) {
+ addToScheduleList(behav);
+ } else {
+ removeFromScheduleList(behav);
+ }
+ }
+ }
+ }
+
+ arrList = targets.targetList[Targets.BLN_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+ Object[] objArr = (Object[])m.args[1];
+ Object[] obj;
+ BoundingLeafRetained mbleaf;
+
+ for (int h=0; h<size; h++) {
+ nodes = (Object[])nodesArr[h];
+ obj = (Object[])objArr[h];
+ for (i=nodes.length-1; i>=0; i--) {
+
+ Object[] users = (Object[])obj[i];
+ Object[] leafObj = new Object[1];
+ mbleaf = (BoundingLeafRetained)nodes[i];
+ for (j = 0; j < users.length; j++) {
+ if (users[j] instanceof BehaviorRetained) {
+ leafObj[0] = users[j];
+ processTransformChanged(leafObj);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void processBoundingLeafChanged(Object users[],
+ Bounds bound) {
+ Object leaf;
+ BehaviorRetained behav;
+
+ for (int i=users.length-1; i>=0; i--) {
+ leaf = users[i];
+ if (leaf instanceof BehaviorRetained) {
+ behav = (BehaviorRetained) leaf;
+ behav.updateTransformRegion(bound);
+ processBehaviorTransform(behav);
+ }
+ }
+ }
+
+ final void removeFromScheduleList(BehaviorRetained behav) {
+ if (behav.active) {
+ if ((behav.wakeupMask &
+ BehaviorRetained.WAKEUP_DEACTIVATE) != 0) {
+ notifyDeactivationCondition(behav);
+ }
+ scheduleList.remove(behav);
+ behav.active = false;
+ if (behav.universe != universe) {
+ J3dMessage m = VirtualUniverse.mc.getMessage();
+ m.threads = J3dThread.UPDATE_BEHAVIOR;
+ m.type = J3dMessage.BEHAVIOR_REEVALUATE;
+ m.universe = behav.universe;
+ m.args[0] = behav;
+ VirtualUniverse.mc.processMessage(m);
+ }
+ }
+ }
+
+ final void addToScheduleList(BehaviorRetained behav) {
+
+ if (!behav.inCallback &&
+ !behav.active &&
+ behav.enable &&
+ behav.switchState.currentSwitchOn &&
+ (behav.wakeupCondition != null) &&
+ ((Behavior) behav.source).isLive() &&
+ intersectVPRegion(behav.transformedRegion)) {
+
+ scheduleList.add(behav);
+ behav.active = true;
+ if ((behav.wakeupMask &
+ BehaviorRetained.WAKEUP_ACTIVATE) != 0) {
+ notifyActivationCondition(behav);
+ }
+
+ if (behav.wakeupCondition != null) {
+ // This reset the conditionMet, otherwise
+ // if conditionMet is true then WakeupCondition
+ // will never post message to BehaviorStructure
+ behav.wakeupCondition.conditionMet = false;
+ }
+ }
+ }
+
+ /**
+ * This prevents wakeupCondition sent out message and sets
+ * conditionMet to true, but the
+ * BehaviorStructure/BehaviorScheduler is not fast enough to
+ * process the message and reset conditionMet to false
+ * when view deactivate/unregister.
+ */
+ void resetConditionMet() {
+ resetConditionMet(wakeupOnAWTEvent);
+ resetConditionMet(wakeupOnActivation);
+ resetConditionMet(wakeupOnDeactivation);
+ resetConditionMet(wakeupOnBehaviorPost);
+ resetConditionMet(wakeupOnElapsedFrames);
+ resetConditionMet(wakeupOnViewPlatformEntry);
+ resetConditionMet(wakeupOnViewPlatformExit);
+ resetConditionMet(wakeupOnSensorEntry);
+ resetConditionMet(wakeupOnSensorExit);
+ }
+
+ static void resetConditionMet(WakeupIndexedList list) {
+ WakeupCondition wakeups[] = (WakeupCondition []) list.toArray(false);
+ int i = list.size()-1;
+ while (i >= 0) {
+ wakeups[i--].conditionMet = false;
+ }
+ }
+
+ void reEvaluateWakeupCount() {
+ WakeupOnElapsedFrames wakeupConds[] = (WakeupOnElapsedFrames [])
+ wakeupOnElapsedFrames.toArray(true);
+ int size = wakeupOnElapsedFrames.arraySize();
+ int i = 0;
+ WakeupOnElapsedFrames cond;
+
+ activeWakeupOnFrameCount = 0;
+
+ while (i < size) {
+ cond = wakeupConds[i++];
+ if (!cond.passive &&
+ (cond.behav != null) &&
+ cond.behav.enable) {
+ activeWakeupOnFrameCount++;
+ }
+ }
+
+
+ activeWakeupOnSensorCount = 0;
+ WakeupOnSensorEntry wentry;
+ WakeupOnSensorEntry wentryArr[] = (WakeupOnSensorEntry [])
+ wakeupOnSensorEntry.toArray();
+
+ for (i=wakeupOnSensorEntry.arraySize()-1; i>=0; i--) {
+ wentry = wentryArr[i];
+ if ((wentry.behav != null) &&
+ (wentry.behav.enable)) {
+ activeWakeupOnSensorCount++;
+ }
+ }
+
+ WakeupOnSensorExit wexit;
+ WakeupOnSensorExit wexitArr[] = (WakeupOnSensorExit [])
+ wakeupOnSensorExit.toArray();
+
+ for (i=wakeupOnSensorExit.arraySize()-1; i>=0; i--) {
+ wexit = wexitArr[i];
+ if ((wexit.behav != null) &&
+ (wexit.behav.enable)) {
+ activeWakeupOnSensorCount++;
+ }
+ }
+
+ }
+
+ void cleanup() {
+ behaviors.clear();
+ viewPlatforms.clear();
+ scheduleList.clear();
+ boundsEntryList.clear();
+ boundsExitList.clear();
+ currentSensorEntryList.clear();
+ currentSensorExitList.clear();
+ wakeupOnAWTEvent.clear();
+ wakeupOnActivation.clear();
+ wakeupOnDeactivation.clear();
+ wakeupOnBehaviorPost.clear();
+ wakeupOnElapsedFrames.clear();
+ wakeupOnViewPlatformEntry.clear();
+ wakeupOnViewPlatformExit.clear();
+ wakeupOnSensorEntry.clear();
+ wakeupOnSensorExit.clear();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Billboard.java b/src/classes/share/javax/media/j3d/Billboard.java
new file mode 100644
index 0000000..34e262c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Billboard.java
@@ -0,0 +1,661 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+import javax.vecmath.Point3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Vector3f;
+import javax.vecmath.AxisAngle4d;
+
+/**
+ * The Billboard behavior node operates on the TransformGroup node
+ * to cause the local +z axis of the TransformGroup to point at
+ * the viewer's eye position. This is done regardless of the transforms
+ * above the specified TransformGroup node in the scene graph.
+ *
+ * <p>
+ * If the alignment mode is ROTATE_ABOUT_AXIS, the rotation will be
+ * around the specified axis. If the alignment mode is
+ * ROTATE_ABOUT_POINT, the rotation will be about the specified
+ * point, with an additional rotation to align the +y axis of the
+ * TransformGroup with the +y axis in the View.
+ *
+ * <p>
+ * Note that in a multiple View system, the alignment is done to
+ * the primary View only.
+ *
+ * <p>
+ * Billboard nodes are ideal for drawing screen aligned-text or
+ * for drawing roughly-symmetrical objects. A typical use might
+ * consist of a quadrilateral that contains a texture of a tree.
+ *
+ * @see OrientedShape3D
+ */
+public class Billboard extends Behavior {
+ /**
+ * Specifies that rotation should be about the specified axis.
+ */
+ public static final int ROTATE_ABOUT_AXIS = 0;
+
+ /**
+ * Specifies that rotation should be about the specified point and
+ * that the children's Y-axis should match the view object's Y-axis.
+ */
+ public static final int ROTATE_ABOUT_POINT = 1;
+
+ // Wakeup condition for Billboard node
+ WakeupOnElapsedFrames wakeupFrame = new WakeupOnElapsedFrames(0, true);
+
+
+ // Specifies the billboard's mode of operation. One of ROTATE_AXIAL,
+ // ROTATE_POINT_VIEW, or ROTATE_POINT_WORLD.
+ int mode = ROTATE_ABOUT_AXIS;
+
+ // Axis about which to rotate.
+ Vector3f axis = new Vector3f(0.0f, 1.0f, 0.0f);
+ Point3f rotationPoint = new Point3f(0.0f, 0.0f, 1.0f);
+ private Vector3d nAxis = new Vector3d(0.0, 1.0, 0.0); // normalized axis
+
+ // TransformGroup to operate on.
+ TransformGroup tg = null;
+
+
+ // reused temporaries
+ private Point3d viewPosition = new Point3d();
+ private Point3d yUpPoint = new Point3d();
+
+ private Vector3d eyeVec = new Vector3d();
+ private Vector3d yUp = new Vector3d();
+ private Vector3d zAxis = new Vector3d();
+ private Vector3d yAxis = new Vector3d();
+ private Vector3d vector = new Vector3d();
+
+ private AxisAngle4d aa = new AxisAngle4d();
+
+ static final double EPSILON = 1.0e-6;
+
+ /**
+ * Constructs a Billboard node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * alignment mode : ROTATE_ABOUT_AXIS<br>
+ * alignment axis : Y-axis (0,1,0)<br>
+ * rotation point : (0,0,1)<br>
+ * target transform group: null<br>
+ *</ul>
+ */
+ public Billboard() {
+ nAxis.x = 0.0;
+ nAxis.y = 1.0;
+ nAxis.z = 0.0;
+ }
+
+
+ /**
+ * Constructs a Billboard node with default parameters that operates
+ * on the specified TransformGroup node.
+ * The default alignment mode is ROTATE_ABOUT_AXIS rotation with the axis
+ * pointing along the Y axis.
+ * @param tg the TransformGroup node that this Billboard
+ * node operates upon
+ */
+ public Billboard(TransformGroup tg) {
+ this.tg = tg;
+ nAxis.x = 0.0;
+ nAxis.y = 1.0;
+ nAxis.z = 0.0;
+
+ }
+
+
+ /**
+ * Constructs a Billboard node with the specified axis and mode
+ * that operates on the specified TransformGroup node.
+ * The specified axis must not be parallel to the <i>Z</i>
+ * axis--(0,0,<i>z</i>) for any value of <i>z</i>. It is not
+ * possible for the +<i>Z</i> axis to point at the viewer's eye
+ * position by rotating about itself. The target transform will
+ * be set to the identity if the axis is (0,0,<i>z</i>).
+ *
+ * @param tg the TransformGroup node that this Billboard
+ * node operates upon
+ * @param mode alignment mode, one of ROTATE_ABOUT_AXIS or
+ * ROTATE_ABOUT_POINT
+ * @param axis the ray about which the billboard rotates
+ */
+ public Billboard(TransformGroup tg, int mode, Vector3f axis) {
+ this.tg = tg;
+ this.mode = mode;
+ this.axis.set(axis);
+ double invMag;
+ invMag = 1.0/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
+ nAxis.x = (double)axis.x*invMag;
+ nAxis.y = (double)axis.y*invMag;
+ nAxis.z = (double)axis.z*invMag;
+
+ }
+
+ /**
+ * Constructs a Billboard node with the specified rotation point and mode
+ * that operates on the specified TransformGroup node.
+ * @param tg the TransformGroup node that this Billboard
+ * node operates upon
+ * @param mode alignment mode, one of ROTATE_ABOUT_AXIS or
+ * ROTATE_ABOUT_POINT
+ * @param point the position about which the billboard rotates
+ */
+ public Billboard(TransformGroup tg, int mode, Point3f point) {
+ this.tg = tg;
+ this.mode = mode;
+ this.rotationPoint.set(point);
+ }
+
+ /**
+ * Sets the alignment mode.
+ * @param mode one of: ROTATE_ABOUT_AXIS or ROTATE_ABOUT_POINT
+ */
+ public void setAlignmentMode(int mode) {
+ this.mode = mode;
+ }
+
+
+ /**
+ * Gets the alignment mode.
+ * @return one of: ROTATE_ABOUT_AXIS or ROTATE_ABOUT_POINT
+ */
+ public int getAlignmentMode() {
+ return this.mode;
+ }
+
+
+ /**
+ * Sets the alignment axis.
+ * The specified axis must not be parallel to the <i>Z</i>
+ * axis--(0,0,<i>z</i>) for any value of <i>z</i>. It is not
+ * possible for the +<i>Z</i> axis to point at the viewer's eye
+ * position by rotating about itself. The target transform will
+ * be set to the identity if the axis is (0,0,<i>z</i>).
+ *
+ * @param axis the ray about which the billboard rotates
+ */
+ public void setAlignmentAxis(Vector3f axis) {
+ this.axis.set(axis);
+ double invMag;
+ invMag = 1.0/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
+ nAxis.x = (double)axis.x*invMag;
+ nAxis.y = (double)axis.y*invMag;
+ nAxis.z = (double)axis.z*invMag;
+
+ }
+
+
+ /**
+ * Sets the alignment axis.
+ * The specified axis must not be parallel to the <i>Z</i>
+ * axis--(0,0,<i>z</i>) for any value of <i>z</i>. It is not
+ * possible for the +<i>Z</i> axis to point at the viewer's eye
+ * position by rotating about itself. The target transform will
+ * be set to the identity if the axis is (0,0,<i>z</i>).
+ *
+ * @param x the x component of the ray about which the billboard rotates
+ * @param y the y component of the ray about which the billboard rotates
+ * @param z the z component of the ray about which the billboard rotates
+ */
+ public void setAlignmentAxis(float x, float y, float z) {
+ this.axis.set(x, y, z);
+ this.axis.set(axis);
+ double invMag;
+ invMag = 1.0/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
+ nAxis.x = (double)axis.x*invMag;
+ nAxis.y = (double)axis.y*invMag;
+ nAxis.z = (double)axis.z*invMag;
+
+ }
+
+
+ /**
+ * Gets the alignment axis and sets the parameter to this value.
+ * @param axis the vector that will contain the ray about which
+ * the billboard rotates
+ */
+ public void getAlignmentAxis(Vector3f axis) {
+ axis.set(this.axis);
+ }
+
+ /**
+ * Sets the rotation point.
+ * @param point the point about which the billboard rotates
+ */
+ public void setRotationPoint(Point3f point) {
+ this.rotationPoint.set(point);
+ }
+
+
+ /**
+ * Sets the rotation point.
+ * @param x the x component of the point about which the billboard rotates
+ * @param y the y component of the point about which the billboard rotates
+ * @param z the z component of the point about which the billboard rotates
+ */
+ public void setRotationPoint(float x, float y, float z) {
+ this.rotationPoint.set(x, y, z);
+ }
+
+
+ /**
+ * Gets the rotation point and sets the parameter to this value.
+ * @param point the position the Billboard rotates about
+ */
+ public void getRotationPoint(Point3f point) {
+ point.set(this.rotationPoint);
+ }
+ /**
+ * Sets the tranformGroup for this Billboard object.
+ * @param tg the transformGroup node which replaces the current
+ * transformGroup node for this Billboard
+ */
+ public void setTarget(TransformGroup tg ) {
+ this.tg = tg;
+ }
+
+ /**
+ * Returns a copy of the transformGroup associated with this Billboard.
+ * @return the TranformGroup for this Billboard
+ */
+ public TransformGroup getTarget() {
+ return(tg);
+ }
+
+
+ /**
+ * Initialize method that sets up initial wakeup criteria.
+ */
+ public void initialize() {
+ // Insert wakeup condition into queue
+ wakeupOn(wakeupFrame);
+ }
+
+
+ /**
+ * Process stimulus method that computes appropriate transform.
+ * @param criteria an enumeration of the criteria that caused the
+ * stimulus
+ */
+ public void processStimulus(Enumeration criteria) {
+ double angle = 0.0;
+ double mag,sign;
+ double tx,ty,tz;
+
+ if( tg == null ){
+ wakeupOn(wakeupFrame);
+ return;
+ }
+
+ // get viewplatforms's location in virutal world
+ View v = this.getView();
+ if( v == null ) {
+ wakeupOn(wakeupFrame);
+ return;
+ }
+ Canvas3D canvas = (Canvas3D)v.getCanvas3D(0);
+ boolean status;
+
+ Transform3D xform = VirtualUniverse.mc.getTransform3D(null);
+ Transform3D bbXform = VirtualUniverse.mc.getTransform3D(null);
+ Transform3D prevTransform = VirtualUniverse.mc.getTransform3D(null);
+
+ ((TransformGroupRetained) tg.retained).getTransform(prevTransform);
+
+ if (mode == ROTATE_ABOUT_AXIS ) { // rotate about axis
+ canvas.getCenterEyeInImagePlate(viewPosition);
+ canvas.getImagePlateToVworld(xform); // xform is imagePlateToLocal
+ xform.transform(viewPosition);
+
+ // get billboard's transform
+
+ // since we are using getTransform() to get the transform
+ // of the transformGroup, we need to use getLocalToVworld()
+ // to get the localToVworld which includes the static transform
+
+ ((NodeRetained)tg.retained).getLocalToVworld(xform);
+
+ xform.invert(); // xform is now vWorldToLocal
+
+ // transform the eye position into the billboard's coordinate system
+ xform.transform(viewPosition);
+
+ // eyeVec is a vector from the local origin to the eye pt in local
+ eyeVec.set(viewPosition);
+ eyeVec.normalize();
+
+ // project the eye into the rotation plane
+ status = projectToPlane(eyeVec, nAxis);
+
+ // If the first project was successful ..
+ if (status) {
+ // project the z axis into the rotation plane
+ zAxis.x = 0.0;
+ zAxis.y = 0.0;
+ zAxis.z = 1.0;
+ status = projectToPlane(zAxis, nAxis);
+ }
+
+
+ ((TransformGroupRetained) tg.retained).getTransform(xform);
+ if (status) {
+ // compute the sign of the angle by checking if the cross product
+ // of the two vectors is in the same direction as the normal axis
+ vector.cross(eyeVec, zAxis);
+ if (vector.dot(nAxis) > 0.0) {
+ sign = 1.0;
+ } else {
+ sign = -1.0;
+ }
+
+ // compute the angle between the projected eye vector and the
+ // projected z
+ double dot = eyeVec.dot(zAxis);
+
+ if (dot > 1.0f) {
+ dot = 1.0f;
+ } else if (dot < -1.0f) {
+ dot = -1.0f;
+ }
+
+ angle = sign*Math.acos(dot);
+
+ // use -angle because xform is to *undo* rotation by angle
+ aa.x = nAxis.x;
+ aa.y = nAxis.y;
+ aa.z = nAxis.z;
+ aa.angle = -angle;
+ bbXform.set(aa);
+ if( !prevTransform.epsilonEquals(bbXform, EPSILON)) {
+ // Optimization for Billboard since it use passive
+ // behavior
+ // set the transform on the Billboard TG
+ tg.setTransform(bbXform);
+ }
+ }
+ else {
+ bbXform.setIdentity();
+ if (!prevTransform.epsilonEquals(bbXform, EPSILON)) {
+ tg.setTransform(bbXform);
+ }
+ }
+
+ } else { // rotate about point
+ // Need to rotate Z axis to point to eye, and Y axis to be
+ // parallel to view platform Y axis, rotating around rotation pt
+
+ Transform3D zRotate = VirtualUniverse.mc.getTransform3D(null);
+
+ // get the eye point
+ canvas.getCenterEyeInImagePlate(viewPosition);
+
+ // derive the yUp point
+ yUpPoint.set(viewPosition);
+ yUpPoint.y += 0.01; // one cm in Physical space
+
+ // transform the points to the Billboard's space
+ canvas.getImagePlateToVworld(xform); // xform is ImagePlateToVworld
+
+ xform.transform(viewPosition);
+ xform.transform(yUpPoint);
+
+ // get billboard's transform
+
+ // since we are using getTransform() to get the transform
+ // of the transformGroup, we need to use getLocalToVworld()
+ // to get the localToVworld which includes the static transform
+
+ ((NodeRetained)tg.retained).getLocalToVworld(xform);
+
+ xform.invert(); // xform is vWorldToLocal
+
+ // transfom points to local coord sys
+ xform.transform(viewPosition);
+ xform.transform(yUpPoint);
+
+ // Make a vector from viewPostion to 0,0,0 in the BB coord sys
+ eyeVec.set(viewPosition);
+ eyeVec.normalize();
+
+ // create a yUp vector
+ yUp.set(yUpPoint);
+ yUp.sub(viewPosition);
+ yUp.normalize();
+
+
+ // find the plane to rotate z
+ zAxis.x = 0.0;
+ zAxis.y = 0.0;
+ zAxis.z = 1.0;
+
+ // rotation axis is cross product of eyeVec and zAxis
+ vector.cross(eyeVec, zAxis); // vector is cross product
+
+ // if cross product is non-zero, vector is rotation axis and
+ // rotation angle is acos(eyeVec.dot(zAxis)));
+ double length = vector.length();
+
+ if (length > 0.0001) {
+ double dot = eyeVec.dot(zAxis);
+
+ if (dot > 1.0f) {
+ dot = 1.0f;
+ } else if (dot < -1.0f) {
+ dot = -1.0f;
+ }
+
+ angle = Math.acos(dot);
+ aa.x = vector.x;
+ aa.y = vector.y;
+ aa.z = vector.z;
+ aa.angle = -angle;
+ zRotate.set(aa);
+ } else {
+ // no rotation needed, set to identity (scale = 1.0)
+ zRotate.set(1.0);
+ }
+
+ // Transform the yAxis by zRotate
+ yAxis.x = 0.0;
+ yAxis.y = 1.0;
+ yAxis.z = 0.0;
+ zRotate.transform(yAxis);
+
+ // project the yAxis onto the plane perp to the eyeVec
+ status = projectToPlane(yAxis, eyeVec);
+
+ if (status) {
+ // project the yUp onto the plane perp to the eyeVec
+ status = projectToPlane(yUp, eyeVec);
+ }
+
+ ((TransformGroupRetained) tg.retained).getTransform(xform);
+ if (status) {
+ // rotation angle is acos(yUp.dot(yAxis));
+ double dot = yUp.dot(yAxis);
+
+ // Fix numerical error, otherwise acos return NULL
+ if (dot > 1.0f) {
+ dot = 1.0f;
+ } else if (dot < -1.0f) {
+ dot = -1.0f;
+ }
+
+ angle = Math.acos(dot);
+
+ // check the sign by looking a the cross product vs the eyeVec
+ vector.cross(yUp, yAxis); // vector is cross product
+ if (eyeVec.dot(vector) < 0) {
+ angle *= -1;
+ }
+ aa.x = eyeVec.x;
+ aa.y = eyeVec.y;
+ aa.z = eyeVec.z;
+ aa.angle = -angle;
+
+ xform.set(aa); // xform is now yRotate
+
+ // rotate around the rotation point
+ vector.x = rotationPoint.x;
+ vector.y = rotationPoint.y;
+ vector.z = rotationPoint.z; // vector to translate to RP
+ bbXform.set(vector); // translate to RP
+ bbXform.mul(xform); // yRotate
+ bbXform.mul(zRotate); // zRotate
+ vector.scale(-1.0); // vector to translate back
+ xform.set(vector); // xform to translate back
+ bbXform.mul(xform); // translate back
+
+
+ if (!prevTransform.epsilonEquals(bbXform, EPSILON)) {
+ // set the transform on the Billboard TG
+ tg.setTransform(bbXform);
+ }
+ }
+ else {
+ bbXform.setIdentity();
+ if (!prevTransform.epsilonEquals(bbXform, EPSILON)) {
+ tg.setTransform(bbXform);
+ }
+ }
+ VirtualUniverse.mc.addToTransformFreeList(zRotate);
+ }
+
+ // Insert wakeup condition into queue
+ wakeupOn(wakeupFrame);
+
+ VirtualUniverse.mc.addToTransformFreeList(xform);
+ VirtualUniverse.mc.addToTransformFreeList(bbXform);
+ VirtualUniverse.mc.addToTransformFreeList(prevTransform);
+}
+
+private boolean projectToPlane(Vector3d projVec, Vector3d planeVec) {
+ double dis = planeVec.dot(projVec);
+ projVec.x = projVec.x - planeVec.x*dis;
+ projVec.y = projVec.y - planeVec.y*dis;
+ projVec.z = projVec.z - planeVec.z*dis;
+
+ double length = projVec.length();
+
+ if (length < EPSILON) {
+ return false;
+ }
+ projVec.scale(1 / length);
+ return true;
+}
+
+ /**
+ * Creates 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) {
+ Billboard b = new Billboard();
+ b.duplicateNode(this, forceDuplicate);
+ return b;
+ }
+
+
+ /**
+ * Copies all Billboard 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ Billboard bb = (Billboard) originalNode;
+
+ setAlignmentMode(bb.getAlignmentMode());
+
+ Vector3f v = new Vector3f();
+ bb.getAlignmentAxis(v);
+ setAlignmentAxis(v);
+
+ Point3f p = new Point3f();
+ bb.getRotationPoint(p);
+ setRotationPoint(p);
+
+ // this will be updated by updateNodeReferences() later
+ setTarget(bb.getTarget());
+ }
+
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ // check for new TransformGroup
+ TransformGroup g = getTarget();
+ if (g != null) {
+ setTarget((TransformGroup) referenceTable.getNewObjectReference(g));
+ }
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/BoundingBox.java b/src/classes/share/javax/media/j3d/BoundingBox.java
new file mode 100644
index 0000000..a6d8f4f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BoundingBox.java
@@ -0,0 +1,1975 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * This class defines an axis aligned bounding box which is used for
+ * bounding regions.
+ *
+ */
+
+public class BoundingBox extends Bounds {
+
+ /**
+ * The corner of the bounding box with the numerically smallest
+ * values.
+ */
+ Point3d lower;
+
+ /**
+ * The corner of the bounding box with the numerically largest
+ * values.
+ */
+ Point3d upper;
+
+ private Point3d centroid = null;
+ private static final double EPS = 1.0E-8;
+
+ // reusable temp objects
+ private BoundingSphere tmpSphere = null;
+ private BoundingBox tmpBox = null;
+ private BoundingPolytope tmpPolytope = null;
+ private Point3d tmpP3d = new Point3d();
+
+
+ /**
+ * Constructs and initializes a BoundingBox given min,max in x,y,z.
+ * @param lower the "small" corner
+ * @param upper the "large" corner
+ */
+ public BoundingBox(Point3d lower, Point3d upper) {
+ boundId = BOUNDING_BOX;
+ this.lower = new Point3d(lower);
+ this.upper = new Point3d(upper);
+ updateBoundsStates();
+ }
+
+ /**
+ * Constructs and initializes a 2X bounding box about the
+ * origin. The lower corner is initialized to (-1.0d, -1.0d, -1.0d)
+ * and the opper corner is initialized to (1.0d, 1.0d, 1.0d).
+ */
+ public BoundingBox() {
+ boundId = BOUNDING_BOX;
+ lower = new Point3d(-1.0d, -1.0d, -1.0d);
+ upper = new Point3d( 1.0d, 1.0d, 1.0d);
+ updateBoundsStates();
+ }
+
+ /**
+ * Constructs a BoundingBox from a bounding object.
+ * @param boundsObject a bounds object
+ */
+ public BoundingBox(Bounds boundsObject) {
+ int i;
+ boundId = BOUNDING_BOX;
+ if( boundsObject == null ) {
+ // Negative volume.
+ lower = new Point3d( 1.0d, 1.0d, 1.0d);
+ upper = new Point3d(-1.0d, -1.0d, -1.0d);
+ }
+ else if( boundsObject.boundsIsInfinite ) {
+ lower = new Point3d( Double.NEGATIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ Double.NEGATIVE_INFINITY);
+ upper = new Point3d(Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY);
+ }
+ else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+
+ lower = new Point3d(box.lower.x, box.lower.y, box.lower.z);
+ upper = new Point3d(box.upper.x, box.upper.y, box.upper.z);
+
+ }
+ else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+
+ lower = new Point3d(sphere.center.x-sphere.radius,
+ sphere.center.y-sphere.radius,
+ sphere.center.z-sphere.radius);
+
+ upper = new Point3d(sphere.center.x+sphere.radius,
+ sphere.center.y+sphere.radius,
+ sphere.center.z+sphere.radius);
+
+ }
+ else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ if( polytope.nVerts < 1 ) { // handle degenerate case
+ lower = new Point3d(-1.0d, -1.0d, -1.0d);
+ upper = new Point3d( 1.0d, 1.0d, 1.0d);
+ } else {
+ lower = new Point3d( polytope.verts[0].x, polytope.verts[0].y,
+ polytope.verts[0].z);
+ upper = new Point3d( polytope.verts[0].x, polytope.verts[0].y,
+ polytope.verts[0].z);
+
+ for(i=1;i<polytope.nVerts;i++) {
+ if( polytope.verts[i].x < lower.x )
+ lower.x = polytope.verts[i].x;
+ if( polytope.verts[i].y < lower.y )
+ lower.y = polytope.verts[i].y;
+ if( polytope.verts[i].z < lower.z )
+ lower.z = polytope.verts[i].z;
+ if( polytope.verts[i].x > upper.x )
+ upper.x = polytope.verts[i].x;
+ if( polytope.verts[i].y > upper.y )
+ upper.y = polytope.verts[i].y;
+ if( polytope.verts[i].z > upper.z )
+ upper.z = polytope.verts[i].z;
+ }
+ }
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox0"));
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Constructs a BoundingBox from an array of bounding objects.
+ * @param bounds an array of bounding objects
+ */
+ public BoundingBox(Bounds[] bounds) {
+ int i=0;
+
+ upper = new Point3d();
+ lower = new Point3d();
+ boundId = BOUNDING_BOX;
+
+ if( bounds == null || bounds.length <= 0 ) {
+ // Negative volume.
+ lower = new Point3d( 1.0d, 1.0d, 1.0d);
+ upper = new Point3d(-1.0d, -1.0d, -1.0d);
+ updateBoundsStates();
+ return;
+ }
+
+ // find first non empty bounds object
+ while( bounds[i] == null && i < bounds.length) {
+ i++;
+ }
+
+ if( i >= bounds.length ) { // all bounds objects were empty
+ // Negative volume.
+ lower = new Point3d( 1.0d, 1.0d, 1.0d);
+ upper = new Point3d(-1.0d, -1.0d, -1.0d);
+ updateBoundsStates();
+ return;
+ }
+
+ this.set(bounds[i++]);
+ if(boundsIsInfinite)
+ return;
+
+ for(;i<bounds.length;i++) {
+ if( bounds[i] == null ); // do nothing
+ else if( bounds[i].boundsIsEmpty); // do nothing
+ else if( bounds[i].boundsIsInfinite ) {
+ lower.x = lower.y = lower.z = Double.NEGATIVE_INFINITY;
+ upper.x = upper.y = upper.z = Double.POSITIVE_INFINITY;
+ break; // We're done.
+ }
+ else if(bounds[i].boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)bounds[i];
+
+ if( lower.x > box.lower.x) lower.x = box.lower.x;
+ if( lower.y > box.lower.y) lower.y = box.lower.y;
+ if( lower.z > box.lower.z) lower.z = box.lower.z;
+ if( upper.x < box.upper.x) upper.x = box.upper.x;
+ if( upper.y < box.upper.y) upper.y = box.upper.y;
+ if( upper.z < box.upper.z) upper.z = box.upper.z;
+
+ }
+ else if(bounds[i].boundId == BOUNDING_SPHERE) {
+ BoundingSphere sphere = (BoundingSphere)bounds[i];
+ if( lower.x > (sphere.center.x - sphere.radius))
+ lower.x = sphere.center.x - sphere.radius;
+ if( lower.y > (sphere.center.y - sphere.radius))
+ lower.y = sphere.center.y - sphere.radius;
+ if( lower.z > (sphere.center.z - sphere.radius))
+ lower.z = sphere.center.z - sphere.radius;
+ if( upper.x < (sphere.center.x + sphere.radius))
+ upper.x = sphere.center.x + sphere.radius;
+ if( upper.y < (sphere.center.y + sphere.radius))
+ upper.y = sphere.center.y + sphere.radius;
+ if( upper.z < (sphere.center.z + sphere.radius))
+ upper.z = sphere.center.z + sphere.radius;
+ }
+ else if(bounds[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)bounds[i];
+
+ for(i=0;i<polytope.nVerts;i++) { // TODO handle polytope with no verts
+ if( polytope.verts[i].x < lower.x )
+ lower.x = polytope.verts[i].x;
+ if( polytope.verts[i].y < lower.y )
+ lower.y = polytope.verts[i].y;
+ if( polytope.verts[i].z < lower.z )
+ lower.z = polytope.verts[i].z;
+ if( polytope.verts[i].x > upper.x )
+ upper.x = polytope.verts[i].x;
+ if( polytope.verts[i].y > upper.y )
+ upper.y = polytope.verts[i].y;
+ if( polytope.verts[i].z > upper.z )
+ upper.z = polytope.verts[i].z;
+ }
+ }
+ else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox1"));
+ }
+ }
+ updateBoundsStates();
+ }
+
+ /**
+ * Gets the lower corner of this bounding box.
+ * @param p1 a Point to receive the lower corner of the bounding box
+ */
+ public void getLower(Point3d p1) {
+ p1.x = lower.x;
+ p1.y = lower.y;
+ p1.z = lower.z;
+ }
+
+ /**
+ * Sets the lower corner of this bounding box.
+ * @param xmin minimum x value of boundining box
+ * @param ymin minimum y value of boundining box
+ * @param zmin minimum z value of boundining box
+ */
+ public void setLower(double xmin, double ymin, double zmin ) {
+ lower.x = xmin;
+ lower.y = ymin;
+ lower.z = zmin;
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Sets the lower corner of this bounding box.
+ * @param p1 a Point defining the new lower corner of the bounding box
+ */
+ public void setLower(Point3d p1) {
+
+ lower.x = p1.x;
+ lower.y = p1.y;
+ lower.z = p1.z;
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Gets the upper corner of this bounding box.
+ * @param p1 a Point to receive the upper corner of the bounding box
+ */
+ public void getUpper(Point3d p1) {
+ p1.x = upper.x;
+ p1.y = upper.y;
+ p1.z = upper.z;
+ }
+
+ /**
+ * Sets the upper corner of this bounding box.
+ * @param xmax max x value of boundining box
+ * @param ymax max y value of boundining box
+ * @param zmax max z value of boundining box
+ */
+ public void setUpper(double xmax, double ymax, double zmax ) {
+ upper.x = xmax;
+ upper.y = ymax;
+ upper.z = zmax;
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Sets the upper corner of this bounding box.
+ * @param p1 a Point defining the new upper corner of the bounding box
+ */
+ public void setUpper(Point3d p1) {
+ upper.x = p1.x;
+ upper.y = p1.y;
+ upper.z = p1.z;
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Sets the the value of this BoundingBox
+ * @param boundsObject another bounds object
+ */
+ public void set(Bounds boundsObject) {
+ int i;
+
+ if(( boundsObject == null ) ||( boundsObject.boundsIsEmpty)) {
+ // Negative volume.
+ lower.x = lower.y = lower.z = 1.0d;
+ upper.x = upper.y = upper.z = -1.0d;
+
+ } else if( boundsObject.boundsIsInfinite ) {
+ lower.x = lower.y = lower.z = Double.NEGATIVE_INFINITY;
+ upper.x = upper.y = upper.z = Double.POSITIVE_INFINITY;
+
+ } else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+
+ lower.x = box.lower.x;
+ lower.y = box.lower.y;
+ lower.z = box.lower.z;
+ upper.x = box.upper.x;
+ upper.y = box.upper.y;
+ upper.z = box.upper.z;
+
+ } else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ lower.x = sphere.center.x - sphere.radius;
+ lower.y = sphere.center.y - sphere.radius;
+ lower.z = sphere.center.z - sphere.radius;
+ upper.x = sphere.center.x + sphere.radius;
+ upper.y = sphere.center.y + sphere.radius;
+ upper.z = sphere.center.z + sphere.radius;
+
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ lower.x = upper.x = polytope.verts[0].x;
+ lower.y = upper.y = polytope.verts[0].y;
+ lower.z = upper.z = polytope.verts[0].z;
+
+ for(i=1;i<polytope.nVerts;i++) {
+ if( polytope.verts[i].x < lower.x ) lower.x = polytope.verts[i].x;
+ if( polytope.verts[i].y < lower.y ) lower.y = polytope.verts[i].y;
+ if( polytope.verts[i].z < lower.z ) lower.z = polytope.verts[i].z;
+ if( polytope.verts[i].x > upper.x ) upper.x = polytope.verts[i].x;
+ if( polytope.verts[i].y > upper.y ) upper.y = polytope.verts[i].y;
+ if( polytope.verts[i].z > upper.z ) upper.z = polytope.verts[i].z;
+ }
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox0"));
+ }
+
+ updateBoundsStates();
+ }
+
+
+ /**
+ * Creates a copy of this bounding box.
+ * @return a new bounding box
+ */
+ public Object clone() {
+ return new BoundingBox(this.lower, this.upper);
+ }
+
+
+ /**
+ * Indicates whether the specified <code>bounds</code> object is
+ * equal to this BoundingBox object. They are equal if the
+ * specified <code>bounds</code> object is an instance of
+ * BoundingBox and all of the data
+ * members of <code>bounds</code> are equal to the corresponding
+ * data members in this BoundingBox.
+ * @param bounds the object with which the comparison is made.
+ * @return true if this BoundingBox is equal to <code>bounds</code>;
+ * otherwise false
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean equals(Object bounds) {
+ try {
+ BoundingBox box = (BoundingBox)bounds;
+ return (lower.equals(box.lower) &&
+ upper.equals(box.upper));
+ }
+ catch (NullPointerException e) {
+ return false;
+ }
+ catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns a hash code value for this BoundingBox object
+ * based on the data values in this object. Two different
+ * BoundingBox objects with identical data values (i.e.,
+ * BoundingBox.equals returns true) will return the same hash
+ * code value. Two BoundingBox objects with different data
+ * members may return the same hash code value, although this is
+ * not likely.
+ * @return a hash code value for this BoundingBox object.
+ *
+ * @since Java 3D 1.2
+ */
+ public int hashCode() {
+ long bits = 1L;
+ bits = 31L * bits + Double.doubleToLongBits(lower.x);
+ bits = 31L * bits + Double.doubleToLongBits(lower.y);
+ bits = 31L * bits + Double.doubleToLongBits(lower.z);
+ bits = 31L * bits + Double.doubleToLongBits(upper.x);
+ bits = 31L * bits + Double.doubleToLongBits(upper.y);
+ bits = 31L * bits + Double.doubleToLongBits(upper.z);
+ return (int) (bits ^ (bits >> 32));
+ }
+
+
+ /**
+ * Combines this bounding box with a bounding object so that the
+ * resulting bounding box encloses the original bounding box and the
+ * specified bounds object.
+ * @param boundsObject another bounds object
+ */
+ public void combine(Bounds boundsObject) {
+
+ if((boundsObject == null) || (boundsObject.boundsIsEmpty)
+ || (boundsIsInfinite))
+ return;
+
+ if((boundsIsEmpty) || (boundsObject.boundsIsInfinite)) {
+ this.set(boundsObject);
+ return;
+ }
+
+ if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+
+ if( lower.x > box.lower.x) lower.x = box.lower.x;
+ if( lower.y > box.lower.y) lower.y = box.lower.y;
+ if( lower.z > box.lower.z) lower.z = box.lower.z;
+ if( upper.x < box.upper.x) upper.x = box.upper.x;
+ if( upper.y < box.upper.y) upper.y = box.upper.y;
+ if( upper.z < box.upper.z) upper.z = box.upper.z;
+
+ }
+ else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ if( lower.x > (sphere.center.x - sphere.radius))
+ lower.x = sphere.center.x - sphere.radius;
+ if( lower.y > (sphere.center.y - sphere.radius))
+ lower.y = sphere.center.y - sphere.radius;
+ if( lower.z > (sphere.center.z - sphere.radius))
+ lower.z = sphere.center.z - sphere.radius;
+ if( upper.x < (sphere.center.x + sphere.radius))
+ upper.x = sphere.center.x + sphere.radius;
+ if( upper.y < (sphere.center.y + sphere.radius))
+ upper.y = sphere.center.y + sphere.radius;
+ if( upper.z < (sphere.center.z + sphere.radius))
+ upper.z = sphere.center.z + sphere.radius;
+
+ }
+ else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ int i;
+ for(i=1;i<polytope.nVerts;i++) {
+ if( polytope.verts[i].x < lower.x ) lower.x = polytope.verts[i].x;
+ if( polytope.verts[i].y < lower.y ) lower.y = polytope.verts[i].y;
+ if( polytope.verts[i].z < lower.z ) lower.z = polytope.verts[i].z;
+ if( polytope.verts[i].x > upper.x ) upper.x = polytope.verts[i].x;
+ if( polytope.verts[i].y > upper.y ) upper.y = polytope.verts[i].y;
+ if( polytope.verts[i].z > upper.z ) upper.z = polytope.verts[i].z;
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox3"));
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Combines this bounding box with an array of bounding objects
+ * so that the resulting bounding box encloses the original bounding
+ * box and the array of bounding objects.
+ * @param bounds an array of bounds objects
+ */
+ public void combine(Bounds[] bounds) {
+ int i=0;
+
+ if( (bounds == null) || (bounds.length <= 0)
+ || (boundsIsInfinite))
+ return;
+
+ // find first non empty bounds object
+ while( (i<bounds.length) && ((bounds[i]==null) || bounds[i].boundsIsEmpty)) {
+ i++;
+ }
+ if( i >= bounds.length)
+ return; // no non empty bounds so do not modify current bounds
+
+ if(boundsIsEmpty)
+ this.set(bounds[i++]);
+
+ if(boundsIsInfinite)
+ return;
+
+ for(;i<bounds.length;i++) {
+ if( bounds[i] == null ); // do nothing
+ else if( bounds[i].boundsIsEmpty); // do nothing
+ else if( bounds[i].boundsIsInfinite ) {
+ lower.x = lower.y = lower.z = Double.NEGATIVE_INFINITY;
+ upper.x = upper.y = upper.z = Double.POSITIVE_INFINITY;
+ break; // We're done.
+ }
+ else if( bounds[i].boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)bounds[i];
+
+ if( lower.x > box.lower.x) lower.x = box.lower.x;
+ if( lower.y > box.lower.y) lower.y = box.lower.y;
+ if( lower.z > box.lower.z) lower.z = box.lower.z;
+ if( upper.x < box.upper.x) upper.x = box.upper.x;
+ if( upper.y < box.upper.y) upper.y = box.upper.y;
+ if( upper.z < box.upper.z) upper.z = box.upper.z;
+ }
+ else if( bounds[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)bounds[i];
+ if( lower.x > (sphere.center.x - sphere.radius))
+ lower.x = sphere.center.x - sphere.radius;
+ if( lower.y > (sphere.center.y - sphere.radius))
+ lower.y = sphere.center.y - sphere.radius;
+ if( lower.z > (sphere.center.z - sphere.radius))
+ lower.z = sphere.center.z - sphere.radius;
+ if( upper.x < (sphere.center.x + sphere.radius))
+ upper.x = sphere.center.x + sphere.radius;
+ if( upper.y < (sphere.center.y + sphere.radius))
+ upper.y = sphere.center.y + sphere.radius;
+ if( upper.z < (sphere.center.z + sphere.radius))
+ upper.z = sphere.center.z + sphere.radius;
+ }
+ else if(bounds[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)bounds[i];
+ for(i=1;i<polytope.nVerts;i++) {
+ if( polytope.verts[i].x < lower.x ) lower.x = polytope.verts[i].x;
+ if( polytope.verts[i].y < lower.y ) lower.y = polytope.verts[i].y;
+ if( polytope.verts[i].z < lower.z ) lower.z = polytope.verts[i].z;
+ if( polytope.verts[i].x > upper.x ) upper.x = polytope.verts[i].x;
+ if( polytope.verts[i].y > upper.y ) upper.y = polytope.verts[i].y;
+ if( polytope.verts[i].z > upper.z ) upper.z = polytope.verts[i].z;
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox4"));
+ }
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Combines this bounding box with a point so that the resulting
+ * bounding box encloses the original bounding box and the point.
+ * @param point a 3d point in space
+ */
+ public void combine(Point3d point) {
+
+ if( boundsIsInfinite) {
+ return;
+ }
+
+ if( boundsIsEmpty) {
+ upper.x = lower.x = point.x;
+ upper.y = lower.y = point.y;
+ upper.z = lower.z = point.z;
+ } else {
+ if( point.x > upper.x) upper.x = point.x;
+ if( point.y > upper.y) upper.y = point.y;
+ if( point.z > upper.z) upper.z = point.z;
+
+ if( point.x < lower.x) lower.x = point.x;
+ if( point.y < lower.y) lower.y = point.y;
+ if( point.z < lower.z) lower.z = point.z;
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Combines this bounding box with an array of points so that the
+ * resulting bounding box encloses the original bounding box and the
+ * array of points.
+ * @param points an array of 3d points in space
+ */
+ public void combine(Point3d[] points) {
+
+ int i;
+
+ if( boundsIsInfinite) {
+ return;
+ }
+
+ if( boundsIsEmpty) {
+ this.setUpper(points[0]);
+ this.setLower(points[0]);
+ }
+
+ for(i=0;i<points.length;i++) {
+ if( points[i].x > upper.x) upper.x = points[i].x;
+ if( points[i].y > upper.y) upper.y = points[i].y;
+ if( points[i].z > upper.z) upper.z = points[i].z;
+
+ if( points[i].x < lower.x) lower.x = points[i].x;
+ if( points[i].y < lower.y) lower.y = points[i].y;
+ if( points[i].z < lower.z) lower.z = points[i].z;
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Modifies the bounding box so that it bounds the volume
+ * generated by transforming the given bounding object.
+ * @param boundsObject the bounding object to be transformed
+ * @param matrix a transformation matrix
+ */
+ public void transform( Bounds boundsObject, Transform3D matrix) {
+
+ if( boundsObject == null || boundsObject.boundsIsEmpty) {
+ // Negative volume.
+ lower.x = lower.y = lower.z = 1.0d;
+ upper.x = upper.y = upper.z = -1.0d;
+ updateBoundsStates();
+ return;
+ }
+
+ if(boundsObject.boundsIsInfinite) {
+ lower.x = lower.y = lower.z = Double.NEGATIVE_INFINITY;
+ upper.x = upper.y = upper.z = Double.POSITIVE_INFINITY;
+ updateBoundsStates();
+ return;
+ }
+
+ if(boundsObject.boundId == BOUNDING_BOX){
+ if (tmpBox == null) {
+ tmpBox = new BoundingBox( (BoundingBox)boundsObject);
+ } else {
+ tmpBox.set((BoundingBox)boundsObject);
+ }
+ tmpBox.transform(matrix);
+ this.set(tmpBox);
+ }
+ else if(boundsObject.boundId == BOUNDING_SPHERE) {
+ if (tmpSphere == null) {
+ tmpSphere = new BoundingSphere( (BoundingSphere)boundsObject);
+ } else {
+ tmpSphere.set((BoundingSphere)boundsObject);
+ }
+ tmpSphere.transform(matrix);
+ this.set(tmpSphere);
+ }
+ else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ if (tmpPolytope == null) {
+ tmpPolytope =
+ new BoundingPolytope((BoundingPolytope) boundsObject);
+ } else {
+ tmpPolytope.set((BoundingPolytope)boundsObject);
+ }
+ tmpPolytope.transform(matrix);
+ this.set(tmpPolytope);
+
+ }
+ else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox5"));
+ }
+ }
+
+ /**
+ * Transforms this bounding box by the given matrix.
+ * @param matrix a transformation matrix
+ */
+ public void transform(Transform3D matrix) {
+
+ if(boundsIsInfinite)
+ return;
+
+ double ux, uy, uz, lx, ly, lz;
+ ux = upper.x; uy = upper.y; uz = upper.z;
+ lx = lower.x; ly = lower.y; lz = lower.z;
+
+ tmpP3d.set(ux, uy, uz);
+ matrix.transform( tmpP3d );
+ upper.x = tmpP3d.x;
+ upper.y = tmpP3d.y;
+ upper.z = tmpP3d.z;
+ lower.x = tmpP3d.x;
+ lower.y = tmpP3d.y;
+ lower.z = tmpP3d.z;
+
+ tmpP3d.set(lx, uy, uz);
+ matrix.transform( tmpP3d );
+ if ( tmpP3d.x > upper.x ) upper.x = tmpP3d.x;
+ if ( tmpP3d.y > upper.y ) upper.y = tmpP3d.y;
+ if ( tmpP3d.z > upper.z ) upper.z = tmpP3d.z;
+ if ( tmpP3d.x < lower.x ) lower.x = tmpP3d.x;
+ if ( tmpP3d.y < lower.y ) lower.y = tmpP3d.y;
+ if ( tmpP3d.z < lower.z ) lower.z = tmpP3d.z;
+
+ tmpP3d.set(lx, ly, uz);
+ matrix.transform( tmpP3d );
+ if ( tmpP3d.x > upper.x ) upper.x = tmpP3d.x;
+ if ( tmpP3d.y > upper.y ) upper.y = tmpP3d.y;
+ if ( tmpP3d.z > upper.z ) upper.z = tmpP3d.z;
+ if ( tmpP3d.x < lower.x ) lower.x = tmpP3d.x;
+ if ( tmpP3d.y < lower.y ) lower.y = tmpP3d.y;
+ if ( tmpP3d.z < lower.z ) lower.z = tmpP3d.z;
+
+ tmpP3d.set(ux, ly, uz);
+ matrix.transform( tmpP3d );
+ if ( tmpP3d.x > upper.x ) upper.x = tmpP3d.x;
+ if ( tmpP3d.y > upper.y ) upper.y = tmpP3d.y;
+ if ( tmpP3d.z > upper.z ) upper.z = tmpP3d.z;
+ if ( tmpP3d.x < lower.x ) lower.x = tmpP3d.x;
+ if ( tmpP3d.y < lower.y ) lower.y = tmpP3d.y;
+ if ( tmpP3d.z < lower.z ) lower.z = tmpP3d.z;
+
+ tmpP3d.set(lx, uy, lz);
+ matrix.transform( tmpP3d );
+ if ( tmpP3d.x > upper.x ) upper.x = tmpP3d.x;
+ if ( tmpP3d.y > upper.y ) upper.y = tmpP3d.y;
+ if ( tmpP3d.z > upper.z ) upper.z = tmpP3d.z;
+ if ( tmpP3d.x < lower.x ) lower.x = tmpP3d.x;
+ if ( tmpP3d.y < lower.y ) lower.y = tmpP3d.y;
+ if ( tmpP3d.z < lower.z ) lower.z = tmpP3d.z;
+
+ tmpP3d.set(ux, uy, lz);
+ matrix.transform( tmpP3d );
+ if ( tmpP3d.x > upper.x ) upper.x = tmpP3d.x;
+ if ( tmpP3d.y > upper.y ) upper.y = tmpP3d.y;
+ if ( tmpP3d.z > upper.z ) upper.z = tmpP3d.z;
+ if ( tmpP3d.x < lower.x ) lower.x = tmpP3d.x;
+ if ( tmpP3d.y < lower.y ) lower.y = tmpP3d.y;
+ if ( tmpP3d.z < lower.z ) lower.z = tmpP3d.z;
+
+ tmpP3d.set(lx, ly, lz);
+ matrix.transform( tmpP3d );
+ if ( tmpP3d.x > upper.x ) upper.x = tmpP3d.x;
+ if ( tmpP3d.y > upper.y ) upper.y = tmpP3d.y;
+ if ( tmpP3d.z > upper.z ) upper.z = tmpP3d.z;
+ if ( tmpP3d.x < lower.x ) lower.x = tmpP3d.x;
+ if ( tmpP3d.y < lower.y ) lower.y = tmpP3d.y;
+ if ( tmpP3d.z < lower.z ) lower.z = tmpP3d.z;
+
+ tmpP3d.set(ux, ly, lz);
+ matrix.transform( tmpP3d );
+ if ( tmpP3d.x > upper.x ) upper.x = tmpP3d.x;
+ if ( tmpP3d.y > upper.y ) upper.y = tmpP3d.y;
+ if ( tmpP3d.z > upper.z ) upper.z = tmpP3d.z;
+ if ( tmpP3d.x < lower.x ) lower.x = tmpP3d.x;
+ if ( tmpP3d.y < lower.y ) lower.y = tmpP3d.y;
+ if ( tmpP3d.z < lower.z ) lower.z = tmpP3d.z;
+
+ }
+
+ /**
+ * Test for intersection with a ray.
+ * @param origin the starting point of the ray
+ * @param direction the direction of the ray
+ * @param position3 a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Point3d origin, Vector3d direction, Point4d position ) {
+ double t1,t2,tmp,tnear,tfar,invDir,invMag;
+ double dirx, diry, dirz;
+
+ /*
+ System.out.println("BoundingBox.intersect(p,d,p) called\n");
+ System.out.println("bounds = " + lower + " -> " + upper);
+ */
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = origin.x;
+ position.y = origin.y;
+ position.z = origin.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ double dirLen = direction.x*direction.x + direction.y*direction.y +
+ direction.z*direction.z;
+
+ // Handle zero length direction vector.
+ if(dirLen == 0.0)
+ return intersect(origin, position);
+
+ invMag = 1.0/Math.sqrt(dirLen);
+ dirx = direction.x*invMag;
+ diry = direction.y*invMag;
+ dirz = direction.z*invMag;
+
+ /*
+ System.out.println("dir = " + dirx + ", " + diry + ", " + dirz);
+ System.out.println("origin = " + origin);
+ */
+
+ // initialize tnear and tfar to handle dir.? == 0 cases
+ tnear = -Double.MAX_VALUE;
+ tfar = Double.MAX_VALUE;
+
+ if(dirx == 0.0) {
+ //System.out.println("dirx == 0.0");
+ if (origin.x < lower.x || origin.x > upper.x ) {
+ //System.out.println( "parallel to x plane and outside");
+ return false;
+ }
+ } else {
+ invDir = 1.0/dirx;
+ t1 = (lower.x-origin.x)*invDir;
+ t2 = (upper.x-origin.x)*invDir;
+
+ //System.out.println("x t1 = " + t1 + " t2 = " + t2);
+ if( t1 > t2) {
+ tnear = t2;
+ tfar = t1;
+ }else {
+ tnear = t1;
+ tfar = t2;
+ }
+ if( tfar < 0.0 ) {
+ //System.out.println( "x failed: tnear="+tnear+" tfar="+tfar);
+ return false;
+ }
+ //System.out.println("x tnear = " + tnear + " tfar = " + tfar);
+ }
+ // y
+ if (diry == 0.0) {
+ //System.out.println("diry == 0.0");
+ if( origin.y < lower.y || origin.y > upper.y ){
+ //System.out.println( "parallel to y plane and outside");
+ return false;
+ }
+ } else {
+ invDir = 1.0/diry;
+ //System.out.println("invDir = " + invDir);
+ t1 = (lower.y-origin.y)*invDir;
+ t2 = (upper.y-origin.y)*invDir;
+
+ if( t1 > t2) {
+ tmp = t1;
+ t1 = t2;
+ t2 = tmp;
+ }
+ //System.out.println("y t1 = " + t1 + " t2 = " + t2);
+ if( t1 > tnear) tnear = t1;
+ if( t2 < tfar ) tfar = t2;
+
+ if( (tfar < 0.0) || (tnear > tfar)){
+ //System.out.println( "y failed: tnear="+tnear+" tfar="+tfar);
+ return false;
+ }
+ //System.out.println("y tnear = " + tnear + " tfar = " + tfar);
+ }
+
+ // z
+ if (dirz == 0.0) {
+ //System.out.println("dirz == 0.0");
+ if( origin.z < lower.z || origin.z > upper.z ) {
+ //System.out.println( "parallel to z plane and outside");
+ return false;
+ }
+ } else {
+ invDir = 1.0/dirz;
+ t1 = (lower.z-origin.z)*invDir;
+ t2 = (upper.z-origin.z)*invDir;
+
+ if( t1 > t2) {
+ tmp = t1;
+ t1 = t2;
+ t2 = tmp;
+ }
+ //System.out.println("z t1 = " + t1 + " t2 = " + t2);
+ if( t1 > tnear) tnear = t1;
+ if( t2 < tfar ) tfar = t2;
+
+ if( (tfar < 0.0) || (tnear > tfar)){
+ //System.out.println( "z failed: tnear="+tnear+" tfar="+tfar);
+ return false;
+ }
+ //System.out.println("z tnear = " + tnear + " tfar = " + tfar);
+ }
+
+ if((tnear < 0.0) && (tfar >= 0.0)) {
+ // origin is inside the BBox.
+ position.x = origin.x + dirx*tfar;
+ position.y = origin.y + diry*tfar;
+ position.z = origin.z + dirz*tfar;
+ position.w = tfar;
+ }
+ else {
+ position.x = origin.x + dirx*tnear;
+ position.y = origin.y + diry*tnear;
+ position.z = origin.z + dirz*tnear;
+ position.w = tnear;
+ }
+
+ return true;
+
+ }
+
+
+ /**
+ * Test for intersection with a point.
+ * @param point the pick point
+ * @param position a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Point3d point, Point4d position ) {
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = point.x;
+ position.y = point.y;
+ position.z = point.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ if( point.x <= upper.x && point.x >= lower.x &&
+ point.y <= upper.y && point.y >= lower.y &&
+ point.z <= upper.z && point.z >= lower.z) {
+ position.x = point.x;
+ position.y = point.y;
+ position.z = point.z;
+ position.w = 0.0;
+ return true;
+ } else
+ return false;
+
+ }
+
+ /**
+ * Test for intersection with a segment.
+ * @param start a point defining the start of the line segment
+ * @param end a point defining the end of the line segment
+ * @param position a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect( Point3d start, Point3d end, Point4d position ) {
+ double t1,t2,tmp,tnear,tfar,invDir,invMag;
+ double dirx, diry, dirz;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = start.x;
+ position.y = start.y;
+ position.z = start.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ dirx = end.x - start.x;
+ diry = end.y - start.y;
+ dirz = end.z - start.z;
+
+ double dirLen = dirx*dirx + diry*diry + dirz*dirz;
+
+ // Optimization : Handle zero length direction vector.
+ if(dirLen == 0.0)
+ return intersect(start, position);
+
+ dirLen = Math.sqrt(dirLen);
+ // System.out.println("dirLen is " + dirLen);
+ invMag = 1.0/dirLen;
+ dirx = dirx*invMag;
+ diry = diry*invMag;
+ dirz = dirz*invMag;
+
+ /*
+ System.out.println("dir = " + dir);
+ System.out.println("start = " + start);
+ System.out.println("lower = " + lower);
+ System.out.println("upper = " + upper);
+ */
+
+ // initialize tnear and tfar to handle dir.? == 0 cases
+ tnear = -Double.MAX_VALUE;
+ tfar = Double.MAX_VALUE;
+
+ if(dirx == 0.0) {
+ //System.out.println("dirx == 0.0");
+ if (start.x < lower.x || start.x > upper.x ) {
+ //System.out.println( "parallel to x plane and outside");
+ return false;
+ }
+ } else {
+ invDir = 1.0/dirx;
+ t1 = (lower.x-start.x)*invDir;
+ t2 = (upper.x-start.x)*invDir;
+
+ //System.out.println("x t1 = " + t1 + " t2 = " + t2);
+ if( t1 > t2) {
+ tnear = t2;
+ tfar = t1;
+ }else {
+ tnear = t1;
+ tfar = t2;
+ }
+ if( tfar < 0.0 ) {
+ //System.out.println( "x failed: tnear="+tnear+" tfar="+tfar);
+ return false;
+ }
+ //System.out.println("x tnear = " + tnear + " tfar = " + tfar);
+ }
+ // y
+ if (diry == 0.0) {
+ //System.out.println("diry == 0.0");
+ if( start.y < lower.y || start.y > upper.y ){
+ //System.out.println( "parallel to y plane and outside");
+ return false;
+ }
+ } else {
+ invDir = 1.0/diry;
+ //System.out.println("invDir = " + invDir);
+ t1 = (lower.y-start.y)*invDir;
+ t2 = (upper.y-start.y)*invDir;
+
+ if( t1 > t2) {
+ tmp = t1;
+ t1 = t2;
+ t2 = tmp;
+ }
+ //System.out.println("y t1 = " + t1 + " t2 = " + t2);
+ if( t1 > tnear) tnear = t1;
+ if( t2 < tfar ) tfar = t2;
+
+ if( (tfar < 0.0) || (tnear > tfar)){
+ //System.out.println( "y failed: tnear="+tnear+" tfar="+tfar);
+ return false;
+ }
+ //System.out.println("y tnear = " + tnear + " tfar = " + tfar);
+ }
+
+ // z
+ if (dirz == 0.0) {
+ //System.out.println("dirz == 0.0");
+ if( start.z < lower.z || start.z > upper.z ) {
+ //System.out.println( "parallel to z plane and outside");
+ return false;
+ }
+ } else {
+ invDir = 1.0/dirz;
+ t1 = (lower.z-start.z)*invDir;
+ t2 = (upper.z-start.z)*invDir;
+
+ if( t1 > t2) {
+ tmp = t1;
+ t1 = t2;
+ t2 = tmp;
+ }
+ //System.out.println("z t1 = " + t1 + " t2 = " + t2);
+ if( t1 > tnear) tnear = t1;
+ if( t2 < tfar ) tfar = t2;
+
+ if( (tfar < 0.0) || (tnear > tfar)){
+ //System.out.println( "z failed: tnear="+tnear+" tfar="+tfar);
+ return false;
+ }
+ //System.out.println("z tnear = " + tnear + " tfar = " + tfar);
+ }
+
+ if((tnear < 0.0) && (tfar >= 0.0)) {
+ // origin is inside the BBox.
+ position.x = start.x + dirx*tfar;
+ position.y = start.y + diry*tfar;
+ position.z = start.z + dirz*tfar;
+ position.w = tfar;
+ }
+ else {
+ if(tnear>dirLen) {
+ // Segment is behind BBox.
+ /*
+ System.out.println("PickSegment : intersected postion : " + position
+ + " tnear " + tnear + " tfar " + tfar );
+ */
+ return false;
+ }
+ position.x = start.x + dirx*tnear;
+ position.y = start.y + diry*tnear;
+ position.z = start.z + dirz*tnear;
+
+ position.w = tnear;
+ }
+
+ /*
+ System.out.println("tnear = " + tnear + " tfar = " + tfar + " w " +
+ position.w);
+ System.out.println("lower = " + lower);
+ System.out.println("upper = " + upper + "\n");
+ */
+ return true;
+
+ }
+
+ /**
+ * Test for intersection with a ray.
+ * @param origin the starting point of the ray
+ * @param direction the direction of the ray
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Point3d origin, Vector3d direction ) {
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ return true;
+ }
+
+ Point3d p=new Point3d();
+ return intersect( origin, direction, p );
+ }
+
+ /**
+ * A protected intersect method that returns the point of intersection.
+ * Used by Picking methods to sort or return closest picked item.
+ */
+ boolean intersect(Point3d origin, Vector3d direction, Point3d intersect ) {
+ double theta=0.0;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ intersect.x = origin.x;
+ intersect.y = origin.y;
+ intersect.z = origin.z;
+ return true;
+ }
+
+ if (direction.x > 0.0 )
+ theta = Math.max( theta, (lower.x - origin.x)/direction.x );
+ if (direction.x < 0.0 )
+ theta = Math.max( theta, (upper.x - origin.x)/direction.x );
+ if (direction.y > 0.0 )
+ theta = Math.max( theta, (lower.y - origin.y)/direction.y );
+ if (direction.y < 0.0 )
+ theta = Math.max( theta, (upper.y - origin.y)/direction.y );
+ if (direction.z > 0.0 )
+ theta = Math.max( theta, (lower.z - origin.z)/direction.z );
+ if (direction.z < 0.0 )
+ theta = Math.max( theta, (upper.z - origin.z)/direction.z );
+
+ intersect.x = origin.x + theta*direction.x;
+ intersect.y = origin.y + theta*direction.y;
+ intersect.z = origin.z + theta*direction.z;
+
+ if (intersect.x < (lower.x-EPS)) return false;
+ if (intersect.x > (upper.x+EPS)) return false;
+ if (intersect.y < (lower.y-EPS)) return false;
+ if (intersect.y > (upper.y+EPS)) return false;
+ if (intersect.z < (lower.z-EPS)) return false;
+ if (intersect.z > (upper.z+EPS)) return false;
+
+ return true;
+
+ }
+
+ /**
+ * Test for intersection with a point.
+ * @param point a point defining a position in 3-space
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Point3d point ) {
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+ if( boundsIsInfinite ) {
+ return true;
+ }
+
+ if( point.x <= upper.x && point.x >= lower.x &&
+ point.y <= upper.y && point.y >= lower.y &&
+ point.z <= upper.z && point.z >= lower.z)
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Tests whether the bounding box is empty. A bounding box is
+ * empty if it is null (either by construction or as the result of
+ * a null intersection) or if its volume is negative. A bounding box
+ * with a volume of zero is <i>not</i> empty.
+ * @return true if the bounding box is empty; otherwise, it returns false
+ */
+ public boolean isEmpty() {
+
+ return boundsIsEmpty;
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Bounds boundsObject, Point4d position) {
+ return intersect(boundsObject);
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds boundsObject) {
+
+ if( boundsObject == null ) {
+ return false;
+ }
+
+ if( boundsIsEmpty || boundsObject.boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite || boundsObject.boundsIsInfinite ) {
+ return true;
+ }
+
+ if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+ // both boxes are axis aligned
+ if( upper.x > box.lower.x && box.upper.x > lower.x &&
+ upper.y > box.lower.y && box.upper.y > lower.y &&
+ upper.z > box.lower.z && box.upper.z > lower.z )
+ return true;
+ else
+ return false;
+ } else if( boundsObject.boundId == BOUNDING_SPHERE) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ double rad_sq = sphere.radius*sphere.radius;
+ double dis = 0.0;
+
+ if( sphere.center.x < lower.x )
+ dis = (sphere.center.x-lower.x)*(sphere.center.x-lower.x);
+ else
+ if( sphere.center.x > upper.x )
+ dis = (sphere.center.x-upper.x)*(sphere.center.x-upper.x);
+
+ if( sphere.center.y < lower.y )
+ dis += (sphere.center.y-lower.y)*(sphere.center.y-lower.y);
+ else
+ if( sphere.center.y > upper.y )
+ dis += (sphere.center.y-upper.y)*(sphere.center.y-upper.y);
+
+ if( sphere.center.z < lower.z )
+ dis += (sphere.center.z-lower.z)*(sphere.center.z-lower.z);
+ else
+ if( sphere.center.z > upper.z )
+ dis += (sphere.center.z-upper.z)*(sphere.center.z-upper.z);
+
+ if( dis <= rad_sq )
+ return true;
+ else
+ return false;
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ // intersect an axis aligned box with a polytope
+ return intersect_ptope_abox ( (BoundingPolytope)boundsObject, this );
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox6"));
+ }
+
+ }
+
+ /**
+ * Test for intersection with an array of bounds objects.
+ * @param boundsObjects an array of bounding objects
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds[] boundsObjects) {
+
+ double distsq, radsq;
+ int i;
+
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ return false;
+ }
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ for(i = 0; i < boundsObjects.length; i++){
+ if( boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty) ;
+ else if( boundsIsInfinite || boundsObjects[i].boundsIsInfinite ) {
+ return true; // We're done here.
+ }
+ else if( boundsObjects[i].boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ // both boxes are axis aligned
+ if( upper.x > box.lower.x && box.upper.x > lower.x &&
+ upper.y > box.lower.y && box.upper.y > lower.y &&
+ upper.z > box.lower.z && box.upper.z > lower.z )
+ return true;
+ }
+ else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ double rad_sq = sphere.radius*sphere.radius;
+ double dis = 0.0;
+
+ if( sphere.center.x < lower.x )
+ dis = (sphere.center.x-lower.x)*(sphere.center.x-lower.x);
+ else
+ if( sphere.center.x > upper.x )
+ dis = (sphere.center.x-upper.x)*(sphere.center.x-upper.x);
+
+ if( sphere.center.y < lower.y )
+ dis += (sphere.center.y-lower.y)*(sphere.center.y-lower.y);
+ else
+ if( sphere.center.y > upper.y )
+ dis += (sphere.center.y-upper.y)*(sphere.center.y-upper.y);
+
+ if( sphere.center.z < lower.z )
+ dis += (sphere.center.z-lower.z)*(sphere.center.z-lower.z);
+ else
+ if( sphere.center.z > upper.z )
+ dis += (sphere.center.z-upper.z)*(sphere.center.z-upper.z);
+
+ if( dis <= rad_sq )
+ return true;
+
+ }
+ else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ if( intersect_ptope_abox ( (BoundingPolytope)boundsObjects[i], this ))
+ return true;
+ }
+ else {
+ // System.out.println("intersect ?? ");
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Test for intersection with another bounding box.
+ * @param boundsObject another bounding object
+ * @param newBoundBox the new bounding box which is the intersection of
+ * the boundsObject and this BoundingBox
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds boundsObject, BoundingBox newBoundBox) {
+
+ if((boundsObject == null) || boundsIsEmpty || boundsObject.boundsIsEmpty ) {
+ // Negative volume.
+ newBoundBox.setLower( 1.0d, 1.0d, 1.0d);
+ newBoundBox.setUpper(-1.0d, -1.0d, -1.0d);
+ return false;
+ }
+
+
+ if(boundsIsInfinite && (!boundsObject.boundsIsInfinite)) {
+ newBoundBox.set(boundsObject);
+ return true;
+ }
+ else if((!boundsIsInfinite) && boundsObject.boundsIsInfinite) {
+ newBoundBox.set(this);
+ return true;
+ }
+ else if(boundsIsInfinite && boundsObject.boundsIsInfinite) {
+ newBoundBox.set(this);
+ return true;
+ }
+ else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+ // both boxes are axis aligned
+ if( upper.x > box.lower.x && box.upper.x > lower.x &&
+ upper.y > box.lower.y && box.upper.y > lower.y &&
+ upper.z > box.lower.z && box.upper.z > lower.z ){
+
+
+ if(upper.x > box.upper.x)
+ newBoundBox.upper.x = box.upper.x;
+ else
+ newBoundBox.upper.x = upper.x;
+
+ if(upper.y > box.upper.y)
+ newBoundBox.upper.y = box.upper.y;
+ else
+ newBoundBox.upper.y = upper.y;
+
+ if(upper.z > box.upper.z)
+ newBoundBox.upper.z = box.upper.z;
+ else
+ newBoundBox.upper.z = upper.z;
+
+ if(lower.x < box.lower.x)
+ newBoundBox.lower.x = box.lower.x;
+ else
+ newBoundBox.lower.x = lower.x;
+
+ if(lower.y < box.lower.y)
+ newBoundBox.lower.y = box.lower.y;
+ else
+ newBoundBox.lower.y = lower.y;
+
+ if(lower.z < box.lower.z)
+ newBoundBox.lower.z = box.lower.z;
+ else
+ newBoundBox.lower.z = lower.z;
+
+ newBoundBox.updateBoundsStates();
+ return true;
+ } else {
+ // Negative volume.
+ newBoundBox.setLower( 1.0d, 1.0d, 1.0d);
+ newBoundBox.setUpper(-1.0d, -1.0d, -1.0d);
+ return false;
+ }
+ }
+ else if( boundsObject.boundId == BOUNDING_SPHERE) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ if( this.intersect( sphere) ) {
+ BoundingBox sbox = new BoundingBox( sphere );
+ this.intersect( sbox, newBoundBox );
+ return true;
+ } else {
+ // Negative volume.
+ newBoundBox.setLower( 1.0d, 1.0d, 1.0d);
+ newBoundBox.setUpper(-1.0d, -1.0d, -1.0d);
+ return false;
+ }
+
+ // System.out.println("intersect Sphere ");
+ }
+ else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ if( this.intersect( polytope)) {
+ BoundingBox pbox = new BoundingBox( polytope); // convert polytope to box
+ this.intersect( pbox, newBoundBox );
+ return true;
+ } else {
+ // Negative volume.
+ newBoundBox.setLower( 1.0d, 1.0d, 1.0d);
+ newBoundBox.setUpper(-1.0d, -1.0d, -1.0d);
+ return false;
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox7"));
+ }
+ }
+
+ /**
+ * Test for intersection with an array of bounds objects.
+ * @param boundsObjects an array of bounds objects
+ * @param newBoundBox the new bounding box which is the intersection of
+ * the boundsObject and this BoundingBox
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds[] boundsObjects, BoundingBox newBoundBox) {
+
+ if( boundsObjects == null || boundsObjects.length <= 0 || boundsIsEmpty ) {
+ // Negative volume.
+ newBoundBox.setLower( 1.0d, 1.0d, 1.0d);
+ newBoundBox.setUpper(-1.0d, -1.0d, -1.0d);
+ return false;
+ }
+
+ int i=0;
+ // find first non null bounds object
+ while( boundsObjects[i] == null && i < boundsObjects.length) {
+ i++;
+ }
+
+ if( i >= boundsObjects.length ) { // all bounds objects were empty
+ // Negative volume.
+ newBoundBox.setLower( 1.0d, 1.0d, 1.0d);
+ newBoundBox.setUpper(-1.0d, -1.0d, -1.0d);
+ return false;
+ }
+
+
+ boolean status = false;
+ BoundingBox tbox = new BoundingBox();
+
+ for(;i<boundsObjects.length;i++) {
+ if( boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty) ;
+ else if( boundsObjects[i].boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ // both boxes are axis aligned
+ if( upper.x > box.lower.x && box.upper.x > lower.x &&
+ upper.y > box.lower.y && box.upper.y > lower.y &&
+ upper.z > box.lower.z && box.upper.z > lower.z ){
+
+ if(upper.x > box.upper.x)
+ newBoundBox.upper.x = box.upper.x;
+ else
+ newBoundBox.upper.x = upper.x;
+
+ if(upper.y > box.upper.y)
+ newBoundBox.upper.y = box.upper.y;
+ else
+ newBoundBox.upper.y = upper.y;
+
+ if(upper.z > box.upper.z)
+ newBoundBox.upper.z = box.upper.z;
+ else
+ newBoundBox.upper.z = upper.z;
+
+ if(lower.x < box.lower.x)
+ newBoundBox.lower.x = box.lower.x;
+ else
+ newBoundBox.lower.x = lower.x;
+
+ if(lower.y < box.lower.y)
+ newBoundBox.lower.y = box.lower.y;
+ else
+ newBoundBox.lower.y = lower.y;
+
+ if(lower.z < box.lower.z)
+ newBoundBox.lower.z = box.lower.z;
+ else
+ newBoundBox.lower.z = lower.z;
+ status = true;
+ newBoundBox.updateBoundsStates();
+ }
+ }
+ else if( boundsObjects[i].boundId == BOUNDING_SPHERE) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ if( this.intersect(sphere)) {
+ BoundingBox sbox = new BoundingBox( sphere ); // convert sphere to box
+ this.intersect(sbox,tbox); // insersect two boxes
+ if( status ) {
+ newBoundBox.combine( tbox );
+ } else {
+ newBoundBox.set( tbox );
+ status = true;
+ }
+ }
+
+ }
+ else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ if( this.intersect( polytope)) {
+ BoundingBox pbox = new BoundingBox( polytope ); // convert polytope to box
+ this.intersect(pbox,tbox); // insersect two boxes
+ if ( status ) {
+ newBoundBox.combine( tbox );
+ } else {
+ newBoundBox.set( tbox );
+ status = true;
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox6"));
+ }
+
+ if(newBoundBox.boundsIsInfinite)
+ break; // We're done.
+ }
+ if( status == false ) {
+ // Negative volume.
+ newBoundBox.setLower( 1.0d, 1.0d, 1.0d);
+ newBoundBox.setUpper(-1.0d, -1.0d, -1.0d);
+ }
+ return status;
+ }
+
+
+ /**
+ * Finds closest bounding object that intersects this bounding box.
+ * @param boundsObjects an array of bounds objects
+ * @return closest bounding object
+ */
+ public Bounds closestIntersection( Bounds[] boundsObjects) {
+
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ return null;
+ }
+
+ if( boundsIsEmpty ) {
+ return null;
+ }
+
+ getCenter();
+
+ double dis,far_dis,pdist,x,y,z,rad_sq;
+ double cenX = 0.0, cenY = 0.0, cenZ = 0.0;
+ boolean contains = false;
+ boolean inside;
+ boolean intersect = false;
+ double smallest_distance = Double.MAX_VALUE;
+ int i,j,index=0;
+
+ for(i = 0; i < boundsObjects.length; i++){
+ if( boundsObjects[i] == null ) ;
+
+ else if( this.intersect( boundsObjects[i])) {
+ intersect = true;
+ if( boundsObjects[i].boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ cenX = (box.upper.x+box.lower.x)/2.0;
+ cenY = (box.upper.y+box.lower.y)/2.0;
+ cenZ = (box.upper.z+box.lower.z)/2.0;
+ dis = Math.sqrt( (centroid.x-cenX)*(centroid.x-cenX) +
+ (centroid.y-cenY)*(centroid.y-cenY) +
+ (centroid.z-cenZ)*(centroid.z-cenZ) );
+ inside = false;
+
+ if( lower.x <= box.lower.x &&
+ lower.y <= box.lower.y &&
+ lower.z <= box.lower.z &&
+ upper.x >= box.upper.x &&
+ upper.y >= box.upper.y &&
+ upper.z >= box.upper.z ) { // box is contained
+ inside = true;
+ }
+ if( inside ) {
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+
+ }
+ else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ dis = Math.sqrt( (centroid.x-sphere.center.x)*
+ (centroid.x-sphere.center.x) +
+ (centroid.y-sphere.center.y)*
+ (centroid.y-sphere.center.y) +
+ (centroid.z-sphere.center.z)*
+ (centroid.z-sphere.center.z) );
+
+ inside = false;
+
+ // sphere sphere.center is inside box
+ if(sphere.center.x <= upper.x && sphere.center.x >= lower.x &&
+ sphere.center.y <= upper.y && sphere.center.y >= lower.y &&
+ sphere.center.z <= upper.z && sphere.center.z >= lower.z ) {
+ // check if sphere intersects any side
+ if (sphere.center.x - lower.x >= sphere.radius &&
+ upper.x - sphere.center.x >= sphere.radius &&
+ sphere.center.y - lower.y >= sphere.radius &&
+ upper.y - sphere.center.y >= sphere.radius &&
+ sphere.center.z - lower.z >= sphere.radius &&
+ upper.z - sphere.center.z >= sphere.radius ) {
+ // contains the sphere
+ inside = true;
+ }
+ }
+ if (inside ) {
+ // initialize smallest_distance for the first containment
+ if( !contains ){
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ }
+ else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope =
+ (BoundingPolytope)boundsObjects[i];
+ dis = Math.sqrt( (centroid.x-polytope.centroid.x)*
+ (centroid.x-polytope.centroid.x) +
+ (centroid.y-polytope.centroid.y)*
+ (centroid.y-polytope.centroid.y) +
+ (centroid.z-polytope.centroid.z)*
+ (centroid.z-polytope.centroid.z) );
+ inside = true;
+ for(j=0;j<polytope.nVerts;j++) {
+ if( polytope.verts[j].x < lower.x ||
+ polytope.verts[j].y < lower.y ||
+ polytope.verts[j].z < lower.z ||
+ polytope.verts[j].x > upper.x ||
+ polytope.verts[j].y > upper.y ||
+ polytope.verts[j].z > upper.z ) { // box contains polytope
+ inside = false;
+
+ }
+
+ }
+ if( inside ) {
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingBox9"));
+ }
+ }
+ }
+
+ if ( intersect )
+ return boundsObjects[index];
+ else
+ return null;
+ }
+
+ /**
+ * Tests for intersection of box and frustum.
+ * @param frustum
+ * @return true if they intersect
+ */
+ boolean intersect( CachedFrustum frustum ) {
+
+ if (boundsIsEmpty)
+ return false;
+
+ if(boundsIsInfinite)
+ return true;
+
+ // System.out.println("intersect frustum with box="+this.toString());
+ // System.out.println("frustum "+frustum.toString());
+ // check if box and bounding box of frustum intersect
+ if (upper.x > frustum.lower.x &&
+ lower.x < frustum.upper.x &&
+ upper.y > frustum.lower.y &&
+ lower.y < frustum.upper.y &&
+ upper.z > frustum.lower.z &&
+ lower.z < frustum.upper.z ) {
+ // check if all box points out any frustum plane
+ int i = 5;
+ while (i>=0){
+ Vector4d vc = frustum.clipPlanes[i--];
+ if ((( upper.x*vc.x + upper.y*vc.y +
+ upper.z*vc.z + vc.w ) < EPS ) &&
+ (( upper.x*vc.x + lower.y*vc.y +
+ upper.z*vc.z + vc.w ) < EPS ) &&
+ (( upper.x*vc.x + lower.y*vc.y +
+ lower.z*vc.z + vc.w ) < EPS ) &&
+ (( upper.x*vc.x + upper.y*vc.y +
+ lower.z*vc.z + vc.w ) < EPS ) &&
+ (( lower.x*vc.x + upper.y*vc.y +
+ upper.z*vc.z + vc.w ) < EPS ) &&
+ (( lower.x*vc.x + lower.y*vc.y +
+ upper.z*vc.z + vc.w ) < EPS ) &&
+ (( lower.x*vc.x + lower.y*vc.y +
+ lower.z*vc.z + vc.w ) < EPS ) &&
+ (( lower.x*vc.x + upper.y*vc.y +
+ lower.z*vc.z + vc.w ) < EPS )) {
+ // all corners outside this frustum plane
+ return false;
+ }
+ }
+
+ // check if any box corner is inside of the frustum silhoette edges in the 3 views
+ // y-z
+ Point4d edge;
+ for (i=frustum.nxEdges-1; i >=0; i--){
+ edge = frustum.xEdges[frustum.xEdgeList[i]];
+ if ( ((upper.y*edge.y +
+ upper.z*edge.z + edge.w ) < EPS ) ||
+ (( upper.y*edge.y +
+ lower.z*edge.z + edge.w ) < EPS) ||
+ (( lower.y*edge.y +
+ upper.z*edge.z + edge.w ) < EPS ) ||
+ (( lower.y*edge.y +
+ lower.z*edge.z + edge.w ) < EPS )) {
+ break;
+ }
+ }
+
+ if ( i < 0) {
+ return false; // all box corners outside yz silhouette edges
+ }
+
+
+ // x-z
+ for(i=frustum.nyEdges-1; i >=0; i--){
+ edge = frustum.yEdges[frustum.yEdgeList[i]];
+ if ((( upper.x*edge.x +
+ upper.z*edge.z + edge.w ) < EPS ) ||
+ (( upper.x*edge.x +
+ lower.z*edge.z + edge.w ) < EPS ) ||
+ (( lower.x*edge.x +
+ upper.z*edge.z + edge.w ) < EPS ) ||
+ (( lower.x*edge.x +
+ lower.z*edge.z + edge.w ) < EPS ) ) {
+ break;
+ }
+ }
+ if (i < 0) {
+ return false; // all box corners outside xz silhouette edges
+ }
+
+ // x-y
+ for(i=frustum.nzEdges-1; i >=0; i--){
+ edge = frustum.zEdges[frustum.zEdgeList[i]];
+ if ((( upper.y*edge.y +
+ upper.z*edge.z + edge.w ) < EPS ) ||
+ (( upper.y*edge.y +
+ lower.z*edge.z + edge.w ) < EPS ) ||
+ (( lower.y*edge.y +
+ upper.z*edge.z + edge.w ) < EPS ) ||
+ (( lower.y*edge.y +
+ lower.z*edge.z + edge.w ) < EPS ) ) {
+ break;
+ }
+ }
+
+ if (i < 0) {
+ return false; // all box corners outside xy silhouette edges
+ }
+ } else {
+ // System.out.println("false box and bounding box of frustum do not intersect");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this class.
+ */
+ public String toString() {
+ return new String( "Bounding box: Lower="+lower.x+" "+
+ lower.y+" "+lower.z+" Upper="+upper.x+" "+
+ upper.y+" "+upper.z );
+ }
+
+ private void updateBoundsStates() {
+ if((lower.x == Double.NEGATIVE_INFINITY) &&
+ (lower.y == Double.NEGATIVE_INFINITY) &&
+ (lower.z == Double.NEGATIVE_INFINITY) &&
+ (upper.x == Double.POSITIVE_INFINITY) &&
+ (upper.y == Double.POSITIVE_INFINITY) &&
+ (upper.z == Double.POSITIVE_INFINITY)) {
+ boundsIsEmpty = false;
+ boundsIsInfinite = true;
+ return;
+ }
+
+ if (checkBoundsIsNaN()) {
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ return;
+ }
+ else {
+ boundsIsInfinite = false;
+ if( lower.x > upper.x ||
+ lower.y > upper.y ||
+ lower.z > upper.z ) {
+ boundsIsEmpty = true;
+ } else {
+ boundsIsEmpty = false;
+ }
+ }
+ }
+
+ // For a infinite bounds. What is the centroid ?
+ Point3d getCenter() {
+ if(centroid == null) {
+ centroid = new Point3d();
+ }
+
+ centroid.x = (upper.x+lower.x)*0.5;
+ centroid.y = (upper.y+lower.y)*0.5;
+ centroid.z = (upper.z+lower.z)*0.5;
+
+ return centroid;
+ }
+
+ void translate(BoundingBox bbox, Vector3d value) {
+ if (bbox == null || bbox.boundsIsEmpty) {
+ // Negative volume.
+ setLower( 1.0d, 1.0d, 1.0d);
+ setUpper(-1.0d, -1.0d, -1.0d);
+ return;
+ }
+ if(bbox.boundsIsInfinite) {
+ this.set(bbox);
+ return;
+ }
+
+ lower.x = bbox.lower.x + value.x;
+ lower.y = bbox.lower.y + value.y;
+ lower.z = bbox.lower.z + value.z;
+ upper.x = bbox.upper.x + value.x;
+ upper.y = bbox.upper.y + value.y;
+ upper.z = bbox.upper.z + value.z;
+ }
+
+
+ /**
+ * if the passed the "region" is same type as this object
+ * then do a copy, otherwise clone the Bounds and
+ * return
+ */
+ Bounds copy(Bounds r) {
+ if (r != null && this.boundId == r.boundId) {
+ BoundingBox region = (BoundingBox) r;
+ region.lower.x = lower.x;
+ region.lower.y = lower.y;
+ region.lower.z = lower.z;
+ region.upper.x = upper.x;
+ region.upper.y = upper.y;
+ region.upper.z = upper.z;
+ region.boundsIsEmpty = boundsIsEmpty;
+ region.boundsIsInfinite = boundsIsInfinite;
+ return region;
+ }
+ else {
+ return (Bounds) this.clone();
+ }
+ }
+
+ // Check is any of the bounds is a NaN, if yes, then
+ // set it an empty bounds
+ boolean checkBoundsIsNaN() {
+ if (Double.isNaN(lower.x+lower.y+lower.z+upper.x+upper.y+upper.z)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ int getPickType() {
+ return PickShape.PICKBOUNDINGBOX;
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/BoundingLeaf.java b/src/classes/share/javax/media/j3d/BoundingLeaf.java
new file mode 100644
index 0000000..15fea39
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BoundingLeaf.java
@@ -0,0 +1,159 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The BoundingLeaf node defines a bounding region object that can be
+ * referenced by other nodes to define a region of influence
+ * (Fog and Light nodes), an application region (Background, Clip,
+ * and Soundscape nodes), or a scheduling region (Sound and
+ * Behavior nodes). The bounding region is defined in the local
+ * coordinate system of the BoundingLeaf node. A reference to a
+ * BoundingLeaf node can be used in place
+ * of a locally defined bounds object for any of the aforementioned regions.
+ * <P>
+ * This allows an application to specify a bounding region in one coordinate system
+ * (the local coordinate system of the BoundingLeaf node) other than the local
+ * coordinate system of the node that references the bounds. For an example of how
+ * this might be used, consider a closed room with a number of track lights. Each
+ * light can move independent of the other lights and, as such, needs its own local
+ * coordinate system. However, the bounding volume is used by all the lights in the
+ * boundary of the room, which doesn't move when the lights move. In this example,
+ * the BoundingLeaf node allows the bounding region to be defined in the local
+ * coordinate system of the room, rather than in the local coordinate system of a
+ * particular light. All lights can then share this single bounding volume.
+ */
+public class BoundingLeaf extends Leaf {
+ /**
+ * Specifies that this BoundingLeaf node allows read access to its
+ * bounding region object.
+ */
+ public static final int
+ ALLOW_REGION_READ = CapabilityBits.BOUNDING_LEAF_ALLOW_REGION_READ;
+
+ /**
+ * Specifies that this BoundingLeaf node allows write access to its
+ * bounding region object.
+ */
+ public static final int
+ ALLOW_REGION_WRITE = CapabilityBits.BOUNDING_LEAF_ALLOW_REGION_WRITE;
+
+ /**
+ * Constructs a BoundingLeaf node with a null (empty) bounding region.
+ */
+ public BoundingLeaf() {
+ ((BoundingLeafRetained)this.retained).createBoundingLeaf();
+ }
+
+ /**
+ * Constructs a BoundingLeaf node with the specified bounding region.
+ * @param region the bounding region of this leaf node
+ */
+ public BoundingLeaf(Bounds region) {
+ ((BoundingLeafRetained)this.retained).createBoundingLeaf();
+ ((BoundingLeafRetained)this.retained).initRegion(region);
+ }
+
+ /**
+ * Sets this BoundingLeaf node's bounding region.
+ * @param region the bounding region of this leaf node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setRegion(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_REGION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("BoundingLeaf0"));
+
+ if (isLive())
+ ((BoundingLeafRetained)this.retained).setRegion(region);
+ else
+ ((BoundingLeafRetained)this.retained).initRegion(region);
+ }
+
+ /**
+ * Retrieves this BoundingLeaf's bounding region.
+ * @return the bounding region of this leaf node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getRegion() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_REGION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("BoundingLeaf1"));
+
+ return ((BoundingLeafRetained)this.retained).getRegion();
+ }
+
+ /**
+ * Creates the BoundingLeafRetained object that this
+ * BoundingLeaf object will point to.
+ */
+ void createRetained() {
+ this.retained = new BoundingLeafRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * 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) {
+ BoundingLeaf bl = new BoundingLeaf();
+ bl.duplicateNode(this, forceDuplicate);
+ return bl;
+ }
+
+
+
+ /**
+ * Copies all BoundingLeaf 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ((BoundingLeafRetained) retained).initRegion(
+ ((BoundingLeafRetained) originalNode.retained).getRegion());
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/BoundingLeafRetained.java b/src/classes/share/javax/media/j3d/BoundingLeafRetained.java
new file mode 100644
index 0000000..887151a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BoundingLeafRetained.java
@@ -0,0 +1,269 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+/**
+ * The BoundingLeaf node defines a bounding region object that can be
+ * referenced by other nodes to define a region of influence, an
+ * application region, or a scheduling region.
+ */
+class BoundingLeafRetained extends LeafRetained {
+ // Statics used when something in the boundingleaf changes
+ static final int REGION_CHANGED = 0x0001;
+ static final Integer REGION_CHANGED_MESSAGE = new Integer(REGION_CHANGED);
+
+ // The bounding region object defined by this node
+ Bounds region = null;
+
+
+ // For the mirror object, this region is the transformed region
+ // (the region of the original bounding leaf object transformed
+ // by the cache transform)
+ Bounds transformedRegion = null;
+
+ BoundingLeafRetained mirrorBoundingLeaf;
+
+ // A list of Objects that refer, directly or indirectly, to this
+ // bounding leaf object
+ ArrayList users = new ArrayList();
+
+ // Target threads to be notified when light changes
+ int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+
+ // Target threads for tranform change
+ int transformTargetThreads =
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT | J3dThread.UPDATE_GEOMETRY;
+
+ BoundingLeafRetained() {
+ this.nodeType = NodeRetained.BOUNDINGLEAF;
+ }
+
+ void createBoundingLeaf() {
+ this.nodeType = NodeRetained.BOUNDINGLEAF;
+ mirrorBoundingLeaf = new BoundingLeafRetained();
+ }
+
+ /**
+ * Initialize the bounding region
+ */
+ void initRegion(Bounds region) {
+ if (region != null) {
+ this.region = (Bounds) region.clone();
+ }
+ else {
+ this.region = null;
+ }
+ if (staticTransform != null) {
+ this.region.transform(staticTransform.transform);
+ }
+ }
+
+ /**
+ * Set the bounding region
+ */
+ void setRegion(Bounds region) {
+ initRegion(region);
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = mirrorBoundingLeaf.targetThreads;
+ createMessage.type = J3dMessage.BOUNDINGLEAF_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= REGION_CHANGED_MESSAGE;
+ if (region != null) {
+ createMessage.args[2] = (Bounds)(region.clone());
+ } else {
+ createMessage.args[2] = null;
+ }
+ createMessage.args[3] = mirrorBoundingLeaf.users.toArray();
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+
+ /**
+ * Get the bounding region
+ */
+ Bounds getRegion() {
+ Bounds b = null;
+ if (this.region != null) {
+ b = (Bounds) this.region.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+
+ void setLive(SetLiveState s) {
+ super.doSetLive(s);
+
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("BoundingLeafRetained0"));
+ }
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("BoundingLeafRetained1"));
+ }
+
+
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorBoundingLeaf,
+ Targets.BLN_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ mirrorBoundingLeaf.localToVworld = new Transform3D[1][];
+ mirrorBoundingLeaf.localToVworldIndex = new int[1][];
+ mirrorBoundingLeaf.localToVworld[0] = this.localToVworld[0];
+ mirrorBoundingLeaf.localToVworldIndex[0] = this.localToVworldIndex[0];
+ mirrorBoundingLeaf.parent = parent;
+ if (region != null) {
+ mirrorBoundingLeaf.region = (Bounds)region.clone();
+ mirrorBoundingLeaf.transformedRegion = (Bounds)region.clone();
+ mirrorBoundingLeaf.transformedRegion.transform(
+ mirrorBoundingLeaf.getCurrentLocalToVworld());
+ } else {
+ mirrorBoundingLeaf.region = null;
+ mirrorBoundingLeaf.transformedRegion = null;
+ }
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorBoundingLeaf,
+ Targets.BLN_TARGETS);
+ }
+ mirrorBoundingLeaf.switchState = (SwitchState)s.switchStates.get(0);
+ super.markAsLive();
+ }
+
+
+ /** Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateImmediateMirrorObject(Object[] objs) {
+
+ int component = ((Integer)objs[1]).intValue();
+ Bounds b = ((Bounds)objs[2]);
+ Transform3D t;
+
+ if ((component & REGION_CHANGED) != 0) {
+ mirrorBoundingLeaf.region = b;
+ if (b != null) {
+ mirrorBoundingLeaf.transformedRegion = (Bounds)b.clone();
+ t = mirrorBoundingLeaf.getCurrentLocalToVworld();
+ mirrorBoundingLeaf.transformedRegion.transform(b, t);
+ }
+ else {
+ mirrorBoundingLeaf.transformedRegion = null;
+ }
+
+ }
+ }
+
+ /**
+ * Add a user to the list of users.
+ * There is no if (node.source.isLive()) check since
+ * mirror objects are the users of the mirror bounding leaf
+ * and they do not have a source.
+ */
+ synchronized void addUser(LeafRetained node) {
+ users.add(node);
+ if (node.nodeType == NodeRetained.BACKGROUND ||
+ node.nodeType == NodeRetained.CLIP ||
+ node.nodeType == NodeRetained.ALTERNATEAPPEARANCE ||
+ node instanceof FogRetained ||
+ node instanceof LightRetained) {
+ transformTargetThreads |= J3dThread.UPDATE_RENDER;
+ }
+ else if (node instanceof BehaviorRetained) {
+ transformTargetThreads |= J3dThread.UPDATE_BEHAVIOR;
+ targetThreads |= J3dThread.UPDATE_BEHAVIOR;
+ }
+ else if (node instanceof SoundRetained ||
+ node.nodeType == NodeRetained.SOUNDSCAPE) {
+ transformTargetThreads |= J3dThread.UPDATE_SOUND;
+ }
+
+ }
+
+ /**
+ * Remove user from the list of users.
+ * There is no if (node.source.isLive()) check since
+ * mirror objects are the users of the mirror bounding leaf
+ * and they do not have a source.
+ */
+ synchronized void removeUser(LeafRetained u) {
+ int i;
+ users.remove(users.indexOf(u));
+ // For now reconstruct the transform target threads from scratch
+ transformTargetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+ for (i =0; i < users.size(); i++) {
+ LeafRetained node = (LeafRetained)users.get(i);
+ if (node.nodeType == NodeRetained.BACKGROUND ||
+ node.nodeType == NodeRetained.CLIP ||
+ node.nodeType == NodeRetained.ALTERNATEAPPEARANCE ||
+ node instanceof FogRetained ||
+ node instanceof LightRetained) {
+ transformTargetThreads |= J3dThread.UPDATE_RENDER;
+ }
+ else if (node.nodeType == NodeRetained.BEHAVIOR) {
+ transformTargetThreads |= J3dThread.UPDATE_BEHAVIOR;
+ targetThreads |= J3dThread.UPDATE_BEHAVIOR;
+ }
+ else if (node instanceof SoundRetained ||
+ node.nodeType == NodeRetained.SOUNDSCAPE) {
+ transformTargetThreads |= J3dThread.UPDATE_SOUND;
+ }
+ }
+ }
+
+
+ // This function is called on the mirror bounding leaf
+ void updateImmediateTransformChange() {
+ Transform3D t;
+ t = getCurrentLocalToVworld();
+ if (region != null) {
+ transformedRegion.transform(region, t);
+ }
+ }
+
+ void clearLive(SetLiveState s) {
+ super.clearLive();
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorBoundingLeaf,
+ Targets.BLN_TARGETS);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorBoundingLeaf,
+ Targets.BLN_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ region.transform(xform.transform);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/BoundingPolytope.java b/src/classes/share/javax/media/j3d/BoundingPolytope.java
new file mode 100644
index 0000000..2c6ae33
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BoundingPolytope.java
@@ -0,0 +1,1741 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * A BoundingPolytope defines a polyhedral bounding region using the
+ * intersection of four or more half spaces. The region defined by a
+ * BoundingPolytope is always convex and must be closed.
+ * <p>
+ * Each plane in the BoundingPolytope specifies a half-space defined
+ * by the equation:
+ * <ul>
+ * Ax + By + Cz + D <= 0
+ * </ul>
+ * where A, B, C, D are the parameters that specify the plane. The
+ * parameters are passed in the x, y, z, and w fields, respectively,
+ * of a Vector4d object. The intersection of the set of half-spaces
+ * corresponding to the planes in this BoundingPolytope defines the
+ * bounding region.
+ */
+
+public class BoundingPolytope extends Bounds {
+
+ /**
+ * An array of bounding planes.
+ */
+ Vector4d[] planes;
+ double[] mag; // magnitude of plane vector
+ double[] pDotN; // point on plane dotted with normal
+ Point3d[] verts; // vertices of polytope
+ int nVerts; // number of verts in polytope
+ Point3d centroid = new Point3d(); // centroid of polytope
+
+ Point3d boxVerts[];
+ boolean allocBoxVerts = false;
+
+ /**
+ * Constructs a BoundingPolytope using the specified planes.
+ * @param planes a set of planes defining the polytope.
+ * @exception IllegalArgumentException if the length of the
+ * specified array of planes is less than 4.
+ */
+ public BoundingPolytope(Vector4d[] planes) {
+ if (planes.length < 4) {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope11"));
+ }
+
+ boundId = BOUNDING_POLYTOPE;
+ int i;
+ double invMag;
+ this.planes = new Vector4d[planes.length];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+
+ for(i=0;i<planes.length;i++) {
+
+ // normalize the plane normals
+ mag[i] = Math.sqrt(planes[i].x*planes[i].x + planes[i].y*planes[i].y +
+ planes[i].z*planes[i].z);
+ invMag = 1.0/mag[i];
+ this.planes[i] = new Vector4d( planes[i].x*invMag, planes[i].y*invMag,
+ planes[i].z*invMag, planes[i].w*invMag );
+
+ }
+ computeAllVerts(); // TODO lazy evaluate
+ }
+
+ /**
+ * Constructs a BoundingPolytope and initializes it to a set of 6
+ * planes that defines a cube such that -1 <= x,y,z <= 1. The
+ * values of the planes are as follows:
+ * <ul>
+ * planes[0] : x <= 1 (1,0,0,-1)<br>
+ * planes[1] : -x <= 1 (-1,0,0,-1)<br>
+ * planes[2] : y <= 1 (0,1,0,-1)<br>
+ * planes[3] : -y <= 1 (0,-1,0,-1)<br>
+ * planes[4] : z <= 1 (0,0,1,-1)<br>
+ * planes[5] : -z <= 1 (0,0,-1,-1)<br>
+ * </ul>
+ */
+ public BoundingPolytope() {
+ boundId = BOUNDING_POLYTOPE;
+ planes = new Vector4d[6];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -1.0 );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0 );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -1.0 );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, -1.0 );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -1.0 );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, -1.0 );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+
+ computeAllVerts(); // TODO lazy evaluate
+ }
+
+
+ /**
+ * Constructs a BoundingPolytope from the specified bounds object.
+ * The new polytope will circumscribe the region specified by the
+ * input bounds.
+ * @param boundsObject the bounds object from which this polytope
+ * is constructed.
+ */
+ public BoundingPolytope(Bounds boundsObject ) {
+ int i;
+
+ boundId = BOUNDING_POLYTOPE;
+
+ if( boundsObject == null ) {
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ initEmptyPolytope();
+ computeAllVerts(); // TODO lazy evaluate
+ return;
+ }
+
+ boundsIsEmpty = boundsObject.boundsIsEmpty;
+ boundsIsInfinite = boundsObject.boundsIsInfinite;
+
+ if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ planes = new Vector4d[6];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -(sphere.center.x+sphere.radius) );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, sphere.center.x-sphere.radius );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -(sphere.center.y+sphere.radius) );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, sphere.center.y-sphere.radius );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -(sphere.center.z+sphere.radius) );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, sphere.center.z-sphere.radius );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+ computeAllVerts(); // TODO lazy evaluate
+
+ } else if( boundsObject.boundId == BOUNDING_BOX ){
+ BoundingBox box = (BoundingBox)boundsObject;
+ planes = new Vector4d[6];
+ pDotN = new double[planes.length];
+ mag = new double[planes.length];
+
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -box.upper.x );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, box.lower.x );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -box.upper.y );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, box.lower.y );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -box.upper.z );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, box.lower.z );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+ computeAllVerts(); // TODO lazy evaluate
+
+ } else if( boundsObject.boundId == BOUNDING_POLYTOPE ) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ planes = new Vector4d[polytope.planes.length];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+ nVerts = polytope.nVerts;
+ verts = new Point3d[nVerts];
+ for(i=0;i<planes.length;i++) {
+ planes[i] = new Vector4d(polytope.planes[i]);
+ mag[i] = polytope.mag[i];
+ pDotN[i] = polytope.pDotN[i];
+ }
+ for(i=0;i<verts.length;i++) {
+ verts[i] = new Point3d(polytope.verts[i]);
+ }
+ centroid = polytope.centroid;
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope0"));
+ }
+ }
+
+ /**
+ * Constructs a BoundingPolytope from the specified array of bounds
+ * objects. The new polytope will circumscribe the union of the
+ * regions specified by the input bounds objects.
+ * @param boundsObjects the array bounds objects from which this
+ * polytope is constructed.
+ */
+ public BoundingPolytope(Bounds[] boundsObjects) {
+ int i=0;
+
+ boundId = BOUNDING_POLYTOPE;
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ initEmptyPolytope();
+ computeAllVerts(); // TODO lazy evaluate
+ return;
+ }
+ // find first non empty bounds object
+ while( boundsObjects[i] == null && i < boundsObjects.length) {
+ i++;
+ }
+
+ if( i >= boundsObjects.length ) { // all bounds objects were empty
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ initEmptyPolytope();
+ computeAllVerts(); // TODO lazy evaluate
+ return;
+ }
+
+ boundsIsEmpty = boundsObjects[i].boundsIsEmpty;
+ boundsIsInfinite = boundsObjects[i].boundsIsInfinite;
+
+ if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ planes = new Vector4d[6];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -(sphere.center.x+sphere.radius) );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, sphere.center.x-sphere.radius );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -(sphere.center.y+sphere.radius) );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, sphere.center.y-sphere.radius );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -(sphere.center.z+sphere.radius) );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, sphere.center.z-sphere.radius );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+
+ computeAllVerts(); // TODO lazy evaluate
+ } else if( boundsObjects[i].boundId == BOUNDING_BOX ){
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ planes = new Vector4d[6];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -box.upper.x );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, box.lower.x );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -box.upper.y );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, box.lower.y );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -box.upper.z );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, box.lower.z );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+
+ computeAllVerts(); // TODO lazy evaluate
+ } else if( boundsObjects[i].boundId == BOUNDING_POLYTOPE ) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ planes = new Vector4d[polytope.planes.length];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+ nVerts = polytope.nVerts;
+ verts = new Point3d[nVerts];
+ for(i=0;i<planes.length;i++) {
+ planes[i] = new Vector4d(polytope.planes[i]);
+ pDotN[i] = polytope.pDotN[i];
+ mag[i] = polytope.mag[i];
+ }
+ for(i=0;i<verts.length;i++) {
+ verts[i] = new Point3d(polytope.verts[i]);
+ }
+ centroid = polytope.centroid;
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope1"));
+ }
+ for(i+=1;i<boundsObjects.length;i++) {
+ this.combine(boundsObjects[i]);
+ }
+ }
+
+ /**
+ * Sets the bounding planes for this polytope.
+ * @param planes the new set of planes for this polytope
+ * @exception IllegalArgumentException if the length of the
+ * specified array of planes is less than 4.
+ */
+ public void setPlanes(Vector4d[] planes) {
+ if (planes.length < 4) {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope11"));
+ }
+
+ int i;
+ double invMag;
+
+ this.planes = new Vector4d[planes.length];
+ pDotN = new double[planes.length];
+ mag = new double[planes.length];
+ boundsIsEmpty = false;
+
+ if( planes.length <= 0 ) {
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ computeAllVerts(); // TODO lazy evaluate
+ return;
+ }
+
+ for(i=0;i<planes.length;i++) {
+ // normalize the plane normals
+ mag[i] = Math.sqrt(planes[i].x*planes[i].x + planes[i].y*planes[i].y +
+ planes[i].z*planes[i].z);
+ invMag = 1.0/mag[i];
+ this.planes[i] = new Vector4d( planes[i].x*invMag, planes[i].y*invMag,
+ planes[i].z*invMag, planes[i].w*invMag );
+ }
+ computeAllVerts(); // TODO lazy evaluate
+
+ }
+
+ /**
+ * Returns the equations of the bounding planes for this bounding polytope.
+ * The equations are copied into the specified array.
+ * The array must be large enough to hold all of the vectors.
+ * The individual array elements must be allocated by the caller.
+ * @param planes an array Vector4d to receive the bounding planes
+ */
+ public void getPlanes(Vector4d[] planes)
+ {
+ int i;
+
+ for(i=0;i<planes.length;i++) {
+ planes[i].x = this.planes[i].x*mag[i];
+ planes[i].y = this.planes[i].y*mag[i];
+ planes[i].z = this.planes[i].z*mag[i];
+ planes[i].w = this.planes[i].w*mag[i];
+ }
+ }
+
+ public int getNumPlanes() {
+ return planes.length;
+ }
+
+ /**
+ * Sets the planes for this BoundingPolytope by keeping its current
+ * number and position of planes and computing new planes positions
+ * to enclose the given bounds object.
+ * @param boundsObject another bounds object
+ */
+ public void set(Bounds boundsObject) {
+ int i,k;
+ double dis;
+
+ // no polytope exists yet so initialize one using the boundsObject
+ if( boundsObject == null ) {
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ computeAllVerts(); // TODO lazy evaluate
+
+ }else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+
+ if( boundsIsEmpty) {
+ initEmptyPolytope(); // no ptope exist so must initialize to default
+ computeAllVerts();
+ }
+
+ for(i=0;i<planes.length;i++) { // D = -(N dot C + radius)
+ planes[i].w = -(sphere.center.x*planes[i].x +
+ sphere.center.y*planes[i].y +
+ sphere.center.z*planes[i].z + sphere.radius);
+ }
+
+ boundsIsEmpty = boundsObject.boundsIsEmpty;
+ boundsIsInfinite = boundsObject.boundsIsInfinite;
+ computeAllVerts(); // TODO lazy evaluate
+
+ } else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+ double ux,uy,uz,lx,ly,lz,newD;
+
+ if( boundsIsEmpty) {
+ initEmptyPolytope(); // no ptope exist so must initialize to default
+ computeAllVerts();
+ }
+
+ for(i=0;i<planes.length;i++) {
+ ux = box.upper.x*planes[i].x;
+ uy = box.upper.y*planes[i].y;
+ uz = box.upper.z*planes[i].z;
+ lx = box.lower.x*planes[i].x;
+ ly = box.lower.y*planes[i].y;
+ lz = box.lower.z*planes[i].z;
+ planes[i].w = -(ux + uy + uz ); // initalize plane to upper vert
+ if( (newD = ux + uy + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
+ if( (newD = ux + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD;
+ if( (newD = ux + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
+
+ if( (newD = lx + uy + uz ) + planes[i].w > 0.0) planes[i].w = -newD;
+ if( (newD = lx + uy + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
+ if( (newD = lx + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD;
+ if( (newD = lx + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
+ }
+
+ boundsIsEmpty = boundsObject.boundsIsEmpty;
+ boundsIsInfinite = boundsObject.boundsIsInfinite;
+ computeAllVerts(); // TODO lazy evaluate
+
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ if( planes.length != polytope.planes.length) {
+ planes = new Vector4d[polytope.planes.length];
+ for(k=0;k<polytope.planes.length;k++) planes[k] = new Vector4d();
+ mag = new double[polytope.planes.length];
+ pDotN = new double[polytope.planes.length];
+ }
+
+
+ for(i=0;i<polytope.planes.length;i++) {
+ planes[i].x = polytope.planes[i].x;
+ planes[i].y = polytope.planes[i].y;
+ planes[i].z = polytope.planes[i].z;
+ planes[i].w = polytope.planes[i].w;
+ mag[i] = polytope.mag[i];
+ }
+ nVerts = polytope.nVerts;
+ verts = new Point3d[nVerts];
+ for (k=0; k<nVerts; k++) {
+ verts[k] = new Point3d(polytope.verts[k]);
+ }
+
+ boundsIsEmpty = boundsObject.boundsIsEmpty;
+ boundsIsInfinite = boundsObject.boundsIsInfinite;
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope2"));
+ }
+
+ }
+
+
+ /**
+ * Creates a copy of a polytope.
+ * @return a new BoundingPolytope
+ */
+ public Object clone() {
+ return new BoundingPolytope(planes);
+ }
+
+
+ /**
+ * Indicates whether the specified <code>bounds</code> object is
+ * equal to this BoundingPolytope object. They are equal if the
+ * specified <code>bounds</code> object is an instance of
+ * BoundingPolytope and all of the data
+ * members of <code>bounds</code> are equal to the corresponding
+ * data members in this BoundingPolytope.
+ * @param bounds the object with which the comparison is made.
+ * @return true if this BoundingPolytope is equal to <code>bounds</code>;
+ * otherwise false
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean equals(Object bounds) {
+ try {
+ BoundingPolytope polytope = (BoundingPolytope)bounds;
+ if (planes.length != polytope.planes.length)
+ return false;
+ for (int i = 0; i < planes.length; i++)
+ if (!planes[i].equals(polytope.planes[i]))
+ return false;
+
+ return true;
+ }
+ catch (NullPointerException e) {
+ return false;
+ }
+ catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns a hash code value for this BoundingPolytope object
+ * based on the data values in this object. Two different
+ * BoundingPolytope objects with identical data values (i.e.,
+ * BoundingPolytope.equals returns true) will return the same hash
+ * code value. Two BoundingPolytope objects with different data
+ * members may return the same hash code value, although this is
+ * not likely.
+ * @return a hash code value for this BoundingPolytope object.
+ *
+ * @since Java 3D 1.2
+ */
+ public int hashCode() {
+ long bits = 1L;
+
+ for (int i = 0; i < planes.length; i++) {
+ bits = 31L * bits + Double.doubleToLongBits(planes[i].x);
+ bits = 31L * bits + Double.doubleToLongBits(planes[i].y);
+ bits = 31L * bits + Double.doubleToLongBits(planes[i].z);
+ bits = 31L * bits + Double.doubleToLongBits(planes[i].w);
+ }
+
+ return (int) (bits ^ (bits >> 32));
+ }
+
+
+ /**
+ * Combines this bounding polytope with a bounding object so that the
+ * resulting bounding polytope encloses the original bounding polytope and the
+ * given bounds object.
+ * @param boundsObject another bounds object
+ */
+ public void combine(Bounds boundsObject) {
+ BoundingSphere sphere;
+
+ if((boundsObject == null) || (boundsObject.boundsIsEmpty)
+ || (boundsIsInfinite))
+ return;
+
+
+ if((boundsIsEmpty) || (boundsObject.boundsIsInfinite)) {
+ this.set(boundsObject);
+ return;
+ }
+
+ boundsIsEmpty = boundsObject.boundsIsEmpty;
+ boundsIsInfinite = boundsObject.boundsIsInfinite;
+
+ if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ sphere = (BoundingSphere)boundsObject;
+ int i;
+ double dis;
+ for(i = 0; i < planes.length; i++){
+ dis = sphere.radius+ sphere.center.x*planes[i].x +
+ sphere.center.y*planes[i].y + sphere.center.z *
+ planes[i].z + planes[i].w;
+ if( dis > 0.0 ) {
+ planes[i].w += -dis;
+ }
+ }
+ } else if( boundsObject instanceof BoundingBox){
+ BoundingBox b = (BoundingBox)boundsObject;
+ if( !allocBoxVerts){
+ boxVerts = new Point3d[8];
+ for(int j=0;j<8;j++)boxVerts[j] = new Point3d();
+ allocBoxVerts = true;
+ }
+ boxVerts[0].set(b.lower.x, b.lower.y, b.lower.z );
+ boxVerts[1].set(b.lower.x, b.upper.y, b.lower.z );
+ boxVerts[2].set(b.upper.x, b.lower.y, b.lower.z );
+ boxVerts[3].set(b.upper.x, b.upper.y, b.lower.z );
+ boxVerts[4].set(b.lower.x, b.lower.y, b.upper.z );
+ boxVerts[5].set(b.lower.x, b.upper.y, b.upper.z );
+ boxVerts[6].set(b.upper.x, b.lower.y, b.upper.z );
+ boxVerts[7].set(b.upper.x, b.upper.y, b.upper.z );
+ this.combine(boxVerts);
+
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ this.combine(polytope.verts);
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope3"));
+ }
+
+ computeAllVerts();
+ }
+
+ /**
+ * Combines this bounding polytope with an array of bounding objects so that the
+ * resulting bounding polytope encloses the original bounding polytope and the
+ * given array of bounds object.
+ * @param boundsObjects an array of bounds objects
+ */
+ public void combine(Bounds[] boundsObjects) {
+ int i=0;
+ double dis;
+
+ if( (boundsObjects == null) || (boundsObjects.length <= 0)
+ || (boundsIsInfinite))
+ return;
+
+ // find first non empty bounds object
+ while( (i<boundsObjects.length) && ((boundsObjects[i]==null)
+ || boundsObjects[i].boundsIsEmpty)) {
+ i++;
+ }
+ if( i >= boundsObjects.length)
+ return; // no non empty bounds so do not modify current bounds
+
+ if(boundsIsEmpty)
+ this.set(boundsObjects[i++]);
+
+ if(boundsIsInfinite)
+ return;
+
+ for(;i<boundsObjects.length;i++) {
+ if( boundsObjects[i] == null ); // do nothing
+ else if( boundsObjects[i].boundsIsEmpty ); // do nothing
+ else if( boundsObjects[i].boundsIsInfinite ) {
+ this.set(boundsObjects[i]);
+ break; // We're done;
+ }
+ else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ for(int j = 0; j < planes.length; j++){
+ dis = sphere.radius+ sphere.center.x*planes[j].x +
+ sphere.center.y*planes[j].y + sphere.center.z*
+ planes[j].z + planes[j].w;
+ if( dis > 0.0 ) {
+ planes[j].w += -dis;
+ }
+ }
+ } else if( boundsObjects[i].boundId == BOUNDING_BOX){
+ BoundingBox b = (BoundingBox)boundsObjects[i];
+ if( !allocBoxVerts){
+ boxVerts = new Point3d[8];
+ for(int j=0;j<8;j++)boxVerts[j] = new Point3d();
+ allocBoxVerts = true;
+ }
+ boxVerts[0].set(b.lower.x, b.lower.y, b.lower.z );
+ boxVerts[1].set(b.lower.x, b.upper.y, b.lower.z );
+ boxVerts[2].set(b.upper.x, b.lower.y, b.lower.z );
+ boxVerts[3].set(b.upper.x, b.upper.y, b.lower.z );
+ boxVerts[4].set(b.lower.x, b.lower.y, b.upper.z );
+ boxVerts[5].set(b.lower.x, b.upper.y, b.upper.z );
+ boxVerts[6].set(b.upper.x, b.lower.y, b.upper.z );
+ boxVerts[7].set(b.upper.x, b.upper.y, b.upper.z );
+ this.combine(boxVerts);
+
+ } else if(boundsObjects[i] instanceof BoundingPolytope) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ this.combine(polytope.verts);
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope4"));
+ }
+
+ computeAllVerts();
+ }
+ }
+
+ /**
+ * Combines this bounding polytope with a point.
+ * @param point a 3d point in space
+ */
+ public void combine(Point3d point) {
+ int i;
+ double dis;
+
+ if(boundsIsInfinite) {
+ return;
+ }
+
+ if( boundsIsEmpty ){
+ planes = new Vector4d[6];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+ nVerts = 1;
+ verts = new Point3d[nVerts];
+ verts[0] = new Point3d( point.x, point.y, point.z);
+
+ for(i=0;i<planes.length;i++) {
+ pDotN[i] = 0.0;
+ }
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -point.x );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, point.x );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -point.y );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, point.y );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -point.z );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, point.z );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+ centroid.x = point.x;
+ centroid.y = point.y;
+ centroid.z = point.z;
+ boundsIsEmpty = false;
+ boundsIsInfinite = false;
+ } else {
+
+ for(i = 0; i < planes.length; i++){
+ dis = point.x*planes[i].x + point.y*planes[i].y + point.z*
+ planes[i].z + planes[i].w;
+ if( dis > 0.0 ) {
+ planes[i].w += -dis;
+ }
+ }
+ computeAllVerts();
+ }
+ }
+
+ /**
+ * Combines this bounding polytope with an array of points.
+ * @param points an array of 3d points in space
+ */
+ public void combine(Point3d[] points) {
+ int i,j;
+ double dis;
+
+ if( boundsIsInfinite) {
+ return;
+ }
+
+ if( boundsIsEmpty ){
+ planes = new Vector4d[6];
+ mag = new double[planes.length];
+ pDotN = new double[planes.length];
+ nVerts = points.length;
+ verts = new Point3d[nVerts];
+ verts[0] = new Point3d( points[0].x, points[0].y, points[0].z);
+
+ for(i=0;i<planes.length;i++) {
+ pDotN[i] = 0.0;
+ }
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -points[0].x );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, points[0].x );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -points[0].y );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, points[0].y );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -points[0].z );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, points[0].z );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+ centroid.x = points[0].x;
+ centroid.y = points[0].y;
+ centroid.z = points[0].z;
+ boundsIsEmpty = false;
+ boundsIsInfinite = false;
+ }
+
+ for(j = 0; j < points.length; j++){
+ for(i = 0; i < planes.length; i++){
+ dis = points[j].x*planes[i].x + points[j].y*planes[i].y +
+ points[j].z*planes[i].z + planes[i].w;
+ if( dis > 0.0 ) {
+ planes[i].w += -dis;
+ }
+ }
+ }
+
+ computeAllVerts();
+ }
+
+ /**
+ * Modifies the bounding polytope so that it bounds the volume
+ * generated by transforming the given bounding object.
+ * @param boundsObject the bounding object to be transformed
+ * @param matrix a transformation matrix
+ */
+ public void transform( Bounds boundsObject, Transform3D matrix) {
+
+ if( boundsObject == null || boundsObject.boundsIsEmpty) {
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ computeAllVerts();
+ return;
+ }
+
+ if(boundsObject.boundsIsInfinite) {
+ this.set(boundsObject);
+ return;
+ }
+
+ if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = new BoundingSphere((BoundingSphere)boundsObject);
+ sphere.transform(matrix);
+ this.set(sphere);
+ } else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = new BoundingBox( (BoundingBox)boundsObject);
+ box.transform(matrix);
+ this.set(box);
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = new BoundingPolytope( (BoundingPolytope)boundsObject);
+ polytope.transform(matrix);
+ this.set(polytope);
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope5"));
+ }
+ }
+
+ /**
+ * Transforms this bounding polytope by the given transformation matrix.
+ * @param matrix a transformation matrix
+ */
+ public void transform( Transform3D matrix) {
+
+ if(boundsIsInfinite)
+ return;
+
+ int i;
+ double invMag;
+ Transform3D invTrans = VirtualUniverse.mc.getTransform3D(matrix);
+
+ invTrans.invert();
+ invTrans.transpose();
+
+ for(i = 0; i < planes.length; i++){
+ planes[i].x = planes[i].x * mag[i];
+ planes[i].y = planes[i].y * mag[i];
+ planes[i].z = planes[i].z * mag[i];
+ planes[i].w = planes[i].w * mag[i];
+ invTrans.transform( planes[i] );
+ }
+
+ VirtualUniverse.mc.addToTransformFreeList(invTrans);
+
+ for(i=0;i<planes.length;i++) {
+
+ // normalize the plane normals
+ mag[i] = Math.sqrt(planes[i].x*planes[i].x + planes[i].y*planes[i].y +
+ planes[i].z*planes[i].z);
+ invMag = 1.0/mag[i];
+ this.planes[i] = new Vector4d( planes[i].x*invMag, planes[i].y*invMag,
+ planes[i].z*invMag, planes[i].w*invMag );
+
+ }
+
+ for (i=0; i < verts.length; i++) {
+ matrix.transform(verts[i]);
+ }
+
+ }
+
+ /**
+ * Test for intersection with a ray.
+ * @param origin is a the starting point of the ray
+ * @param direction is the direction of the ray
+ * @param intersectPoint is a point defining the location of the intersection
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Point3d origin, Vector3d direction, Point3d intersectPoint ) {
+
+ double t,v0,vd,x,y,z,invMag;
+ double dx, dy, dz;
+ int i;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ intersectPoint.x = origin.x;
+ intersectPoint.y = origin.y;
+ intersectPoint.z = origin.z;
+ return true;
+ }
+
+ invMag = 1.0/Math.sqrt(direction.x*direction.x +
+ direction.y*direction.y + direction.z*direction.z);
+ dx = direction.x*invMag;
+ dy = direction.y*invMag;
+ dz = direction.z*invMag;
+
+ // compute intersection point of ray and each plane then test if point is in polytope
+ for(i=0;i<planes.length;i++) {
+ vd = planes[i].x*dx + planes[i].y*dy + planes[i].z*dz;
+ v0 = -(planes[i].x*origin.x + planes[i].y*origin.y +
+ planes[i].z*origin.z + planes[i].w);
+ if(vd != 0.0) { // ray is parallel to plane
+ t = v0/vd;
+
+ if( t >= 0.0) { // plane is behind origin
+
+ x = origin.x + dx*t; // compute intersection point
+ y = origin.y + dy*t;
+ z = origin.z + dz*t;
+
+ if( pointInPolytope(x,y,z) ) {
+ intersectPoint.x = x;
+ intersectPoint.y = y;
+ intersectPoint.z = z;
+ return true; // ray intersects a face of polytope
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Test for intersection with a ray
+ * @param origin is a the starting point of the ray
+ * @param direction is the direction of the ray
+ * @param position is a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Point3d origin, Vector3d direction, Point4d position ) {
+ double t,v0,vd,x,y,z,invMag;
+ double dx, dy, dz;
+ int i,j;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = origin.x;
+ position.y = origin.y;
+ position.z = origin.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ invMag = 1.0/Math.sqrt(direction.x*direction.x + direction.y*
+ direction.y + direction.z*direction.z);
+ dx = direction.x*invMag;
+ dy = direction.y*invMag;
+ dz = direction.z*invMag;
+
+ for(i=0;i<planes.length;i++) {
+ vd = planes[i].x*dx + planes[i].y*dy + planes[i].z*dz;
+ v0 = -(planes[i].x*origin.x + planes[i].y*origin.y +
+ planes[i].z*origin.z + planes[i].w);
+ // System.out.println("v0="+v0+" vd="+vd);
+ if(vd != 0.0) { // ray is parallel to plane
+ t = v0/vd;
+
+ if( t >= 0.0) { // plane is behind origin
+
+ x = origin.x + dx*t; // compute intersection point
+ y = origin.y + dy*t;
+ z = origin.z + dz*t;
+ // System.out.println("t="+t+" point="+x+" "+y+" "+z);
+
+ if( pointInPolytope(x,y,z) ) {
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ position.w = t;
+ return true; // ray intersects a face of polytope
+ }
+ }
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Test for intersection with a point
+ * @param point is the pick point
+ * @param position is a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Point3d point, Point4d position ) {
+ int i;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = point.x;
+ position.y = point.y;
+ position.z = point.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ for(i = 0; i < this.planes.length; i++){
+ if(( point.x*this.planes[i].x +
+ point.y*this.planes[i].y +
+ point.z*this.planes[i].z + planes[i].w ) > 0.0 )
+ return false;
+
+ }
+ return true;
+
+ }
+
+ /**
+ * Test for intersection with a segment
+ * @param start is a point defining the start of the line segment
+ * @param end is a point defining the end of the line segment
+ * @param position is a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect( Point3d start, Point3d end, Point4d position ) {
+ double t,v0,vd,x,y,z;
+ int i,j;
+
+ //System.out.println("line segment intersect : planes.length " + planes.length);
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = start.x;
+ position.y = start.y;
+ position.z = start.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ Point3d direction = new Point3d();
+
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ for(i=0;i<planes.length;i++) {
+ vd = planes[i].x*direction.x + planes[i].y*direction.y +
+ planes[i].z*direction.z;
+ v0 = -(planes[i].x*start.x + planes[i].y*start.y +
+ planes[i].z*start.z + planes[i].w);
+ // System.out.println("v0="+v0+" vd="+vd);
+ if(vd != 0.0) { // ray is parallel to plane
+ t = v0/vd;
+
+ // System.out.println("t is " + t);
+
+ if( t >= 0.0) { // plane is behind start
+
+ x = start.x + direction.x*t; // compute intersection point
+ y = start.y + direction.y*t;
+ z = start.z + direction.z*t;
+ // System.out.println("t="+t+" point="+x+" "+y+" "+z);
+
+ if( pointInPolytope(x,y,z) ) {
+ // if((t*t) > (end.x-start.x)*(end.x-start.x) +
+ // (end.y-start.y)*(end.y-start.y) +
+ // (end.z-start.z)*(end.z-start.z)) {
+ if(t <= 1.0) {
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ position.w = t;
+ return true; // ray intersects a face of polytope
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Test for intersection with a ray.
+ * @param origin the starting point of the ray
+ * @param direction the direction of the ray
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Point3d origin, Vector3d direction ) {
+
+ // compute intersection point of ray and each plane then test if point is in polytope
+
+ double t,v0,vd,x,y,z;
+ int i,j;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ return true;
+ }
+
+ for(i=0;i<planes.length;i++) {
+ vd = planes[i].x*direction.x + planes[i].y*direction.y +
+ planes[i].z*direction.z;
+ v0 = -(planes[i].x*origin.x + planes[i].y*origin.y +
+ planes[i].z*origin.z + planes[i].w);
+ if(vd != 0.0) { // ray is parallel to plane
+ t = v0/vd;
+
+ if( t >= 0.0) { // plane is behind origin
+
+ x = origin.x + direction.x*t; // compute intersection point
+ y = origin.y + direction.y*t;
+ z = origin.z + direction.z*t;
+
+ if( pointInPolytope(x,y,z) ) {
+ return true; // ray intersects a face of polytope
+ } else {
+ // System.out.println("point outside polytope");
+ }
+ }
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Tests whether the bounding polytope is empty. A bounding polytope is
+ * empty if it is null (either by construction or as the result of
+ * a null intersection) or if its volume is negative. A bounding polytope
+ * with a volume of zero is <i>not</i> empty.
+ * @return true if the bounding polytope is empty;
+ * otherwise, it returns false
+ */
+ public boolean isEmpty() {
+ // if nVerts > 0 after computeAllVerts(), that means
+ // there is some intersection between 3 planes.
+ return (boundsIsEmpty || (nVerts <= 0));
+ }
+
+ /**
+ * Test for intersection with a point.
+ * @param point a Point defining a position in 3-space
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Point3d point ) {
+
+ int i;
+ if( boundsIsEmpty ) {
+ return false;
+ }
+ if( boundsIsInfinite ) {
+ return true;
+ }
+
+ for(i = 0; i < this.planes.length; i++){
+ if(( point.x*this.planes[i].x +
+ point.y*this.planes[i].y +
+ point.z*this.planes[i].z + planes[i].w ) > 0.0 )
+ return false;
+
+ }
+ return true;
+ }
+
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Bounds boundsObject, Point4d position) {
+ return intersect(boundsObject);
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds boundsObject) {
+
+ if( boundsObject == null ) {
+ return false;
+ }
+
+ if( boundsIsEmpty || boundsObject.boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite || boundsObject.boundsIsInfinite ) {
+ return true;
+ }
+
+ if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ return intersect_ptope_sphere( this, (BoundingSphere)boundsObject);
+ } else if( boundsObject.boundId == BOUNDING_BOX){
+ return intersect_ptope_abox( this, (BoundingBox)boundsObject);
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ return intersect_ptope_ptope( this, (BoundingPolytope)boundsObject);
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope6"));
+ }
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObjects an array of bounding objects
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds[] boundsObjects) {
+
+ double distsq, radsq;
+ BoundingSphere sphere;
+ int i;
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ return false;
+ }
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ for(i = 0; i < boundsObjects.length; i++){
+ if( boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty) ;
+ else if( boundsIsInfinite || boundsObjects[i].boundsIsInfinite ) {
+ return true; // We're done here.
+ }
+ if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ sphere = (BoundingSphere)boundsObjects[i];
+ radsq = sphere.radius;
+ radsq *= radsq;
+ distsq = sphere.center.distanceSquared(sphere.center);
+ if (distsq < radsq) {
+ return true;
+ }
+ } else if(boundsObjects[i].boundId == BOUNDING_BOX){
+ if( this.intersect(boundsObjects[i])) return true;
+ } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ if( this.intersect(boundsObjects[i])) return true;
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope7"));
+ }
+ }
+
+ return false;
+ }
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @param newBoundPolytope the new bounding polytope, which is the intersection of
+ * the boundsObject and this BoundingPolytope
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds boundsObject, BoundingPolytope newBoundPolytope) {
+ int i;
+
+ if((boundsObject == null) || boundsIsEmpty || boundsObject.boundsIsEmpty ) {
+ newBoundPolytope.boundsIsEmpty = true;
+ newBoundPolytope.boundsIsInfinite = false;
+ newBoundPolytope.computeAllVerts();
+ return false;
+ }
+ if(boundsIsInfinite && (!boundsObject.boundsIsInfinite)) {
+ newBoundPolytope.set(boundsObject);
+ return true;
+ }
+ else if((!boundsIsInfinite) && boundsObject.boundsIsInfinite) {
+ newBoundPolytope.set(this);
+ return true;
+ }
+ else if(boundsIsInfinite && boundsObject.boundsIsInfinite) {
+ newBoundPolytope.set(this);
+ return true;
+ }
+
+
+ BoundingBox tbox = new BoundingBox(); // convert sphere to box
+
+ if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ if( this.intersect( sphere)) {
+ BoundingBox sbox = new BoundingBox( sphere ); // convert sphere to box
+ BoundingBox pbox = new BoundingBox( this ); // convert polytope to box
+ pbox.intersect(sbox, tbox); // insersect two boxes
+ newBoundPolytope.set( tbox );
+ return true;
+ }
+ } else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+ if( this.intersect( box)) {
+ BoundingBox pbox = new BoundingBox( this ); // convert polytope to box
+ pbox.intersect(box, tbox); // insersect two boxes
+ newBoundPolytope.set( tbox );
+ return true;
+ }
+
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ if( this.intersect( polytope)) {
+ Vector4d newPlanes[] = new Vector4d[planes.length + polytope.planes.length];
+ for(i=0;i<planes.length;i++) {
+ newPlanes[i] = new Vector4d(planes[i]);
+ }
+ for(i=0;i<polytope.planes.length;i++) {
+ newPlanes[planes.length + i] = new Vector4d(polytope.planes[i]);
+ }
+ BoundingPolytope newPtope= new BoundingPolytope( newPlanes );
+
+ newBoundPolytope.set(newPtope);
+ return true;
+ }
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope8"));
+ }
+
+ newBoundPolytope.boundsIsEmpty = true;
+ newBoundPolytope.boundsIsInfinite = false;
+ newBoundPolytope.computeAllVerts();
+
+ return false;
+ }
+
+ /**
+ * Test for intersection with an array of bounds objects.
+ * @param boundsObjects an array of bounds objects
+ * @param newBoundingPolytope the new bounding polytope, which is the intersection of
+ * the boundsObject and this BoundingPolytope
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds[] boundsObjects, BoundingPolytope newBoundingPolytope) {
+
+ if( boundsObjects == null || boundsObjects.length <= 0 || boundsIsEmpty ) {
+ newBoundingPolytope.boundsIsEmpty = true;
+ newBoundingPolytope.boundsIsInfinite = false;
+ newBoundingPolytope.computeAllVerts();
+ return false;
+ }
+
+ int i=0;
+ // find first non null bounds object
+ while( boundsObjects[i] == null && i < boundsObjects.length) {
+ i++;
+ }
+
+ if( i >= boundsObjects.length ) { // all bounds objects were empty
+ newBoundingPolytope.boundsIsEmpty = true;
+ newBoundingPolytope.boundsIsInfinite = false;
+ newBoundingPolytope.computeAllVerts();
+ return false;
+ }
+
+ boolean status = false;
+ BoundingBox tbox = new BoundingBox(); // convert sphere to box
+
+ for(i=0;i<boundsObjects.length;i++) {
+ if( boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty) ;
+ else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ if( this.intersect( sphere)) {
+ BoundingBox sbox = new BoundingBox( sphere ); // convert sphere to box
+ BoundingBox pbox = new BoundingBox( this ); // convert polytope to box
+ pbox.intersect(sbox, tbox); // insersect two boxes
+ if ( status ) {
+ newBoundingPolytope.combine( tbox );
+ } else {
+ newBoundingPolytope.set( tbox );
+ status = true;
+ }
+ }
+ } else if( boundsObjects[i].boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ if( this.intersect( box) ){
+ BoundingBox pbox = new BoundingBox( this ); // convert polytope to box
+ pbox.intersect(box,tbox); // insersect two boxes
+ if ( status ) {
+ newBoundingPolytope.combine( tbox );
+ } else {
+ newBoundingPolytope.set( tbox );
+ status = true;
+ }
+ } else {
+ }
+
+ } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ if( this.intersect( polytope)) {
+ Vector4d newPlanes[] = new Vector4d[planes.length + polytope.planes.length];
+ for(i=0;i<planes.length;i++) {
+ newPlanes[i] = new Vector4d(planes[i]);
+ }
+ for(i=0;i<polytope.planes.length;i++) {
+ newPlanes[planes.length + i] = new Vector4d(polytope.planes[i]);
+ }
+ BoundingPolytope newPtope= new BoundingPolytope( newPlanes );
+ if ( status ) {
+ newBoundingPolytope.combine( newPtope );
+ } else {
+ newBoundingPolytope.set( newPtope );
+ status = true;
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope8"));
+ }
+
+ if(newBoundingPolytope.boundsIsInfinite)
+ break; // We're done.
+
+ }
+
+ if( status == false ) {
+ newBoundingPolytope.boundsIsEmpty = true;
+ newBoundingPolytope.boundsIsInfinite = false;
+ newBoundingPolytope.computeAllVerts();
+ }
+ return status;
+
+ }
+ /**
+ * Finds closest bounding object that intersects this bounding polytope.
+ * @param boundsObjects is an array of bounds objects
+ * @return closest bounding object
+ */
+ public Bounds closestIntersection( Bounds[] boundsObjects) {
+
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ return null;
+ }
+
+ if( boundsIsEmpty ) {
+ return null;
+ }
+
+ double dis,disToPlane;
+ boolean contains = false;
+ boolean inside;
+ double smallest_distance = Double.MAX_VALUE;
+ int i,j,index=0;
+ double cenX = 0.0, cenY = 0.0, cenZ = 0.0;
+
+ for(i = 0; i < boundsObjects.length; i++){
+ if( boundsObjects[i] == null );
+
+ else if( this.intersect( boundsObjects[i])) {
+ if( boundsObjects[i] instanceof BoundingSphere ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ dis = Math.sqrt( (centroid.x-sphere.center.x)*(centroid.x-sphere.center.x) +
+ (centroid.y-sphere.center.y)*(centroid.y-sphere.center.y) +
+ (centroid.z-sphere.center.z)*(centroid.z-sphere.center.z) );
+ inside = true;
+ for(j=0;j<planes.length;j++) {
+ if( ( sphere.center.x*planes[j].x +
+ sphere.center.y*planes[j].y +
+ sphere.center.z*planes[j].z + planes[i].w ) > 0.0 ) { // check if sphere center in polytope
+ disToPlane = sphere.center.x*planes[j].x +
+ sphere.center.y*planes[j].y +
+ sphere.center.z*planes[j].z + planes[j].w;
+
+ // check if distance from center to plane is larger than radius
+ if( disToPlane > sphere.radius ) inside = false;
+ }
+ }
+ if( inside) { // contains the sphere
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if( boundsObjects[i] instanceof BoundingBox){
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ cenX = (box.upper.x+box.lower.x)/2.0;
+ cenY = (box.upper.y+box.lower.y)/2.0;
+ cenZ = (box.upper.z+box.lower.z)/2.0;
+ dis = Math.sqrt( (centroid.x-cenX)*(centroid.x-cenX) +
+ (centroid.y-cenY)*(centroid.y-cenY) +
+ (centroid.z-cenZ)*(centroid.z-cenZ) );
+ inside = true;
+ if( !pointInPolytope( box.upper.x, box.upper.y, box.upper.z ) ) inside = false;
+ if( !pointInPolytope( box.upper.x, box.upper.y, box.lower.z ) ) inside = false;
+ if( !pointInPolytope( box.upper.x, box.lower.y, box.upper.z ) ) inside = false;
+ if( !pointInPolytope( box.upper.x, box.lower.y, box.lower.z ) ) inside = false;
+ if( !pointInPolytope( box.lower.x, box.upper.y, box.upper.z ) ) inside = false;
+ if( !pointInPolytope( box.lower.x, box.upper.y, box.lower.z ) ) inside = false;
+ if( !pointInPolytope( box.lower.x, box.lower.y, box.upper.z ) ) inside = false;
+ if( !pointInPolytope( box.lower.x, box.lower.y, box.lower.z ) ) inside = false;
+
+ if( inside ) { // contains box
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+
+ } else if(boundsObjects[i] instanceof BoundingPolytope) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ dis = Math.sqrt( (centroid.x-polytope.centroid.x)*(centroid.x-polytope.centroid.x) +
+ (centroid.y-polytope.centroid.y)*(centroid.y-polytope.centroid.y) +
+ (centroid.z-polytope.centroid.z)*(centroid.z-polytope.centroid.z) );
+ inside = true;
+ for(j=0;j<polytope.nVerts;j++) {
+ if ( !pointInPolytope( polytope.verts[j].x, polytope.verts[j].y, polytope.verts[j].z ) )
+ inside = false;
+ }
+ if( inside ) {
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingPolytope10"));
+ }
+ }
+ }
+
+ return boundsObjects[index];
+ }
+
+ /**
+ * Returns a string representation of this class
+ */
+ public String toString() {
+ int i;
+
+ String description = new String("BoundingPolytope:\n Num Planes ="+planes.length);
+ for(i = 0; i < planes.length; i++){
+ description = description+"\n"+mag[i]*planes[i].x+" "+
+ mag[i]*planes[i].y+" "+mag[i]*planes[i].z+" "+mag[i]*planes[i].w;
+ }
+
+ return description;
+ }
+
+ private void computeVertex( int a, int b, int c ) {
+ double det,x,y,z;
+
+ det = planes[a].x*planes[b].y*planes[c].z + planes[a].y*planes[b].z*planes[c].x +
+ planes[a].z*planes[b].x*planes[c].y - planes[a].z*planes[b].y*planes[c].x -
+ planes[a].y*planes[b].x*planes[c].z - planes[a].x*planes[b].z*planes[c].y;
+
+ // System.out.println("\n det="+det);
+ if( det*det < EPSILON ){
+ // System.out.println("parallel planes="+a+" "+b+" "+c);
+ return; // two planes are parallel
+ }
+
+ det = 1.0/det;
+
+ x = (planes[b].y*planes[c].z - planes[b].z*planes[c].y) * pDotN[a];
+ y = (planes[b].z*planes[c].x - planes[b].x*planes[c].z) * pDotN[a];
+ z = (planes[b].x*planes[c].y - planes[b].y*planes[c].x) * pDotN[a];
+
+ x += (planes[c].y*planes[a].z - planes[c].z*planes[a].y) * pDotN[b];
+ y += (planes[c].z*planes[a].x - planes[c].x*planes[a].z) * pDotN[b];
+ z += (planes[c].x*planes[a].y - planes[c].y*planes[a].x) * pDotN[b];
+
+ x += (planes[a].y*planes[b].z - planes[a].z*planes[b].y) * pDotN[c];
+ y += (planes[a].z*planes[b].x - planes[a].x*planes[b].z) * pDotN[c];
+ z += (planes[a].x*planes[b].y - planes[a].y*planes[b].x) * pDotN[c];
+
+ x = x*det;
+ y = y*det;
+ z = z*det;
+
+ if (pointInPolytope( x, y, z ) ) {
+ if (nVerts >= verts.length) {
+ Point3d newVerts[] = new Point3d[nVerts << 1];
+ for(int i=0;i<nVerts;i++) {
+ newVerts[i] = verts[i];
+ }
+ verts = newVerts;
+ }
+ verts[nVerts++] = new Point3d( x,y,z);
+ }
+ }
+
+
+ private void computeAllVerts() {
+ int i,a,b,c;
+ double x,y,z;
+
+ nVerts = 0;
+
+ if( boundsIsEmpty) {
+ verts = null;
+ return;
+ }
+
+ verts = new Point3d[planes.length*planes.length];
+
+ for(i=0;i<planes.length;i++) {
+ pDotN[i] = -planes[i].x*planes[i].w*planes[i].x -
+ planes[i].y*planes[i].w*planes[i].y -
+ planes[i].z*planes[i].w*planes[i].z;
+ }
+
+ for(a=0;a<planes.length-2;a++) {
+ for(b=a+1;b<planes.length-1;b++) {
+ for(c=b+1;c<planes.length;c++) {
+ computeVertex(a,b,c);
+ }
+ }
+ }
+ // TODO correctly compute centroid
+
+ x=y=z=0.0;
+ Point3d newVerts[] = new Point3d[nVerts];
+
+ for(i=0;i<nVerts;i++) {
+ x += verts[i].x;
+ y += verts[i].y;
+ z += verts[i].z;
+ // copy the verts into an array of the correct size
+ newVerts[i] = verts[i];
+ }
+
+ this.verts = newVerts; // copy the verts into an array of the correct size
+
+ centroid.x = x/nVerts;
+ centroid.y = y/nVerts;
+ centroid.z = z/nVerts;
+
+ checkBoundsIsEmpty();
+
+ }
+
+ private boolean pointInPolytope( double x, double y, double z ){
+
+ for (int i = 0; i < planes.length; i++){
+ if(( x*planes[i].x +
+ y*planes[i].y +
+ z*planes[i].z + planes[i].w ) > EPSILON ) {
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+ private void checkBoundsIsEmpty() {
+ boundsIsEmpty = (planes.length < 4);
+ }
+
+ private void initEmptyPolytope() {
+ planes = new Vector4d[6];
+ pDotN = new double[6];
+ mag = new double[6];
+ verts = new Point3d[planes.length*planes.length];
+ nVerts = 0;
+
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0, -1.0 );
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0 );
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0, -1.0 );
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0, -1.0 );
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0, -1.0 );
+ planes[5] = new Vector4d( 0.0, 0.0,-1.0, -1.0 );
+ mag[0] = 1.0;
+ mag[1] = 1.0;
+ mag[2] = 1.0;
+ mag[3] = 1.0;
+ mag[4] = 1.0;
+ mag[5] = 1.0;
+
+ checkBoundsIsEmpty();
+ }
+
+ Point3d getCenter() {
+ return centroid;
+ }
+
+ /**
+ * if the passed the "region" is same type as this object
+ * then do a copy, otherwise clone the Bounds and
+ * return
+ */
+ Bounds copy(Bounds r) {
+ int i, k;
+
+ if (r != null && this.boundId == r.boundId) {
+ BoundingPolytope region = (BoundingPolytope) r;
+ if( region.planes.length !=planes.length) {
+ region.planes = new Vector4d[planes.length];
+
+ for(k=0;k< region.planes.length;k++)
+ region.planes[k] = new Vector4d();
+
+ region.mag = new double[planes.length];
+ region.pDotN = new double[planes.length];
+ region.verts = new Point3d[nVerts];
+ region.nVerts = nVerts;
+ for(k=0;k<nVerts;k++)
+ region.verts[k] = new Point3d(verts[k]);
+ }
+
+
+ for(i=0;i<planes.length;i++) {
+ region.planes[i].x = planes[i].x;
+ region.planes[i].y = planes[i].y;
+ region.planes[i].z = planes[i].z;
+ region.planes[i].w = planes[i].w;
+ region.mag[i] = mag[i];
+ }
+
+ region.boundsIsEmpty = boundsIsEmpty;
+ region.boundsIsInfinite = boundsIsInfinite;
+ return region;
+ }
+ else {
+ return (Bounds) this.clone();
+ }
+ }
+
+ int getPickType() {
+ return PickShape.PICKBOUNDINGPOLYTOPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BoundingSphere.java b/src/classes/share/javax/media/j3d/BoundingSphere.java
new file mode 100644
index 0000000..796adac
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BoundingSphere.java
@@ -0,0 +1,1765 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * This class defines a spherical bounding region which is defined by a
+ * center point and a radius.
+ */
+
+public class BoundingSphere extends Bounds {
+
+ /**
+ * The center of the bounding sphere.
+ */
+ Point3d center;
+
+ /**
+ * The radius of the bounding sphere.
+ */
+ double radius;
+
+ Point3d boxVerts[];
+ boolean allocBoxVerts = false;
+
+ // reusable temp objects
+ private BoundingBox tmpBox = null;
+ private BoundingPolytope tmpPolytope = null;
+
+ /**
+ * Constructs and initializes a BoundingSphere from a center and radius.
+ * @param center the center of the bounding sphere
+ * @param radius the radius of the bounding sphere
+ */
+ public BoundingSphere(Point3d center, double radius) {
+ this.center = new Point3d(center);
+ this.radius = radius;
+ boundId = BOUNDING_SPHERE;
+ updateBoundsStates();
+ }
+ /**
+ * Constructs and initializes a BoundingSphere with radius = 1 at 0 0 0.
+ */
+ public BoundingSphere() {
+ boundId = BOUNDING_SPHERE;
+ center = new Point3d();
+ radius = 1.0;
+ }
+
+ /**
+ * Constructs and initializes a BoundingSphere from a bounding object.
+ * @param boundsObject a bounds object
+ */
+ public BoundingSphere(Bounds boundsObject) {
+ int i;
+
+ boundId = BOUNDING_SPHERE;
+ if (boundsObject == null) {
+ // Negative volume.
+ center = new Point3d();
+ radius = -1.0;
+ }
+ else if( boundsObject.boundsIsInfinite ) {
+ center = new Point3d();
+ radius = Double.POSITIVE_INFINITY;
+
+ } else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+ center = new Point3d();
+ center.x = (box.upper.x+box.lower.x)/2.0;
+ center.y = (box.upper.y+box.lower.y)/2.0;
+ center.z = (box.upper.z+box.lower.z)/2.0;
+ radius = 0.5*(Math.sqrt((box.upper.x-box.lower.x)*
+ (box.upper.x-box.lower.x)+
+ (box.upper.y-box.lower.y)*
+ (box.upper.y-box.lower.y)+
+ (box.upper.z-box.lower.z)*
+ (box.upper.z-box.lower.z)));
+
+ } else if (boundsObject.boundId == BOUNDING_SPHERE) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ center = new Point3d(sphere.center);
+ radius = sphere.radius;
+
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ double t,dis,dis_sq,rad_sq,oldc_to_new_c;
+ center = new Point3d();
+ center.x = polytope.centroid.x;
+ center.y = polytope.centroid.y;
+ center.z = polytope.centroid.z;
+ radius = Math.sqrt( (polytope.verts[0].x - center.x)*
+ (polytope.verts[0].x - center.x) +
+ (polytope.verts[0].y - center.y)*
+ (polytope.verts[0].y - center.y) +
+ (polytope.verts[0].z - center.z)*
+ (polytope.verts[0].z - center.z));
+
+ for(i=1;i<polytope.nVerts;i++) {
+ rad_sq = radius * radius;
+
+ dis_sq = (polytope.verts[i].x - center.x)*
+ (polytope.verts[i].x - center.x) +
+ (polytope.verts[i].y - center.y)*
+ (polytope.verts[i].y - center.y) +
+ (polytope.verts[i].z - center.z)*
+ (polytope.verts[i].z - center.z);
+
+ // change sphere so one side passes through the point
+ // and other passes through the old sphere
+ if( dis_sq > rad_sq) {
+ dis = Math.sqrt( dis_sq);
+ radius = (radius + dis)*.5;
+ oldc_to_new_c = dis - radius;
+ t = oldc_to_new_c/dis;
+ center.x = center.x + (polytope.verts[i].x - center.x)*t;
+ center.y = center.y + (polytope.verts[i].y - center.y)*t;
+ center.z = center.z + (polytope.verts[i].z - center.z)*t;
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere0"));
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Constructs and initializes a BoundingSphere from an array of bounding objects.
+ * @param boundsObjects an array of bounds objects
+ */
+ public BoundingSphere(Bounds[] boundsObjects) {
+ int i=0;
+ double dis,t,d1;
+
+ boundId = BOUNDING_SPHERE;
+ center = new Point3d();
+
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ // Negative volume.
+ radius = -1.0;
+ updateBoundsStates();
+ return;
+ }
+
+ // find first non empty bounds object
+ while( boundsObjects[i] == null && i < boundsObjects.length) {
+ i++;
+ }
+
+ if( i >= boundsObjects.length ) { // all bounds objects were empty
+ // Negative volume.
+ radius = -1.0;
+ updateBoundsStates();
+ return;
+ }
+
+ this.set(boundsObjects[i++]);
+ if(boundsIsInfinite)
+ return;
+
+ for(;i<boundsObjects.length;i++) {
+ if( boundsObjects[i] == null ); // do nothing
+ else if( boundsObjects[i].boundsIsEmpty); // do nothing
+ else if( boundsObjects[i].boundsIsInfinite ) {
+ radius = Double.POSITIVE_INFINITY;
+ break; // We're done.
+ }
+ else if( boundsObjects[i].boundId == BOUNDING_BOX){
+ BoundingBox b = (BoundingBox)boundsObjects[i];
+ if( !allocBoxVerts){
+ boxVerts = new Point3d[8];
+ for(int j=0;j<8;j++)boxVerts[j] = new Point3d();
+ allocBoxVerts = true;
+ }
+ boxVerts[0].set(b.lower.x, b.lower.y, b.lower.z );
+ boxVerts[1].set(b.lower.x, b.upper.y, b.lower.z );
+ boxVerts[2].set(b.upper.x, b.lower.y, b.lower.z );
+ boxVerts[3].set(b.upper.x, b.upper.y, b.lower.z );
+ boxVerts[4].set(b.lower.x, b.lower.y, b.upper.z );
+ boxVerts[5].set(b.lower.x, b.upper.y, b.upper.z );
+ boxVerts[6].set(b.upper.x, b.lower.y, b.upper.z );
+ boxVerts[7].set(b.upper.x, b.upper.y, b.upper.z );
+ this.combine(boxVerts);
+ }
+ else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ dis = Math.sqrt( (center.x - sphere.center.x)*
+ (center.x - sphere.center.x) +
+ (center.y - sphere.center.y)*
+ (center.y - sphere.center.y) +
+ (center.z - sphere.center.z)*
+ (center.z - sphere.center.z) );
+ if( radius > sphere.radius) {
+ if( (dis+sphere.radius) > radius) {
+ d1 = .5*(radius-sphere.radius+dis);
+ t = d1/dis;
+ radius = d1+sphere.radius;
+ center.x = sphere.center.x + (center.x-sphere.center.x)*t;
+ center.y = sphere.center.y + (center.y-sphere.center.y)*t;
+ center.z = sphere.center.z + (center.z-sphere.center.z)*t;
+ }
+ }else {
+ if( (dis+radius) <= sphere.radius) {
+ center.x = sphere.center.x;
+ center.y = sphere.center.y;
+ center.z = sphere.center.z;
+ radius = sphere.radius;
+ }else {
+ d1 = .5*(sphere.radius-radius+dis);
+ t = d1/dis;
+ radius = d1+radius;
+ center.x = center.x + (sphere.center.x-center.x)*t;
+ center.y = center.y + (sphere.center.y-center.y)*t;
+ center.z = center.z + (sphere.center.z-center.z)*t;
+ }
+ }
+ }
+ else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ this.combine(polytope.verts);
+
+ }
+ else {
+ if( boundsObjects[i] != null )
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere0"));
+ }
+ }
+ updateBoundsStates();
+ }
+
+ /**
+ * Returns the radius of this bounding sphere as a double.
+ * @return the radius of the bounding sphere
+ */
+ public double getRadius() {
+ return radius;
+ }
+
+ /**
+ * Sets the radius of this bounding sphere from a double.
+ * @param r the new radius for the bounding sphere
+ */
+ public void setRadius(double r) {
+ radius = r;
+ updateBoundsStates();
+ }
+
+ /**
+ * Returns the position of this bounding sphere as a point.
+ * @param center a Point to receive the center of the bounding sphere
+
+ */
+ public void getCenter(Point3d center) {
+ center.x = this.center.x;
+ center.y = this.center.y;
+ center.z = this.center.z;
+ }
+
+ /**
+ * Sets the position of this bounding sphere from a point.
+ * @param center a Point defining the new center of the bounding sphere
+ */
+ public void setCenter(Point3d center) {
+ this.center.x = center.x;
+ this.center.y = center.y;
+ this.center.z = center.z;
+ checkBoundsIsNaN();
+ }
+
+ /**
+ * Sets the value of this BoundingSphere.
+ * @param boundsObject another bounds object
+ */
+ public void set(Bounds boundsObject){
+ int i;
+
+ if ((boundsObject == null) || boundsObject.boundsIsEmpty) {
+ center.x = 0.0;
+ center.y = 0.0;
+ center.z = 0.0;
+ radius = -1.0;
+ } else if( boundsObject.boundsIsInfinite ) {
+ center.x = 0.0;
+ center.y = 0.0;
+ center.z = 0.0;
+ radius = Double.POSITIVE_INFINITY;
+ } else if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+ center.x = (box.upper.x + box.lower.x )/2.0;
+ center.y = (box.upper.y + box.lower.y )/2.0;
+ center.z = (box.upper.z + box.lower.z )/2.0;
+ radius = 0.5*Math.sqrt((box.upper.x-box.lower.x)*
+ (box.upper.x-box.lower.x)+
+ (box.upper.y-box.lower.y)*
+ (box.upper.y-box.lower.y)+
+ (box.upper.z-box.lower.z)*
+ (box.upper.z-box.lower.z));
+ } else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ radius = sphere.radius;
+ center.x = sphere.center.x;
+ center.y = sphere.center.y;
+ center.z = sphere.center.z;
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ double t,dis,dis_sq,rad_sq,oldc_to_new_c;
+ center.x = polytope.centroid.x;
+ center.y = polytope.centroid.y;
+ center.z = polytope.centroid.z;
+ radius = Math.sqrt((polytope.verts[0].x - center.x)*
+ (polytope.verts[0].x - center.x) +
+ (polytope.verts[0].y - center.y)*
+ (polytope.verts[0].y - center.y) +
+ (polytope.verts[0].z - center.z)*
+ (polytope.verts[0].z - center.z));
+
+ for(i=1;i<polytope.nVerts;i++) {
+ rad_sq = radius * radius;
+
+ dis_sq = (polytope.verts[i].x - center.x)*
+ (polytope.verts[i].x - center.x) +
+ (polytope.verts[i].y - center.y)*
+ (polytope.verts[i].y - center.y) +
+ (polytope.verts[i].z - center.z)*
+ (polytope.verts[i].z - center.z);
+
+ // change sphere so one side passes through the point
+ // and other passes through the old sphere
+ if( dis_sq > rad_sq) { // point is outside sphere
+ dis = Math.sqrt( dis_sq);
+ radius = (radius + dis)*.5;
+ oldc_to_new_c = dis - radius;
+ t = oldc_to_new_c/dis;
+ center.x = center.x + (polytope.verts[i].x - center.x)*t;
+ center.y = center.y + (polytope.verts[i].y - center.y)*t;
+ center.z = center.z + (polytope.verts[i].z - center.z)*t;
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere2"));
+ }
+ updateBoundsStates();
+ }
+
+ /**
+ * Creates a copy of the bounding sphere.
+ * @return a BoundingSphere
+ */
+ public Object clone() {
+ return new BoundingSphere(this.center, this.radius);
+ }
+
+
+ /**
+ * Indicates whether the specified <code>bounds</code> object is
+ * equal to this BoundingSphere object. They are equal if the
+ * specified <code>bounds</code> object is an instance of
+ * BoundingSphere and all of the data
+ * members of <code>bounds</code> are equal to the corresponding
+ * data members in this BoundingSphere.
+ * @param bounds the object with which the comparison is made.
+ * @return true if this BoundingSphere is equal to <code>bounds</code>;
+ * otherwise false
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean equals(Object bounds) {
+ try {
+ BoundingSphere sphere = (BoundingSphere)bounds;
+ return (center.equals(sphere.center) &&
+ radius == sphere.radius);
+ }
+ catch (NullPointerException e) {
+ return false;
+ }
+ catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns a hash code value for this BoundingSphere object
+ * based on the data values in this object. Two different
+ * BoundingSphere objects with identical data values (i.e.,
+ * BoundingSphere.equals returns true) will return the same hash
+ * code value. Two BoundingSphere objects with different data
+ * members may return the same hash code value, although this is
+ * not likely.
+ * @return a hash code value for this BoundingSphere object.
+ *
+ * @since Java 3D 1.2
+ */
+ public int hashCode() {
+ long bits = 1L;
+ bits = 31L * bits + Double.doubleToLongBits(radius);
+ bits = 31L * bits + Double.doubleToLongBits(center.x);
+ bits = 31L * bits + Double.doubleToLongBits(center.y);
+ bits = 31L * bits + Double.doubleToLongBits(center.z);
+ return (int) (bits ^ (bits >> 32));
+ }
+
+
+ /**
+ * Combines this bounding sphere with a bounding object so that the
+ * resulting bounding sphere encloses the original bounding sphere and the
+ * given bounds object.
+ * @param boundsObject another bounds object
+ */
+ public void combine(Bounds boundsObject) {
+ double t,dis,d1,u,l,x,y,z,oldc_to_new_c;
+ BoundingSphere sphere;
+
+ if((boundsObject == null) || (boundsObject.boundsIsEmpty)
+ || (boundsIsInfinite))
+ return;
+
+ if((boundsIsEmpty) || (boundsObject.boundsIsInfinite)) {
+ this.set(boundsObject);
+ return;
+ }
+
+
+ if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox b = (BoundingBox)boundsObject;
+
+ // start with point furthest from sphere
+ u = b.upper.x-center.x;
+ l = b.lower.x-center.x;
+ if( u*u > l*l)
+ x = b.upper.x;
+ else
+ x = b.lower.x;
+
+ u = b.upper.y-center.y;
+ l = b.lower.y-center.y;
+ if( u*u > l*l)
+ y = b.upper.y;
+ else
+ y = b.lower.y;
+
+ u = b.upper.z-center.z;
+ l = b.lower.z-center.z;
+ if( u*u > l*l)
+ z = b.upper.z;
+ else
+ z = b.lower.z;
+
+ dis = Math.sqrt( (x - center.x)*(x - center.x) +
+ (y - center.y)*(y - center.y) +
+ (z - center.z)*(z - center.z) );
+
+ if( dis > radius) {
+ radius = (dis + radius)*.5;
+ oldc_to_new_c = dis - radius;
+ center.x = (radius*center.x + oldc_to_new_c*x)/dis;
+ center.y = (radius*center.y + oldc_to_new_c*y)/dis;
+ center.z = (radius*center.z + oldc_to_new_c*z)/dis;
+ combinePoint( b.upper.x, b.upper.y, b.upper.z);
+ combinePoint( b.upper.x, b.upper.y, b.lower.z);
+ combinePoint( b.upper.x, b.lower.y, b.upper.z);
+ combinePoint( b.upper.x, b.lower.y, b.lower.z);
+ combinePoint( b.lower.x, b.upper.y, b.upper.z);
+ combinePoint( b.lower.x, b.upper.y, b.lower.z);
+ combinePoint( b.lower.x, b.lower.y, b.upper.z);
+ combinePoint( b.lower.x, b.lower.y, b.lower.z);
+ }
+ } else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ sphere = (BoundingSphere)boundsObject;
+ dis = Math.sqrt( (center.x - sphere.center.x)*
+ (center.x - sphere.center.x) +
+ (center.y - sphere.center.y)*
+ (center.y - sphere.center.y) +
+ (center.z - sphere.center.z)*
+ (center.z - sphere.center.z) );
+ if( radius > sphere.radius) {
+ if( (dis+sphere.radius) > radius) {
+ d1 = .5*(radius-sphere.radius+dis);
+ t = d1/dis;
+ radius = d1+sphere.radius;
+ center.x = sphere.center.x + (center.x-sphere.center.x)*t;
+ center.y = sphere.center.y + (center.y-sphere.center.y)*t;
+ center.z = sphere.center.z + (center.z-sphere.center.z)*t;
+ }
+ }else {
+ if( (dis+radius) <= sphere.radius) {
+ center.x = sphere.center.x;
+ center.y = sphere.center.y;
+ center.z = sphere.center.z;
+ radius = sphere.radius;
+ }else {
+ d1 = .5*(sphere.radius-radius+dis);
+ t = d1/dis;
+ radius = d1+radius;
+ center.x = center.x + (sphere.center.x-center.x)*t;
+ center.y = center.y + (sphere.center.y-center.y)*t;
+ center.z = center.z + (sphere.center.z-center.z)*t;
+ }
+ }
+
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ this.combine(polytope.verts);
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere3"));
+ }
+ updateBoundsStates();
+ }
+
+ private void combinePoint( double x, double y, double z) {
+ double dis,oldc_to_new_c;
+ dis = Math.sqrt( (x - center.x)*(x - center.x) +
+ (y - center.y)*(y - center.y) +
+ (z - center.z)*(z - center.z) );
+
+ if( dis > radius) {
+ radius = (dis + radius)*.5;
+ oldc_to_new_c = dis - radius;
+ center.x = (radius*center.x + oldc_to_new_c*x)/dis;
+ center.y = (radius*center.y + oldc_to_new_c*y)/dis;
+ center.z = (radius*center.z + oldc_to_new_c*z)/dis;
+ }
+ }
+
+ /**
+ * Combines this bounding sphere with an array of bounding objects so that the
+ * resulting bounding sphere encloses the original bounding sphere and the
+ * given array of bounds object.
+ * @param boundsObjects an array of bounds objects
+ */
+ public void combine(Bounds[] boundsObjects) {
+ BoundingSphere sphere;
+ BoundingBox b;
+ BoundingPolytope polytope;
+ double t,dis,d1,u,l,x,y,z,oldc_to_new_c;
+ int i=0;
+
+
+ if((boundsObjects == null) || (boundsObjects.length <= 0)
+ || (boundsIsInfinite))
+ return;
+
+ // find first non empty bounds object
+ while((i<boundsObjects.length) &&
+ ((boundsObjects[i] == null) || boundsObjects[i].boundsIsEmpty)) {
+ i++;
+ }
+ if( i >= boundsObjects.length)
+ return; // no non empty bounds so do not modify current bounds
+
+ if( boundsIsEmpty)
+ this.set(boundsObjects[i++]);
+
+ if(boundsIsInfinite)
+ return;
+
+ for(;i<boundsObjects.length;i++) {
+ if( boundsObjects[i] == null ); // do nothing
+ else if( boundsObjects[i].boundsIsEmpty); // do nothing
+ else if( boundsObjects[i].boundsIsInfinite ) {
+ center.x = 0.0;
+ center.y = 0.0;
+ center.z = 0.0;
+ radius = Double.POSITIVE_INFINITY;
+ break; // We're done.
+ } else if( boundsObjects[i].boundId == BOUNDING_BOX){
+ b = (BoundingBox)boundsObjects[i];
+
+ // start with point furthest from sphere
+ u = b.upper.x-center.x;
+ l = b.lower.x-center.x;
+ if( u*u > l*l)
+ x = b.upper.x;
+ else
+ x = b.lower.x;
+
+ u = b.upper.y-center.y;
+ l = b.lower.y-center.y;
+ if( u*u > l*l)
+ y = b.upper.y;
+ else
+ y = b.lower.y;
+
+ u = b.upper.z-center.z;
+ l = b.lower.z-center.z;
+ if( u*u > l*l)
+ z = b.upper.z;
+ else
+ z = b.lower.z;
+
+ dis = Math.sqrt( (x - center.x)*(x - center.x) +
+ (y - center.y)*(y - center.y) +
+ (z - center.z)*(z - center.z) );
+
+ if( dis > radius) {
+ radius = (dis + radius)*.5;
+ oldc_to_new_c = dis - radius;
+ center.x = (radius*center.x + oldc_to_new_c*x)/dis;
+ center.y = (radius*center.y + oldc_to_new_c*y)/dis;
+ center.z = (radius*center.z + oldc_to_new_c*z)/dis;
+ combinePoint( b.upper.x, b.upper.y, b.upper.z);
+ combinePoint( b.upper.x, b.upper.y, b.lower.z);
+ combinePoint( b.upper.x, b.lower.y, b.upper.z);
+ combinePoint( b.upper.x, b.lower.y, b.lower.z);
+ combinePoint( b.lower.x, b.upper.y, b.upper.z);
+ combinePoint( b.lower.x, b.upper.y, b.lower.z);
+ combinePoint( b.lower.x, b.lower.y, b.upper.z);
+ combinePoint( b.lower.x, b.lower.y, b.lower.z);
+ }
+ } else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ sphere = (BoundingSphere)boundsObjects[i];
+ dis = Math.sqrt( (center.x - sphere.center.x)*
+ (center.x - sphere.center.x) +
+ (center.y - sphere.center.y)*
+ (center.y - sphere.center.y) +
+ (center.z - sphere.center.z)*
+ (center.z - sphere.center.z) );
+ if( radius > sphere.radius) {
+ if( (dis+sphere.radius) > radius) {
+ d1 = .5*(radius-sphere.radius+dis);
+ t = d1/dis;
+ radius = d1+sphere.radius;
+ center.x = sphere.center.x + (center.x-sphere.center.x)*t;
+ center.y = sphere.center.y + (center.y-sphere.center.y)*t;
+ center.z = sphere.center.z + (center.z-sphere.center.z)*t;
+ }
+ }else {
+ if( (dis+radius) <= sphere.radius) {
+ center.x = sphere.center.x;
+ center.y = sphere.center.y;
+ center.z = sphere.center.z;
+ radius = sphere.radius;
+ }else {
+ d1 = .5*(sphere.radius-radius+dis);
+ t = d1/dis;
+ radius = d1+radius;
+ center.x = center.x + (sphere.center.x-center.x)*t;
+ center.y = center.y + (sphere.center.y-center.y)*t;
+ center.z = center.z + (sphere.center.z-center.z)*t;
+ }
+ }
+ } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ polytope = (BoundingPolytope)boundsObjects[i];
+ this.combine(polytope.verts);
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere4"));
+ }
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Combines this bounding sphere with a point.
+ * @param point a 3D point in space
+ */
+ public void combine(Point3d point) {
+ double t,dis,oldc_to_new_c;
+
+ if( boundsIsInfinite) {
+ return;
+ }
+
+ if( boundsIsEmpty) {
+ radius = 0.0;
+ center.x = point.x;
+ center.y = point.y;
+ center.z = point.z;
+ } else {
+ dis = Math.sqrt( (point.x - center.x)*(point.x - center.x) +
+ (point.y - center.y)*(point.y - center.y) +
+ (point.z - center.z)*(point.z - center.z) );
+
+ if( dis > radius) {
+ radius = (dis + radius)*.5;
+ oldc_to_new_c = dis - radius;
+ center.x = (radius*center.x + oldc_to_new_c*point.x)/dis;
+ center.y = (radius*center.y + oldc_to_new_c*point.y)/dis;
+ center.z = (radius*center.z + oldc_to_new_c*point.z)/dis;
+ }
+ }
+
+ updateBoundsStates();
+ }
+
+ /**
+ * Combines this bounding sphere with an array of points.
+ * @param points an array of 3D points in space
+ */
+ public void combine(Point3d[] points) {
+ int i;
+ double dis,dis_sq,rad_sq,oldc_to_new_c;
+
+ if( boundsIsInfinite) {
+ return;
+ }
+
+ if( boundsIsEmpty ) {
+ center.x = points[0].x;
+ center.y = points[0].y;
+ center.z = points[0].z;
+ radius = 0.0;
+ }
+
+ for(i=0;i<points.length;i++) {
+ rad_sq = radius * radius;
+ dis_sq = (points[i].x - center.x)*(points[i].x - center.x) +
+ (points[i].y - center.y)*(points[i].y - center.y) +
+ (points[i].z - center.z)*(points[i].z - center.z);
+
+ // change sphere so one side passes through the point and
+ // other passes through the old sphere
+ if( dis_sq > rad_sq) {
+ dis = Math.sqrt( dis_sq);
+ radius = (radius + dis)*.5;
+ oldc_to_new_c = dis - radius;
+ center.x = (radius*center.x + oldc_to_new_c*points[i].x)/dis;
+ center.y = (radius*center.y + oldc_to_new_c*points[i].y)/dis;
+ center.z = (radius*center.z + oldc_to_new_c*points[i].z)/dis;
+ }
+ }
+
+ updateBoundsStates();
+ }
+
+
+ /**
+ * Modifies the bounding sphere so that it bounds the volume
+ * generated by transforming the given bounding object.
+ * @param boundsObject the bounding object to be transformed
+ * @param matrix a transformation matrix
+ */
+ public void transform( Bounds boundsObject, Transform3D matrix) {
+ double scale;
+
+ if( boundsObject == null || boundsObject.boundsIsEmpty) {
+ // Negative volume.
+ center.x = center.y = center.z = 0.0;
+ radius = -1.0;
+ updateBoundsStates();
+ return;
+ }
+
+ if(boundsObject.boundsIsInfinite) {
+ center.x = center.y = center.z = 0.0;
+ radius = Double.POSITIVE_INFINITY;
+ updateBoundsStates();
+ return;
+ }
+
+ if( boundsObject.boundId == BOUNDING_BOX){
+ if (tmpBox == null) {
+ tmpBox = new BoundingBox( (BoundingBox)boundsObject);
+ } else {
+ tmpBox.set((BoundingBox)boundsObject);
+ }
+ tmpBox.transform(matrix);
+ this.set(tmpBox);
+ }else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ matrix.transform(((BoundingSphere)boundsObject).center, this.center);
+ // A very simple radius scale.
+ scale = matrix.getDistanceScale();
+ this.radius = ((BoundingSphere)boundsObject).radius * scale;
+ if (Double.isNaN(radius)) {
+ // Negative volume.
+ center.x = center.y = center.z = 0.0;
+ radius = -1.0;
+ updateBoundsStates();
+ return;
+ }
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ if (tmpPolytope == null) {
+ tmpPolytope = new BoundingPolytope((BoundingPolytope)boundsObject);
+ } else {
+ tmpPolytope.set((BoundingPolytope)boundsObject);
+ }
+ tmpPolytope.transform(matrix);
+ this.set(tmpPolytope);
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere5"));
+ }
+ }
+
+ /**
+ * Transforms this bounding sphere by the given matrix.
+ */
+ public void transform( Transform3D trans) {
+ double scale;
+
+ if(boundsIsInfinite)
+ return;
+
+ trans.transform(center);
+ scale = trans.getDistanceScale();
+ radius = radius * scale;
+ if (Double.isNaN(radius)) {
+ // Negative volume.
+ center.x = center.y = center.z = 0.0;
+ radius = -1.0;
+ updateBoundsStates();
+ return;
+ }
+ }
+
+ /**
+ * Test for intersection with a ray
+ * @param origin the starting point of the ray
+ * @param direction the direction of the ray
+ * @param position3 a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Point3d origin, Vector3d direction, Point4d position ) {
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = origin.x;
+ position.y = origin.y;
+ position.z = origin.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ double l2oc,rad2,tca,t2hc,mag,t,invMag;
+ Vector3d dir = new Vector3d(); // normalized direction of ray
+ Point3d oc = new Point3d(); // vector from sphere center to ray origin
+
+ oc.x = center.x - origin.x;
+ oc.y = center.y - origin.y;
+ oc.z = center.z - origin.z;
+
+ l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared
+
+ rad2 = radius*radius;
+ if( l2oc < rad2 ){
+ // System.out.println("ray origin inside sphere" );
+ return true; // ray origin inside sphere
+ }
+
+ invMag = 1.0/Math.sqrt(direction.x*direction.x +
+ direction.y*direction.y +
+ direction.z*direction.z);
+ dir.x = direction.x*invMag;
+ dir.y = direction.y*invMag;
+ dir.z = direction.z*invMag;
+ tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z;
+
+ if( tca <= 0.0 ) {
+ // System.out.println("ray points away from sphere" );
+ return false; // ray points away from sphere
+ }
+
+ t2hc = rad2 - l2oc + tca*tca;
+
+ if( t2hc > 0.0 ){
+ t = tca - Math.sqrt(t2hc);
+ // System.out.println("ray hits sphere:"+this.toString()+" t="+t+" direction="+dir );
+ position.x = origin.x + dir.x*t;
+ position.y = origin.y + dir.y*t;
+ position.z = origin.z + dir.z*t;
+ position.w = t;
+ return true; // ray hits sphere
+ }else {
+ // System.out.println("ray does not hit sphere" );
+ return false;
+ }
+
+ }
+
+ /**
+ * Test for intersection with a point
+ * @param point the pick point
+ * @param position a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Point3d point, Point4d position ) {
+ double x,y,z,dist;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = point.x;
+ position.y = point.y;
+ position.z = point.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ x = point.x - center.x;
+ y = point.y - center.y;
+ z = point.z - center.z;
+
+ dist = x*x + y*y + z*z;
+ if( dist > radius*radius)
+ return false;
+ else {
+ position.x = point.x;
+ position.y = point.y;
+ position.z = point.z;
+ position.w = Math.sqrt(dist);
+ return true;
+ }
+
+ }
+
+ /**
+ * Test for intersection with a segment
+ * @param start a point defining the start of the line segment
+ * @param end a point defining the end of the line segment
+ * @param position a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect( Point3d start, Point3d end, Point4d position ) {
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ position.x = start.x;
+ position.y = start.y;
+ position.z = start.z;
+ position.w = 0.0;
+ return true;
+ }
+
+ double l2oc,rad2,tca,t2hc,mag,invMag,t;
+ Vector3d dir = new Vector3d(); // normalized direction of ray
+ Point3d oc = new Point3d(); // vector from sphere center to ray origin
+ Vector3d direction = new Vector3d();
+
+ oc.x = center.x - start.x;
+ oc.y = center.y - start.y;
+ oc.z = center.z - start.z;
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+ invMag = 1.0/Math.sqrt( direction.x*direction.x +
+ direction.y*direction.y +
+ direction.z*direction.z);
+ dir.x = direction.x*invMag;
+ dir.y = direction.y*invMag;
+ dir.z = direction.z*invMag;
+
+
+ l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared
+
+ rad2 = radius*radius;
+ if( l2oc < rad2 ){
+ // System.out.println("ray origin inside sphere" );
+ return true; // ray origin inside sphere
+ }
+
+ tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z;
+
+ if( tca <= 0.0 ) {
+ // System.out.println("ray points away from sphere" );
+ return false; // ray points away from sphere
+ }
+
+ t2hc = rad2 - l2oc + tca*tca;
+
+ if( t2hc > 0.0 ){
+ t = tca - Math.sqrt(t2hc);
+ if( t*t <= ((end.x-start.x)*(end.x-start.x)+
+ (end.y-start.y)*(end.y-start.y)+
+ (end.z-start.z)*(end.z-start.z))){
+
+ position.x = start.x + dir.x*t;
+ position.y = start.y + dir.x*t;
+ position.z = start.z + dir.x*t;
+ position.w = t;
+ return true; // segment hits sphere
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test for intersection with a ray.
+ * @param origin the starting point of the ray
+ * @param direction the direction of the ray
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Point3d origin, Vector3d direction ) {
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ return true;
+ }
+
+ double l2oc,rad2,tca,t2hc,mag;
+ Vector3d dir = new Vector3d(); // normalized direction of ray
+ Point3d oc = new Point3d(); // vector from sphere center to ray origin
+
+ oc.x = center.x - origin.x;
+ oc.y = center.y - origin.y;
+ oc.z = center.z - origin.z;
+
+ l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared
+
+ rad2 = radius*radius;
+ if( l2oc < rad2 ){
+ // System.out.println("ray origin inside sphere" );
+ return true; // ray origin inside sphere
+ }
+
+ mag = Math.sqrt(direction.x*direction.x +
+ direction.y*direction.y +
+ direction.z*direction.z);
+ dir.x = direction.x/mag;
+ dir.y = direction.y/mag;
+ dir.z = direction.z/mag;
+ tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z;
+
+ if( tca <= 0.0 ) {
+ // System.out.println("ray points away from sphere" );
+ return false; // ray points away from sphere
+ }
+
+ t2hc = rad2 - l2oc + tca*tca;
+
+ if( t2hc > 0.0 ){
+ // System.out.println("ray hits sphere" );
+ return true; // ray hits sphere
+ }else {
+ // System.out.println("ray does not hit sphere" );
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns the position of the intersect point if the ray intersects with
+ * the sphere.
+ *
+ */
+ boolean intersect(Point3d origin, Vector3d direction, Point3d intersectPoint ) {
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite ) {
+ intersectPoint.x = origin.x;
+ intersectPoint.y = origin.y;
+ intersectPoint.z = origin.z;
+ return true;
+ }
+
+ double l2oc,rad2,tca,t2hc,mag,t;
+ Point3d dir = new Point3d(); // normalized direction of ray
+ Point3d oc = new Point3d(); // vector from sphere center to ray origin
+
+ oc.x = center.x - origin.x; // TODO check if this method is still needed
+ oc.y = center.y - origin.y;
+ oc.z = center.z - origin.z;
+
+ l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared
+
+ rad2 = radius*radius;
+ if( l2oc < rad2 ){
+ // System.out.println("ray origin inside sphere" );
+ return true; // ray origin inside sphere
+ }
+
+ mag = Math.sqrt(direction.x*direction.x +
+ direction.y*direction.y +
+ direction.z*direction.z);
+ dir.x = direction.x/mag;
+ dir.y = direction.y/mag;
+ dir.z = direction.z/mag;
+ tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z;
+
+ if( tca <= 0.0 ) {
+ // System.out.println("ray points away from sphere" );
+ return false; // ray points away from sphere
+ }
+
+ t2hc = rad2 - l2oc + tca*tca;
+
+ if( t2hc > 0.0 ){
+ t = tca - Math.sqrt(t2hc);
+ intersectPoint.x = origin.x + direction.x*t;
+ intersectPoint.y = origin.y + direction.y*t;
+ intersectPoint.z = origin.z + direction.z*t;
+ // System.out.println("ray hits sphere" );
+ return true; // ray hits sphere
+ }else {
+ // System.out.println("ray does not hit sphere" );
+ return false;
+ }
+ }
+
+
+ /**
+ * Test for intersection with a point.
+ * @param point a point defining a position in 3-space
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Point3d point ) {
+ double x,y,z,dist;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+ if( boundsIsInfinite ) {
+ return true;
+ }
+
+ x = point.x - center.x;
+ y = point.y - center.y;
+ z = point.z - center.z;
+
+ dist = x*x + y*y + z*z;
+ if( dist > radius*radius)
+ return false;
+ else
+ return true;
+
+ }
+
+ /**
+ * Tests whether the bounding sphere is empty. A bounding sphere is
+ * empty if it is null (either by construction or as the result of
+ * a null intersection) or if its volume is negative. A bounding sphere
+ * with a volume of zero is <i>not</i> empty.
+ * @return true if the bounding sphere is empty;
+ * otherwise, it returns false
+ */
+ public boolean isEmpty() {
+ return boundsIsEmpty;
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @return true or false indicating if an intersection occured
+ */
+ boolean intersect(Bounds boundsObject, Point4d position) {
+ return intersect(boundsObject);
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds boundsObject) {
+ double distsq, radsq;
+ BoundingSphere sphere;
+ boolean intersect;
+
+ if( boundsObject == null ) {
+ return false;
+ }
+
+ if( boundsIsEmpty || boundsObject.boundsIsEmpty ) {
+ return false;
+ }
+
+ if( boundsIsInfinite || boundsObject.boundsIsInfinite ) {
+ return true;
+ }
+
+ if( boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObject;
+ double dis = 0.0;
+ double rad_sq = radius*radius;
+
+ // find the corner closest to the center of sphere
+
+ if( center.x < box.lower.x )
+ dis = (center.x-box.lower.x)*(center.x-box.lower.x);
+ else
+ if( center.x > box.upper.x )
+ dis = (center.x-box.upper.x)*(center.x-box.upper.x);
+
+ if( center.y < box.lower.y )
+ dis += (center.y-box.lower.y)*(center.y-box.lower.y);
+ else
+ if( center.y > box.upper.y )
+ dis += (center.y-box.upper.y)*(center.y-box.upper.y);
+
+ if( center.z < box.lower.z )
+ dis += (center.z-box.lower.z)*(center.z-box.lower.z);
+ else
+ if( center.z > box.upper.z )
+ dis += (center.z-box.upper.z)*(center.z-box.upper.z);
+
+ return ( dis <= rad_sq );
+ } else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ sphere = (BoundingSphere)boundsObject;
+ radsq = radius + sphere.radius;
+ radsq *= radsq;
+ distsq = center.distanceSquared(sphere.center);
+ return (distsq <= radsq);
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ return intersect_ptope_sphere( (BoundingPolytope)boundsObject, this);
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere6"));
+ }
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObjects an array of bounding objects
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds[] boundsObjects) {
+ double distsq, radsq;
+ BoundingSphere sphere;
+ int i;
+
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ return false;
+ }
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ for(i = 0; i < boundsObjects.length; i++){
+ if( boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty);
+ else if( boundsIsInfinite || boundsObjects[i].boundsIsInfinite ) {
+ return true; // We're done here.
+ } else if( boundsObjects[i].boundId == BOUNDING_BOX){
+ if( this.intersect( boundsObjects[i])) return true;
+ } else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ sphere = (BoundingSphere)boundsObjects[i];
+ radsq = radius + sphere.radius;
+ radsq *= radsq;
+ distsq = center.distanceSquared(sphere.center);
+ if (distsq <= radsq) {
+ return true;
+ }
+ } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ if( this.intersect( boundsObjects[i])) return true;
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere7"));
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @param newBoundSphere the new bounding sphere which is the intersection of
+ * the boundsObject and this BoundingSphere
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds boundsObject, BoundingSphere newBoundSphere) {
+
+ if((boundsObject == null ) || boundsIsEmpty || boundsObject.boundsIsEmpty) {
+ // Negative volume.
+ newBoundSphere.center.x = newBoundSphere.center.y =
+ newBoundSphere.center.z = 0.0;
+ newBoundSphere.radius = -1.0;
+ newBoundSphere.updateBoundsStates();
+ return false;
+ }
+
+ if(boundsIsInfinite && (!boundsObject.boundsIsInfinite)) {
+ newBoundSphere.set(boundsObject);
+ return true;
+ }
+ else if((!boundsIsInfinite) && boundsObject.boundsIsInfinite) {
+ newBoundSphere.set(this);
+ return true;
+ }
+ else if(boundsIsInfinite && boundsObject.boundsIsInfinite) {
+ newBoundSphere.set(this);
+ return true;
+ } else if(boundsObject.boundId == BOUNDING_BOX){
+ BoundingBox tbox = new BoundingBox();
+ BoundingBox box = (BoundingBox)boundsObject;
+ if( this.intersect( box) ){
+ BoundingBox sbox = new BoundingBox( this ); // convert sphere to box
+ sbox.intersect(box, tbox); // insersect two boxes
+ newBoundSphere.set( tbox ); // set sphere to the intersection of 2 boxes
+ return true;
+ } else {
+ // Negative volume.
+ newBoundSphere.center.x = newBoundSphere.center.y =
+ newBoundSphere.center.z = 0.0;
+ newBoundSphere.radius = -1.0;
+ newBoundSphere.updateBoundsStates();
+ return false;
+ }
+ } else if( boundsObject.boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObject;
+ double dis,t,d2;
+ boolean status;
+ dis = Math.sqrt( (center.x-sphere.center.x)*(center.x-sphere.center.x) +
+ (center.y-sphere.center.y)*(center.y-sphere.center.y) +
+ (center.z-sphere.center.z)*(center.z-sphere.center.z) );
+ if ( dis > radius+sphere.radius) {
+ // Negative volume.
+ newBoundSphere.center.x = newBoundSphere.center.y =
+ newBoundSphere.center.z = 0.0;
+ newBoundSphere.radius = -1.0;
+ status = false;
+ } else if( dis+radius <= sphere.radius ) { // this sphere is contained within boundsObject
+ newBoundSphere.center.x = center.x;
+ newBoundSphere.center.y = center.y;
+ newBoundSphere.center.z = center.z;
+ newBoundSphere.radius = radius;
+ status = true;
+ } else if( dis+sphere.radius <= radius ) { // boundsObject is containted within this sphere
+ newBoundSphere.center.x = sphere.center.x;
+ newBoundSphere.center.y = sphere.center.y;
+ newBoundSphere.center.z = sphere.center.z;
+ newBoundSphere.radius = sphere.radius;
+ status = true;
+ } else {
+ // distance from this center to center of overlapped volume
+ d2 = (dis*dis + radius*radius - sphere.radius*sphere.radius)/(2.0*dis);
+ newBoundSphere.radius = Math.sqrt( radius*radius - d2*d2);
+ t = d2/dis;
+ newBoundSphere.center.x = center.x + (sphere.center.x - center.x)*t;
+ newBoundSphere.center.y = center.y + (sphere.center.y - center.y)*t;
+ newBoundSphere.center.z = center.z + (sphere.center.z - center.z)*t;
+ status = true;
+ }
+
+ newBoundSphere.updateBoundsStates();
+ return status;
+
+ } else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
+ BoundingBox tbox = new BoundingBox();
+
+ BoundingPolytope polytope = (BoundingPolytope)boundsObject;
+ if( this.intersect( polytope) ){
+ BoundingBox sbox = new BoundingBox( this ); // convert sphere to box
+ BoundingBox pbox = new BoundingBox( polytope ); // convert polytope to box
+ sbox.intersect(pbox,tbox); // insersect two boxes
+ newBoundSphere.set( tbox ); // set sphere to the intersection of 2 boxesf
+ return true;
+ } else {
+ // Negative volume.
+ newBoundSphere.center.x = newBoundSphere.center.y =
+ newBoundSphere.center.z = 0.0;
+ newBoundSphere.radius = -1.0;
+ newBoundSphere.updateBoundsStates();
+ return false;
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere8"));
+ }
+ }
+
+ /**
+ * Test for intersection with an array of bounds objects.
+ * @param boundsObjects an array of bounds objects
+ * @param newBoundSphere the new bounding sphere which is the intersection of
+ * the boundsObject and this BoundingSphere
+ * @return true or false indicating if an intersection occured
+ */
+ public boolean intersect(Bounds[] boundsObjects, BoundingSphere newBoundSphere) {
+
+ if( boundsObjects == null || boundsObjects.length <= 0 || boundsIsEmpty ) {
+ // Negative volume.
+ newBoundSphere.center.x = newBoundSphere.center.y =
+ newBoundSphere.center.z = 0.0;
+ newBoundSphere.radius = -1.0;
+ newBoundSphere.updateBoundsStates();
+ return false;
+ }
+
+ int i=0;
+
+ // find first non null bounds object
+ while( boundsObjects[i] == null && i < boundsObjects.length) {
+ i++;
+ }
+
+ if( i >= boundsObjects.length ) { // all bounds objects were empty
+ // Negative volume.
+ newBoundSphere.center.x = newBoundSphere.center.y =
+ newBoundSphere.center.z = 0.0;
+ newBoundSphere.radius = -1.0;
+ newBoundSphere.updateBoundsStates();
+ return false;
+ }
+
+ boolean status = false;
+ double newRadius;
+ Point3d newCenter = new Point3d();
+ BoundingBox tbox = new BoundingBox();
+
+ for(i=0;i<boundsObjects.length;i++) {
+ if( boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty) ;
+ else if( boundsObjects[i].boundId == BOUNDING_BOX) {
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ if( this.intersect( box) ){
+ BoundingBox sbox = new BoundingBox( this ); // convert sphere to box
+ sbox.intersect(box,tbox); // insersect two boxes
+ if( status ) {
+ newBoundSphere.combine( tbox );
+ } else {
+ newBoundSphere.set( tbox ); // set sphere to the intersection of 2 boxesf
+ status = true;
+ }
+ }
+ } else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ double dis,t,d2;
+ dis = Math.sqrt( (center.x-sphere.center.x)*(center.x-sphere.center.x) +
+ (center.y-sphere.center.y)*(center.y-sphere.center.y) +
+ (center.z-sphere.center.z)*(center.z-sphere.center.z) );
+ if ( dis > radius+sphere.radius) {
+ } else if( dis+radius <= sphere.radius ) { // this sphere is contained within boundsObject
+ if( status ) {
+ newBoundSphere.combine( this );
+ } else {
+ newBoundSphere.center.x = center.x;
+ newBoundSphere.center.y = center.y;
+ newBoundSphere.center.z = center.z;
+ newBoundSphere.radius = radius;
+ status = true;
+ newBoundSphere.updateBoundsStates();
+ }
+ } else if( dis+sphere.radius <= radius ) { // boundsObject is containted within this sphere
+ if( status ) {
+ newBoundSphere.combine( sphere );
+ } else {
+ newBoundSphere.center.x = center.x;
+ newBoundSphere.center.y = center.y;
+ newBoundSphere.center.z = center.z;
+ newBoundSphere.radius = sphere.radius;
+ status = true;
+ newBoundSphere.updateBoundsStates();
+ }
+ } else {
+ // distance from this center to center of overlapped volume
+ d2 = (dis*dis + radius*radius - sphere.radius*sphere.radius)/(2.0*dis);
+ newRadius = Math.sqrt( radius*radius - d2*d2);
+ t = d2/dis;
+ newCenter.x = center.x + (sphere.center.x - center.x)*t;
+ newCenter.y = center.y + (sphere.center.y - center.y)*t;
+ newCenter.z = center.z + (sphere.center.z - center.z)*t;
+ if( status ) {
+ BoundingSphere newSphere = new BoundingSphere( newCenter,
+ newRadius );
+ newBoundSphere.combine( newSphere );
+ } else {
+ newBoundSphere.setRadius( newRadius );
+ newBoundSphere.setCenter( newCenter );
+ status = true;
+ }
+ }
+
+ } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ if( this.intersect( polytope) ){
+ BoundingBox sbox = new BoundingBox( this ); // convert sphere to box
+ BoundingBox pbox = new BoundingBox( polytope ); // convert polytope to box
+ sbox.intersect(pbox, tbox); // insersect two boxes
+ if( status ) {
+ newBoundSphere.combine( tbox );
+ } else {
+ newBoundSphere.set( tbox ); // set sphere to the intersection of 2 boxesf
+ status = true;
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere9"));
+ }
+ }
+ if( status == false) {
+ // Negative volume.
+ newBoundSphere.center.x = newBoundSphere.center.y =
+ newBoundSphere.center.z = 0.0;
+ newBoundSphere.radius = -1.0;
+ newBoundSphere.updateBoundsStates();
+ }
+ return status;
+ }
+
+ /**
+ * Finds closest bounding object that intersects this bounding sphere.
+ * @param boundsObjects an array of bounds objects
+ * @return closest bounding object
+ */
+ public Bounds closestIntersection( Bounds[] boundsObjects) {
+
+ if( boundsObjects == null || boundsObjects.length <= 0 ) {
+ return null;
+ }
+
+ if( boundsIsEmpty ) {
+ return null;
+ }
+
+ double dis,far_dis,pdist,x,y,z,rad_sq;
+ double cenX = 0.0, cenY = 0.0, cenZ = 0.0;
+ boolean contains = false;
+ boolean inside;
+ boolean intersect = false;
+ double smallest_distance = Double.MAX_VALUE;
+ int i,j,index=0;
+
+
+ for(i = 0; i < boundsObjects.length; i++){
+ if( boundsObjects[i] == null ) ;
+
+ else if( this.intersect( boundsObjects[i])) {
+ intersect = true;
+ if(boundsObjects[i].boundId == BOUNDING_BOX){
+ BoundingBox box = (BoundingBox)boundsObjects[i];
+ cenX = (box.upper.x+box.lower.x)/2.0;
+ cenY = (box.upper.y+box.lower.y)/2.0;
+ cenZ = (box.upper.z+box.lower.z)/2.0;
+ dis = Math.sqrt( (center.x-cenX)*(center.x-cenX) +
+ (center.y-cenY)*(center.y-cenY) +
+ (center.z-cenZ)*(center.z-cenZ) );
+ if( (center.x-box.lower.x)*(center.x-box.lower.x) >
+ (center.x-box.upper.x)*(center.x-box.upper.x) )
+ far_dis = (center.x-box.lower.x)*(center.x-box.lower.x);
+ else
+ far_dis = (center.x-box.upper.x)*(center.x-box.upper.x);
+
+ if( (center.y-box.lower.y)*(center.y-box.lower.y) >
+ (center.y-box.upper.y)*(center.y-box.upper.y) )
+ far_dis += (center.y-box.lower.y)*(center.y-box.lower.y);
+ else
+ far_dis += (center.y-box.upper.y)*(center.y-box.upper.y);
+
+ if( (center.z-box.lower.z)*(center.z-box.lower.z) >
+ (center.z-box.upper.z)*(center.z-box.upper.z) )
+ far_dis += (center.z-box.lower.z)*(center.z-box.lower.z);
+ else
+ far_dis += (center.z-box.upper.z)*(center.z-box.upper.z);
+
+ rad_sq = radius * radius;
+ if( far_dis <= rad_sq ) { // contains box
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) {
+ BoundingSphere sphere = (BoundingSphere)boundsObjects[i];
+ dis = Math.sqrt( (center.x-sphere.center.x)*(center.x-sphere.center.x) +
+ (center.y-sphere.center.y)*(center.y-sphere.center.y) +
+ (center.z-sphere.center.z)*(center.z-sphere.center.z) );
+ if( (dis+sphere.radius) <= radius) { // contains the sphere
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+
+ } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
+ BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
+ dis = Math.sqrt( (center.x-polytope.centroid.x)*(center.x-polytope.centroid.x) +
+ (center.y-polytope.centroid.y)*(center.y-polytope.centroid.y) +
+ (center.z-polytope.centroid.z)*(center.z-polytope.centroid.z) );
+ inside = true;
+ for(j=0;j<polytope.nVerts;j++) {
+ x = polytope.verts[j].x - center.x;
+ y = polytope.verts[j].y - center.y;
+ z = polytope.verts[j].z - center.z;
+
+ pdist = x*x + y*y + z*z;
+ if( pdist > radius*radius)
+ inside=false;
+ }
+ if( inside ) {
+ if( !contains ){ // initialize smallest_distance for the first containment
+ index = i;
+ smallest_distance = dis;
+ contains = true;
+ } else{
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+ } else if (!contains) {
+ if( dis < smallest_distance){
+ index = i;
+ smallest_distance = dis;
+ }
+ }
+
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere10"));
+ }
+ }
+ }
+
+ if ( intersect )
+ return boundsObjects[index];
+ else
+ return null;
+
+ }
+
+
+ /**
+ * Intersects this bounding sphere with preprocessed frustum.
+ * @return true if the bounding sphere and frustum intersect.
+ */
+ boolean intersect(CachedFrustum frustum) {
+ int i;
+ double dist;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if(boundsIsInfinite)
+ return true;
+
+ for (i=0; i<6; i++) {
+ dist = frustum.clipPlanes[i].x*center.x + frustum.clipPlanes[i].y*center.y +
+ frustum.clipPlanes[i].z*center.z + frustum.clipPlanes[i].w;
+ if (dist < 0.0 && (dist + radius) < 0.0) {
+ return(false);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This intersects this bounding sphere with 6 frustum plane equations
+ * @return returns true if the bounding sphere and frustum intersect.
+ */
+ boolean intersect(Vector4d[] planes) {
+ int i;
+ double dist;
+
+ if( boundsIsEmpty ) {
+ return false;
+ }
+
+ if(boundsIsInfinite)
+ return true;
+
+ for (i=0; i<6; i++) {
+ dist = planes[i].x*center.x + planes[i].y*center.y +
+ planes[i].z*center.z + planes[i].w;
+ if (dist < 0.0 && (dist + radius) < 0.0) {
+ //System.out.println("Tossing " + i + " " + dist + " " + radius);
+ return(false);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this class.
+ */
+ public String toString() {
+ return new String( "Center="+center+" Radius="+radius);
+ }
+
+ private void updateBoundsStates() {
+
+ if (checkBoundsIsNaN()) {
+ boundsIsEmpty = true;
+ boundsIsInfinite = false;
+ return;
+ }
+
+ if(radius == Double.POSITIVE_INFINITY) {
+ boundsIsEmpty = false;
+ boundsIsInfinite = true;
+ }
+ else {
+ boundsIsInfinite = false;
+ if( radius < 0.0 ) {
+ boundsIsEmpty = true;
+ } else {
+ boundsIsEmpty = false;
+ }
+ }
+ }
+
+ Point3d getCenter() {
+ return center;
+ }
+
+ /**
+ * if the passed the "region" is same type as this object
+ * then do a copy, otherwise clone the Bounds and
+ * return
+ */
+ Bounds copy(Bounds r) {
+ if (r != null && this.boundId == r.boundId) {
+ BoundingSphere region = (BoundingSphere)r;
+ region.radius = radius;
+ region.center.x = center.x;
+ region.center.y = center.y;
+ region.center.z = center.z;
+ region.boundsIsEmpty = boundsIsEmpty;
+ region.boundsIsInfinite = boundsIsInfinite;
+ return region;
+ }
+ else {
+ return (Bounds) this.clone();
+ }
+ }
+
+ boolean checkBoundsIsNaN() {
+ if (Double.isNaN(radius+center.x+center.y+center.z)) {
+ return true;
+ }
+ return false;
+ }
+
+ int getPickType() {
+ return PickShape.PICKBOUNDINGSPHERE;
+ }
+}
+
+
+
+
diff --git a/src/classes/share/javax/media/j3d/Bounds.java b/src/classes/share/javax/media/j3d/Bounds.java
new file mode 100644
index 0000000..79e9e17
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Bounds.java
@@ -0,0 +1,647 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The abstract base class for bounds objects. Bounds objects define
+ * a convex, closed volume that is used for various intersection and
+ * culling operations.
+ */
+
+public abstract class Bounds extends Object implements Cloneable {
+ static final double EPSILON = .000001;
+ static final boolean debug = false;
+
+ static final int BOUNDING_BOX = 0x1;
+ static final int BOUNDING_SPHERE = 0x2;
+ static final int BOUNDING_POLYTOPE = 0x4;
+
+ boolean boundsIsEmpty = false;
+ boolean boundsIsInfinite = false;
+ int boundId = 0;
+
+ /**
+ * Constructs a new Bounds object.
+ */
+ public Bounds() {
+ }
+
+
+ /**
+ * Makes a copy of a bounds object.
+ */
+ public abstract Object clone();
+
+
+ /**
+ * Indicates whether the specified <code>bounds</code> object is
+ * equal to this Bounds object. They are equal if both the
+ * specified <code>bounds</code> object and this Bounds are
+ * instances of the same Bounds subclass and all of the data
+ * members of <code>bounds</code> are equal to the corresponding
+ * data members in this Bounds.
+ * @param bounds the object with which the comparison is made.
+ * @return true if this Bounds object is equal to <code>bounds</code>;
+ * otherwise false
+ *
+ * @since Java 3D 1.2
+ */
+ public abstract boolean equals(Object bounds);
+
+
+ /**
+ * Returns a hash code for this Bounds object based on the
+ * data values in this object. Two different Bounds objects of
+ * the same type with identical data values (i.e., Bounds.equals
+ * returns true) will return the same hash code. Two Bounds
+ * objects with different data members may return the same hash code
+ * value, although this is not likely.
+ * @return a hash code for this Bounds object.
+ *
+ * @since Java 3D 1.2
+ */
+ public abstract int hashCode();
+
+
+ /**
+ * Test for intersection with a ray.
+ * @param origin the starting point of the ray
+ * @param direction the direction of the ray
+ * @return true or false indicating if an intersection occured
+ */
+ public abstract boolean intersect( Point3d origin, Vector3d direction );
+
+ /**
+ * Test for intersection with a point.
+ * @param point a point defining a position in 3-space
+ * @return true or false indicating if an intersection occured
+ */
+ public abstract boolean intersect( Point3d point );
+
+ /**
+ * Test for intersection with a ray
+ * @param origin is a the starting point of the ray
+ * @param direction is the direction of the ray
+ * @param position is a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ abstract boolean intersect( Point3d origin, Vector3d direction, Point4d position );
+
+ /**
+ * Test for intersection with a point
+ * @param point is a point defining a position in 3-space
+ * @param position is a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ abstract boolean intersect( Point3d point, Point4d position);
+
+ /**
+ * Test for intersection with a segment
+ * @param start is a point defining the start of the line segment
+ * @param end is a point defining the end of the line segment
+ * @param position is a point defining the location of the pick w= distance to pick
+ * @return true or false indicating if an intersection occured
+ */
+ abstract boolean intersect( Point3d start, Point3d end, Point4d position );
+
+ /**
+ * Test for intersection with another bounds object
+ *
+ * Test for intersection with another bounds object
+ * @param boundsObject is another bounds object
+ * @return true or false indicating if an intersection occured
+ */
+ abstract boolean intersect( Bounds boundsObject, Point4d position );
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObject another bounds object
+ * @return true or false indicating if an intersection occurred
+ */
+ public abstract boolean intersect( Bounds boundsObject );
+
+ /**
+ * Test for intersection with another bounds object.
+ * @param boundsObjects an array of bounding objects
+ * @return true or false indicating if an intersection occured
+ */
+ public abstract boolean intersect( Bounds[] boundsObjects );
+
+
+ /**
+ * Finds closest bounding object that intersects this bounding object.
+ * @param boundsObjects an array of bounds objects
+ * @return closest bounding object
+ */
+ public abstract Bounds closestIntersection( Bounds[] boundsObjects);
+
+ /**
+ * Returns the center of the bounds
+ * @return bounds center
+ */
+ abstract Point3d getCenter();
+
+ /**
+ * Combines this bounding object with a bounding object so that the
+ * resulting bounding object encloses the original bounding object and the
+ * given bounds object.
+ * @param boundsObject another bounds object
+ */
+ public abstract void combine( Bounds boundsObject );
+
+
+ /**
+ * Combines this bounding object with an array of bounding objects so that the
+ * resulting bounding object encloses the original bounding object and the
+ * given array of bounds object.
+ * @param boundsObjects an array of bounds objects
+ */
+ public abstract void combine( Bounds[] boundsObjects);
+
+ /**
+ * Combines this bounding object with a point.
+ * @param point a 3d point in space
+ */
+ public abstract void combine( Point3d point);
+
+ /**
+ * Combines this bounding object with an array of points.
+ * @param points an array of 3d points in space
+ */
+ public abstract void combine( Point3d[] points);
+
+ /**
+ * Transforms this bounding object by the given matrix.
+ * @param trans the transformation matrix
+ */
+ public abstract void transform(Transform3D trans);
+
+ /**
+ * Modifies the bounding object so that it bounds the volume
+ * generated by transforming the given bounding object.
+ * @param bounds the bounding object to be transformed
+ * @param trans the transformation matrix
+ */
+ public abstract void transform( Bounds bounds, Transform3D trans);
+
+ /**
+ * Tests whether the bounds is empty. A bounds is
+ * empty if it is null (either by construction or as the result of
+ * a null intersection) or if its volume is negative. A bounds
+ * with a volume of zero is <i>not</i> empty.
+ * @return true if the bounds is empty; otherwise, it returns false
+ */
+ public abstract boolean isEmpty();
+
+ /**
+ * Sets the value of this Bounds object.
+ * @param boundsObject another bounds object.
+ */
+ public abstract void set( Bounds boundsObject);
+
+
+ abstract Bounds copy(Bounds region);
+
+
+ private void test_point(Vector4d[] planes, Point3d new_point) {
+ for (int i = 0; i < planes.length; i++){
+ double dist = (new_point.x*planes[i].x + new_point.y*planes[i].y +
+ new_point.z*planes[i].z + planes[i].w ) ;
+ if (dist > EPSILON ){
+ System.out.println("new point is outside of" +
+ " plane["+i+"] dist = " + dist);
+ }
+ }
+ }
+
+ /**
+ * computes the closest point from the given point to a set of planes
+ * (polytope)
+ * @param g the point
+ * @param planes array of bounding planes
+ * @param new_point point on planes closest g
+ */
+ boolean closest_point( Point3d g, Vector4d[] planes, Point3d new_point ) {
+
+ double t,s,dist,w;
+ boolean converged, inside, firstPoint, firstInside;
+ int i,count;
+ double ab,ac,bc,ad,bd,cd,aa,bb,cc;
+ double b1,b2,b3,d1,d2,d3,y1,y2,y3;
+ double h11,h12,h13,h22,h23,h33;
+ double l12,l13,l23;
+ Point3d n = new Point3d();
+ Point3d p = new Point3d();
+ Vector3d delta = null;
+
+ // These are temporary until the solve code is working
+ Matrix3d hMatrix = new Matrix3d();
+
+ /*
+ * The algorithm:
+ * We want to find the point "n", closest to "g", while still within
+ * the the polytope defined by "planes". We find the solution by
+ * minimizing the value for a "penalty function";
+ *
+ * f = distance(n,g)^2 + sum for each i: w(distance(n, planes[i]))
+ *
+ * Where "w" is a weighting which indicates how much more important
+ * it is to be close to the planes than it is to be close to "g".
+ *
+ * We minimize this function by taking it's derivitive, and then
+ * solving for the value of n when the derivitive equals 0.
+ *
+ * For the 1D case with a single plane (a,b,c,d), x = n.x and g = g.x,
+ * this looks like:
+ *
+ * f(x) = (x - g) ^ 2 + w(ax + d)^2
+ * f'(x) = 2x -2g + 2waax + 2wad
+ *
+ * (note aa = a^2) setting f'(x) = 0 gives:
+ *
+ * (1 + waa)x = g - wad
+ *
+ * Note that the solution is just outside the plane [a, d]. With the
+ * correct choice of w, this should be inside of the EPSILON tolerance
+ * outside the planes.
+ *
+ * Extending to 3D gives the matrix solution:
+ *
+ * | (1 + waa) wab wac |
+ * H = | wab (1 + wbb) wbc |
+ * | wac wbc (1 + wcc) |
+ *
+ * b = [g.x - wad, g.y - wbd, g.z - wcd]
+ *
+ * H * n = b
+ *
+ * n = b * H.inverse()
+ *
+ * The implementation speeds this process up by recognizing that
+ * H is symmetric, so that it can be decomposed into three matrices:
+ *
+ * H = L * D * L.transpose()
+ *
+ * 1.0 0.0 0.0 d1 0.0 0.0
+ * L = l12 1.0 0.0 D = 0.0 d2 0.0
+ * l13 l23 1.0 0.0 0.0 d3
+ *
+ * n can then be derived by back-substitution, where the original
+ * problem is decomposed as:
+ *
+ * H * n = b
+ * L * D * L.transpose() * n = b
+ * L * D * y = b; L.transpose() * n = y
+ *
+ * We can then multiply out the terms of L * D and solve for y, and
+ * then use y to solve for n.
+ */
+
+ w=100.0 / EPSILON; // must be large enough to ensure that solution
+ // is within EPSILON of planes
+
+ count = 0;
+ p.set(g);
+
+ if (debug) {
+ System.out.println("closest_point():\nincoming g="+" "+g.x+" "+g.y+
+ " "+g.z);
+ }
+
+ converged = false;
+ firstPoint = true;
+ firstInside = false;
+
+ Vector4d pln;
+
+ while( !converged ) {
+ if (debug) {
+ System.out.println("start: p="+" "+p.x+" "+p.y+" "+p.z);
+ }
+
+ // test the current point against the planes, for each
+ // plane that is violated, add it's contribution to the
+ // penalty function
+ inside = true;
+ aa=0.0; bb=0.0; cc=0.0;
+ ab=0.0; ac=0.0; bc=0.0; ad=0.0; bd=0.0; cd=0.0;
+ for(i = 0; i < planes.length; i++){
+ pln = planes[i];
+ dist = (p.x*pln.x + p.y*pln.y +
+ p.z*pln.z + pln.w ) ;
+ // if point is outside or within EPSILON of the boundary, add
+ // the plane to the penalty matrix. We do this even if the
+ // point is already inside the polytope to prevent numerical
+ // instablity in cases where the point is just outside the
+ // boundary of several planes of the polytope
+ if (dist > -EPSILON ){
+ aa = aa + pln.x * pln.x;
+ bb = bb + pln.y * pln.y;
+ cc = cc + pln.z * pln.z;
+ ab = ab + pln.x * pln.y;
+ ac = ac + pln.x * pln.z;
+ bc = bc + pln.y * pln.z;
+ ad = ad + pln.x * pln.w;
+ bd = bd + pln.y * pln.w;
+ cd = cd + pln.z * pln.w;
+ }
+ // If the point is inside if dist is <= EPSILON
+ if (dist > EPSILON ){
+ inside = false;
+ if (debug) {
+ System.out.println("point outside plane["+i+"]=("+
+ pln.x+ ","+pln.y+",\n\t"+pln.z+
+ ","+ pln.w+")\ndist = " + dist);
+ }
+ }
+ }
+ // see if we are done
+ if (inside) {
+ if (debug) {
+ System.out.println("p is inside");
+ }
+ if (firstPoint) {
+ firstInside = true;
+ }
+ new_point.set(p);
+ converged = true;
+ } else { // solve for a closer point
+ firstPoint = false;
+
+ // this is the upper right corner of H, which is all we
+ // need to do the decomposition since the matrix is symetric
+ h11 = 1.0 + aa * w;
+ h12 = ab * w;
+ h13 = ac * w;
+ h22 = 1.0 + bb * w;
+ h23 = bc * w;
+ h33 = 1.0 + cc * w;
+
+ if (debug) {
+ System.out.println(" hessin= ");
+ System.out.println(h11+" "+h12+" "+h13);
+ System.out.println(" "+h22+" "+h23);
+ System.out.println(" "+h33);
+ }
+
+ // these are the constant terms
+ b1 = g.x - w * ad;
+ b2 = g.y - w * bd;
+ b3 = g.z - w * cd;
+
+ if (debug) {
+ System.out.println(" b1,b2,b3 = "+b1+" "+b2+" " +b3);
+ }
+
+ // solve, d1, d2, d3 actually 1/dx, which is more useful
+ d1 = 1/h11;
+ l12 = d1 * h12;
+ l13 = d1 * h13;
+ s = h22-l12*h12;
+ d2 = 1/s;
+ t = h23-h12*l13;
+ l23 = d2 * t;
+ d3 = 1/(h33 - h13*l13 - t*l23);
+
+ if (debug) {
+ System.out.println(" l12,l13,l23 "+l12+" "+l13+" "+l23);
+ System.out.println(" d1,d2,d3 "+ d1+" "+d2+" "+d3);
+ }
+
+ // we have L and D, now solve for y
+ y1 = d1 * b1;
+ y2 = d2 * (b2 - h12*y1);
+ y3 = d3 * (b3 - h13*y1 - t*y2);
+
+ if (debug) {
+ System.out.println(" y1,y2,y3 = "+y1+" "+y2+" "+y3);
+ }
+
+ // we have y, solve for n
+ n.z = y3;
+ n.y = (y2 - l23*n.z);
+ n.x = (y1 - l13*n.z - l12*n.y);
+
+ if (debug) {
+ System.out.println("new point = " + n.x+" " + n.y+" " +
+ n.z);
+ test_point(planes, n);
+
+ if (delta == null) delta = new Vector3d();
+ delta.sub(n, p);
+ delta.normalize();
+ System.out.println("p->n direction: " + delta);
+
+ // check using the the javax.vecmath routine
+ hMatrix.m00 = h11;
+ hMatrix.m01 = h12;
+ hMatrix.m02 = h13;
+ hMatrix.m10 = h12; // h21 = h12
+ hMatrix.m11 = h22;
+ hMatrix.m12 = h23;
+ hMatrix.m20 = h13; // h31 = h13
+ hMatrix.m21 = h23; // h32 = h22
+ hMatrix.m22 = h33;
+ hMatrix.invert();
+ Point3d check = new Point3d(b1, b2, b3);
+ hMatrix.transform(check);
+
+ System.out.println("check point = " + check.x+" " +
+ check.y+" " + check.z);
+ }
+
+ // see if we have converged yet
+ dist = (p.x-n.x)*(p.x-n.x) + (p.y-n.y)*(p.y-n.y) +
+ (p.z-n.z)*(p.z-n.z);
+
+ if (debug) {
+ System.out.println("p->n distance =" + dist );
+ }
+
+ if( dist < EPSILON) { // close enough
+ converged = true;
+ new_point.set(n);
+ } else {
+ p.set(n);
+ count++;
+ if(count > 4 ){ // watch for cycling between two minimums
+ new_point.set(n);
+ converged = true;
+ }
+ }
+ }
+ }
+ if (debug) {
+ System.out.println("returning pnt ("+new_point.x+" "+
+ new_point.y+" "+new_point.z+")");
+
+ if(firstInside) System.out.println("input point inside polytope ");
+ }
+ return firstInside;
+ }
+
+ boolean intersect_ptope_sphere( BoundingPolytope polyTope,
+ BoundingSphere sphere) {
+ Point3d p = new Point3d();
+ boolean inside;
+
+
+ if (debug) {
+ System.out.println("ptope_sphere intersect sphere ="+sphere);
+ }
+ inside = closest_point( sphere.center, polyTope.planes, p );
+ if (debug) {
+ System.out.println("ptope sphere intersect point ="+p);
+ }
+ if (!inside){
+ // if distance between polytope and sphere center is greater than
+ // radius then no intersection
+ if (p.distanceSquared( sphere.center) >
+ sphere.radius*sphere.radius){
+ if (debug) {
+ System.out.println("ptope_sphere returns false");
+ }
+ return false;
+ } else {
+ if (debug) {
+ System.out.println("ptope_sphere returns true");
+ }
+ return true;
+ }
+ } else {
+ if (debug) {
+ System.out.println("ptope_sphere returns true");
+ }
+ return true;
+ }
+ }
+
+ boolean intersect_ptope_abox( BoundingPolytope polyTope, BoundingBox box) {
+ Vector4d planes[] = new Vector4d[6];
+
+ if (debug) {
+ System.out.println("ptope_abox, box = " + box);
+ }
+ planes[0] = new Vector4d( -1.0, 0.0, 0.0, box.lower.x);
+ planes[1] = new Vector4d( 1.0, 0.0, 0.0,-box.upper.x);
+ planes[2] = new Vector4d( 0.0,-1.0, 0.0, box.lower.y);
+ planes[3] = new Vector4d( 0.0, 1.0, 0.0,-box.upper.y);
+ planes[4] = new Vector4d( 0.0, 0.0,-1.0, box.lower.z);
+ planes[5] = new Vector4d( 0.0, 0.0, 1.0,-box.upper.z);
+
+
+ BoundingPolytope pbox = new BoundingPolytope( planes);
+
+ boolean result = intersect_ptope_ptope( polyTope, pbox );
+ if (debug) {
+ System.out.println("ptope_abox returns " + result);
+ }
+ return(result);
+ }
+
+
+ boolean intersect_ptope_ptope( BoundingPolytope poly1,
+ BoundingPolytope poly2) {
+ boolean intersect;
+ Point3d p = new Point3d();
+ Point3d g = new Point3d();
+ Point3d gnew = new Point3d();
+ Point3d pnew = new Point3d();
+
+ intersect = false;
+
+ p.x = 0.0;
+ p.y = 0.0;
+ p.z = 0.0;
+
+ // start from an arbitrary point on poly1
+ closest_point( p, poly1.planes, g);
+
+ // get the closest points on each polytope
+ if (debug) {
+ System.out.println("ptope_ptope: first g = "+g);
+ }
+ intersect = closest_point( g, poly2.planes, p);
+
+ if (intersect) {
+ return true;
+ }
+
+ if (debug) {
+ System.out.println("first p = "+p+"\n");
+ }
+ intersect = closest_point( p, poly1.planes, gnew);
+ if (debug) {
+ System.out.println("gnew = "+gnew+" intersect="+intersect);
+ }
+
+ // loop until the closest points on the two polytopes are not changing
+
+ double prevDist = p.distanceSquared(g);
+ double dist;
+
+ while( !intersect ) {
+
+ dist = p.distanceSquared(gnew);
+
+ if (dist < prevDist) {
+ g.set(gnew);
+ intersect = closest_point( g, poly2.planes, pnew );
+ if (debug) {
+ System.out.println("pnew = "+pnew+" intersect="+intersect);
+ }
+ } else {
+ g.set(gnew);
+ break;
+ }
+ prevDist = dist;
+ dist = pnew.distanceSquared(g);
+
+ if (dist < prevDist) {
+ p.set(pnew);
+ if( !intersect ) {
+ intersect = closest_point( p, poly1.planes, gnew );
+ if (debug) {
+ System.out.println("gnew = "+gnew+" intersect="+
+ intersect);
+ }
+ }
+ } else {
+ p.set(pnew);
+ break;
+ }
+ prevDist = dist;
+ }
+
+ if (debug) {
+ System.out.println("gnew="+" "+gnew.x+" "+gnew.y+" "+gnew.z);
+ System.out.println("pnew="+" "+pnew.x+" "+pnew.y+" "+pnew.z);
+ }
+ return intersect;
+ }
+
+
+ synchronized void setWithLock(Bounds b) {
+ this.set(b);
+ }
+
+ synchronized void getWithLock(Bounds b) {
+ b.set(this);
+ }
+
+ // Return one of Pick Bounds type define in PickShape
+ abstract int getPickType();
+}
diff --git a/src/classes/share/javax/media/j3d/BranchGroup.java b/src/classes/share/javax/media/j3d/BranchGroup.java
new file mode 100644
index 0000000..5083a10
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BranchGroup.java
@@ -0,0 +1,207 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The BranchGroup serves as a pointer to the root of a
+ * scene graph branch; BranchGroup objects are the only objects that
+ * can be inserted into a Locale's set of objects. A subgraph, rooted
+ * by a BranchGroup node can be thought of as a compile unit. The
+ * following things may be done with BranchGroup:
+ * <P><UL>
+ * <LI>A BranchGroup may be compiled by calling its compile method. This causes the
+ * entire subgraph to be compiled. If any BranchGroup nodes are contained within the
+ * subgraph, they are compiled as well (along with their descendants).</LI>
+ * <p>
+ * <LI>A BranchGroup may be inserted into a virtual universe by attaching it to a
+ * Locale. The entire subgraph is then said to be live.</LI>
+ * <p>
+ * <LI>A BranchGroup that is contained within another subgraph may be reparented or
+ * detached at run time if the appropriate capabilities are set.</LI>
+ * </UL>
+ * Note that that if a BranchGroup is included in another subgraph, as a child of
+ * some other group node, it may not be attached to a Locale.
+ */
+
+public class BranchGroup extends Group {
+
+ /**
+ * For BranchGroup nodes, specifies that this BranchGroup allows detaching
+ * from its parent.
+ */
+ public static final int
+ ALLOW_DETACH = CapabilityBits.BRANCH_GROUP_ALLOW_DETACH;
+
+ /**
+ * Constructs and initializes a new BranchGroup node object.
+ */
+ public BranchGroup() {
+ }
+
+ /**
+ * Creates the retained mode BranchGroupRetained object that this
+ * BranchGroup component object will point to.
+ */
+ void createRetained() {
+ this.retained = new BranchGroupRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Compiles the source BranchGroup associated with this object and
+ * creates and caches a compiled scene graph.
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of a live scene graph.
+ */
+ public void compile() {
+ if (isLive()) {
+ throw new RestrictedAccessException(
+ J3dI18N.getString("BranchGroup0"));
+ }
+
+ if (isCompiled() == false) {
+ // will throw SceneGraphCycleException if there is a cycle
+ // in the scene graph
+ checkForCycle();
+
+ ((BranchGroupRetained)this.retained).compile();
+ }
+ }
+
+ /**
+ * Detaches this BranchGroup from its parent.
+ */
+ public void detach() {
+ Group parent;
+
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_DETACH))
+ throw new CapabilityNotSetException(J3dI18N.getString("BranchGroup1"));
+
+ if (((BranchGroupRetained)this.retained).parent != null) {
+ parent = (Group)((BranchGroupRetained)this.retained).parent.source;
+ if(!parent.getCapability(Group.ALLOW_CHILDREN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("BranchGroup2"));
+ }
+ }
+
+ ((BranchGroupRetained)this.retained).detach();
+ }
+
+ /**
+ * Returns an array referencing all the items that are pickable below this
+ * <code>BranchGroup</code> that intersect with PickShape.
+ * The resultant array is unordered.
+ *
+ * @param pickShape the PickShape object
+ *
+ * @see SceneGraphPath
+ * @see Locale#pickAll
+ * @see PickShape
+ * @exception IllegalStateException if BranchGroup is not live.
+ *
+ */
+ public SceneGraphPath[] pickAll( PickShape pickShape ) {
+ if(isLive()==false)
+ throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
+
+ return Picking.pickAll( this, pickShape );
+ }
+
+ /**
+ * Returns a sorted array of references to all the Pickable items that
+ * intersect with the pickShape. Element [0] references the item closest
+ * to <i>origin</i> of PickShape successive array elements are further
+ * from the <i>origin</i>
+ *
+ * Note: If pickShape is of type PickBounds, the resulting array
+ * is unordered.
+ * @param pickShape the PickShape object
+ *
+ * @see SceneGraphPath
+ * @see Locale#pickAllSorted
+ * @see PickShape
+ * @exception IllegalStateException if BranchGroup is not live.
+ *
+ */
+ public SceneGraphPath[] pickAllSorted( PickShape pickShape ) {
+ if(isLive()==false)
+ throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
+
+ return Picking.pickAllSorted( this, pickShape );
+ }
+
+ /**
+ * Returns a SceneGraphPath that references the pickable item
+ * closest to the origin of <code>pickShape</code>.
+ *
+ * Note: If pickShape is of type PickBounds, the return is any pickable node
+ * below this BranchGroup.
+ * @param pickShape the PickShape object
+ *
+ * @see SceneGraphPath
+ * @see Locale#pickClosest
+ * @see PickShape
+ * @exception IllegalStateException if BranchGroup is not live.
+ *
+ */
+ public SceneGraphPath pickClosest( PickShape pickShape ) {
+ if(isLive()==false)
+ throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
+
+ return Picking.pickClosest( this, pickShape );
+ }
+
+ /**
+ * Returns a reference to any item that is Pickable below this BranchGroup that
+ * intersects with <code>pickShape</code>.
+ *
+ * @param pickShape the PickShape object
+ * @see SceneGraphPath
+ * @see Locale#pickAny
+ * @see PickShape
+ * @exception IllegalStateException if BranchGroup is not live.
+ *
+ */
+ public SceneGraphPath pickAny( PickShape pickShape ) {
+ if(isLive()==false)
+ throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
+
+ return Picking.pickAny( this, pickShape );
+ }
+
+
+ /**
+ * Creates 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) {
+ BranchGroup bg = new BranchGroup();
+ bg.duplicateNode(this, forceDuplicate);
+ return bg;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/BranchGroupRetained.java b/src/classes/share/javax/media/j3d/BranchGroupRetained.java
new file mode 100644
index 0000000..b73438c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/BranchGroupRetained.java
@@ -0,0 +1,209 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * The BranchGroup node provides the ability to insert a branch of
+ * a scene graph into the virtual world.
+ */
+
+class BranchGroupRetained extends GroupRetained {
+
+ // This boolean is used in a moveTo operation to get transforms to update
+ boolean isDirty = false;
+
+ // This boolean is used when the BG is inserted into the scene
+ boolean isNew = false;
+
+ /**
+ * True, if this branchGroup is directly attached to the locale
+ */
+ boolean attachedToLocale = false;
+
+
+ BranchGroupRetained() {
+ this.nodeType = NodeRetained.BRANCHGROUP;
+ }
+
+ /**
+ * This sets the current locale.
+ */
+ void setLocale(Locale loc) {
+ locale = loc;
+ }
+
+ /**
+ * This gets the current locale
+ */
+ Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Detaches this BranchGroup from its parent.
+ */
+ void detach() {
+ if (universe != null) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ if (source.isLive()) {
+ notifySceneGraphChanged(true);
+ }
+ do_detach();
+ universe.setLiveState.clear();
+ }
+ universe.waitForMC();
+ } else { // Not live yet, just do it.
+ this.do_detach();
+ if (universe != null) {
+ synchronized (universe.sceneGraphLock) {
+ universe.setLiveState.clear();
+ }
+ }
+ }
+ }
+
+ // The method that does the work once the lock is acquired.
+ void do_detach() {
+ if (attachedToLocale) {
+ locale.doRemoveBranchGraph((BranchGroup)this.source, null, 0);
+ } else if (parent != null) {
+ GroupRetained g = (GroupRetained)parent;
+ g.doRemoveChild(g.children.indexOf(this), null, 0);
+ }
+ }
+
+ void setNodeData(SetLiveState s) {
+ // super.setNodeData will set branchGroupPaths
+ // based on s.parentBranchGroupPaths, we need to
+ // set it earlier in order to undo the effect.
+ s.parentBranchGroupPaths = branchGroupPaths;
+
+ super.setNodeData(s);
+
+ if (!inSharedGroup) {
+ setAuxData(s, 0, 0);
+ } else {
+ // For inSharedGroup case.
+ int j, hkIndex;
+ for(j=0; j<s.keys.length; j++) {
+ hkIndex = s.keys[j].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+
+ if(hkIndex >= 0) {
+ setAuxData(s, j, hkIndex);
+
+ } else {
+ // TODO: change this to an assertion exception
+ System.out.println("Can't Find matching hashKey in setNodeData.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ }
+ }
+ }
+
+
+ void setAuxData(SetLiveState s, int index, int hkIndex) {
+ super.setAuxData(s, index, hkIndex);
+
+ BranchGroupRetained path[] = (BranchGroupRetained[])
+ s.branchGroupPaths.get(index);
+ BranchGroupRetained clonePath[] =
+ new BranchGroupRetained[path.length+1];
+ System.arraycopy(path, 0, clonePath, 0, path.length);
+ clonePath[path.length] = this;
+ s.branchGroupPaths.set(index, clonePath);
+ branchGroupPaths.add(hkIndex, clonePath);
+ }
+
+
+ /**
+ * remove the localToVworld transform for this node.
+ */
+ void removeNodeData(SetLiveState s) {
+
+ if((!inSharedGroup) || (s.keys.length == localToVworld.length)) {
+ // restore to default and avoid calling clear()
+ // that may clear parent reference branchGroupPaths
+ branchGroupPaths = new ArrayList(1);
+ }
+ else {
+ int i, index;
+ // Must be in reverse, to preserve right indexing.
+ for (i = s.keys.length-1; i >= 0; i--) {
+ index = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length);
+ if(index >= 0) {
+ branchGroupPaths.remove(index);
+ }
+ }
+ // Set it back to its parent localToVworld data. This is b/c the parent has
+ // changed it localToVworld data arrays.
+ }
+ super.removeNodeData(s);
+ }
+
+ void setLive(SetLiveState s) {
+ // recursively call child
+ super.doSetLive(s);
+ super.markAsLive();
+ }
+
+
+ // Top level compile call
+ void compile() {
+
+ if (source.isCompiled() || VirtualUniverse.mc.disableCompile)
+ return;
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_3,
+ "BranchGroupRetained.compile()....\n");
+ }
+
+ CompileState compState = new CompileState();
+
+ isRoot = true;
+
+ compile(compState);
+ merge(compState);
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ if (J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_3)) {
+ compState.printStats();
+ }
+ if (J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_5)) {
+ this.traverse(false, 1);
+ System.out.println();
+ }
+ }
+ }
+
+ void compile(CompileState compState) {
+ // if this branch group is previously compiled, don't
+ // go any further. Mark the keepTG flag for now. Static
+ // transform doesn't go beyond previously compiled group.
+
+ if (mergeFlag == SceneGraphObjectRetained.MERGE_DONE) {
+ compState.keepTG = true;
+ return;
+ }
+
+ super.compile(compState);
+
+ // don't remove this node because user can do pickAll on this node
+ // without any capabilities set
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/CachedFrustum.java b/src/classes/share/javax/media/j3d/CachedFrustum.java
new file mode 100644
index 0000000..a034e7d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CachedFrustum.java
@@ -0,0 +1,571 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The CachedFrustum class is used to pre compute data for a set of view
+ * frustum planes which allows more efficient intersection testing.
+ *
+ * The CachedFrustum caches data for frustums for effecient intersection
+ * testing.
+ */
+
+// this class assumes the planes are in the following order:
+ // left plane = frustumPlanes[0]
+ // right plane = frustumPlanes[1]
+ // top plane = frustumPlanes[2]
+ // bottom plane = frustumPlanes[3]
+ // front plane = frustumPlanes[4]
+ // back plane = frustumPlanes[5]
+
+class CachedFrustum {
+
+ static final double EPSILON = .000001;
+ Vector4d[] clipPlanes;
+ Point3d[] verts;
+ Point4d[] xEdges; // silhouette edges in yz plane
+ Point4d[] yEdges; // silhouette edges in xz plane
+ Point4d[] zEdges; // silhouette edges in xy plane
+ int nxEdges, nyEdges, nzEdges;
+ int[] xEdgeList;
+ int[] yEdgeList;
+ int[] zEdgeList;
+ Point3d upper,lower; // bounding box of frustum
+ Point3d center; // center of frustum
+
+ // re-used temporary from computeEdges
+ Point3d edge = new Point3d();
+
+ /**
+ * Constructs and initializes a new CachedFrustum using the values
+ * provided in the argument.
+ * @param planes array specifying the frustum's clip plane position
+ */
+ CachedFrustum(Vector4d[] planes) {
+ int i;
+ if( planes.length < 6 ) {
+ throw new IllegalArgumentException(J3dI18N.getString("CachedFrustum0"));
+ }
+ clipPlanes = new Vector4d[6];
+ xEdges = new Point4d[12];
+ yEdges = new Point4d[12];
+ zEdges = new Point4d[12];
+ verts = new Point3d[8];
+ upper = new Point3d();
+ lower = new Point3d();
+ xEdgeList = new int[12];
+ yEdgeList = new int[12];
+ zEdgeList = new int[12];
+ center = new Point3d();
+
+ for(i=0;i<8;i++){
+ verts[i] = new Point3d();
+ }
+
+ for(i=0;i<6;i++){
+ clipPlanes[i] = new Vector4d( planes[i] );
+ }
+ for(i=0;i<12;i++){
+ xEdges[i] = new Point4d();
+ yEdges[i] = new Point4d();
+ zEdges[i] = new Point4d();
+ }
+ computeValues(clipPlanes);
+ }
+
+ /**
+ * Constructs and initializes a new default CachedFrustum
+ * @param planes array specifying the frustum's clip planes
+ */
+ CachedFrustum() {
+ int i;
+ clipPlanes = new Vector4d[6];
+ upper = new Point3d();
+ lower = new Point3d();
+ xEdges = new Point4d[12];
+ yEdges = new Point4d[12];
+ zEdges = new Point4d[12];
+ verts = new Point3d[8];
+ xEdgeList = new int[12];
+ yEdgeList = new int[12];
+ zEdgeList = new int[12];
+ center = new Point3d();
+
+ for(i=0;i<8;i++){
+ verts[i] = new Point3d();
+ }
+ for(i=0;i<6;i++){
+ clipPlanes[i] = new Vector4d();
+ }
+ for(i=0;i<12;i++){
+ xEdges[i] = new Point4d();
+ yEdges[i] = new Point4d();
+ zEdges[i] = new Point4d();
+ }
+
+ }
+
+ /**
+ * Returns a string containing the values of the CachedFrustum.
+ */
+ public String toString() {
+ return( clipPlanes[0].toString()+"\n"+
+ clipPlanes[1].toString()+"\n"+
+ clipPlanes[2].toString()+"\n"+
+ clipPlanes[3].toString()+"\n"+
+ clipPlanes[4].toString()+"\n"+
+ clipPlanes[5].toString()+"\n"+
+ "corners="+"\n"+
+ verts[0].toString()+"\n"+
+ verts[1].toString()+"\n"+
+ verts[2].toString()+"\n"+
+ verts[3].toString()+"\n"+
+ verts[4].toString()+"\n"+
+ verts[5].toString()+"\n"+
+ verts[6].toString()+"\n"+
+ verts[7].toString()
+ );
+ }
+
+
+ /**
+ * Sets the values of the CachedFrustum based on a new set of frustum planes.
+ * @param planes array specifying the frustum's clip planes
+ */
+ void set(Vector4d[] planes) {
+ int i;
+
+ if( planes.length != 6 ) {
+ throw new IllegalArgumentException(J3dI18N.getString("CachedFrustum1"));
+ }
+
+ for(i=0;i<6;i++){
+ clipPlanes[i].set( planes[i] );
+ }
+
+ computeValues(clipPlanes);
+ }
+
+ /**
+ * Computes cached values.
+ * @param planes array specifying the frustum's clip planes
+ */
+ private void computeValues(Vector4d[] planes) {
+
+ int i;
+
+ // compute verts
+
+ computeVertex( 0, 3, 4, verts[0]); // front-bottom-left
+ computeVertex( 0, 2, 4, verts[1]); // front-top-left
+ computeVertex( 1, 2, 4, verts[2]); // front-top-right
+ computeVertex( 1, 3, 4, verts[3]); // front-bottom-right
+ computeVertex( 0, 3, 5, verts[4]); // back-bottom-left
+ computeVertex( 0, 2, 5, verts[5]); // back-top-left
+ computeVertex( 1, 2, 5, verts[6]); // back-top-right
+ computeVertex( 1, 3, 5, verts[7]); // back-bottom-right
+
+ // compute bounding box
+
+ upper.x = verts[0].x;
+ upper.y = verts[0].y;
+ upper.z = verts[0].z;
+ lower.x = verts[0].x;
+ lower.y = verts[0].y;
+ lower.z = verts[0].z;
+
+ center.x = verts[0].x;
+ center.y = verts[0].y;
+ center.z = verts[0].z;
+
+ // find min and max in x-y-z directions
+
+ for(i=1;i<8;i++) {
+ if( verts[i].x > upper.x) upper.x = verts[i].x; // xmax
+ if( verts[i].x < lower.x) lower.x = verts[i].x; // xmin
+
+ if( verts[i].y > upper.y) upper.y = verts[i].y; // ymay
+ if( verts[i].y < lower.y) lower.y = verts[i].y; // ymin
+
+ if( verts[i].z > upper.z) upper.z = verts[i].z; // zmaz
+ if( verts[i].z < lower.z) lower.z = verts[i].z; // zmin
+
+ center.x += verts[i].x;
+ center.y += verts[i].y;
+ center.z += verts[i].z;
+ }
+
+ center.x = center.x*0.125;
+ center.y = center.y*0.125;
+ center.z = center.z*0.125;
+
+ // to find the sil. edges in the xz plane check the sign y component of the normals
+ // from the plane equation and see if they are opposite
+ // xz plane
+ i = 0;
+ if( (clipPlanes[0].y * clipPlanes[4].y) <= 0.0 ) yEdgeList[i++] = 0; // front-left
+ if( (clipPlanes[2].y * clipPlanes[4].y) <= 0.0 ) yEdgeList[i++] = 1; // front-top
+ if( (clipPlanes[1].y * clipPlanes[4].y) <= 0.0 ) yEdgeList[i++] = 2; // front-right
+ if( (clipPlanes[3].y * clipPlanes[4].y) <= 0.0 ) yEdgeList[i++] = 3; // front-bottom
+ if( (clipPlanes[0].y * clipPlanes[3].y) <= 0.0 ) yEdgeList[i++] = 4; // middle-left
+ if( (clipPlanes[0].y * clipPlanes[2].y) <= 0.0 ) yEdgeList[i++] = 5; // middle-top
+ if( (clipPlanes[1].y * clipPlanes[2].y) <= 0.0 ) yEdgeList[i++] = 6; // middle-right
+ if( (clipPlanes[1].y * clipPlanes[3].y) <= 0.0 ) yEdgeList[i++] = 7; // middle-bottom
+ if( (clipPlanes[0].y * clipPlanes[5].y) <= 0.0 ) yEdgeList[i++] = 8; // back-left
+ if( (clipPlanes[2].y * clipPlanes[5].y) <= 0.0 ) yEdgeList[i++] = 9; // back-top
+ if( (clipPlanes[1].y * clipPlanes[5].y) <= 0.0 ) yEdgeList[i++] =10; // back-right
+ if( (clipPlanes[3].y * clipPlanes[5].y) <= 0.0 ) yEdgeList[i++] =11; // back-bottom
+ nyEdges = i;
+
+ // yz plane
+ i = 0;
+ if( (clipPlanes[0].x * clipPlanes[4].x) <= 0.0 ) xEdgeList[i++] = 0; // front-left
+ if( (clipPlanes[2].x * clipPlanes[4].x) <= 0.0 ) xEdgeList[i++] = 1; // front-top
+ if( (clipPlanes[1].x * clipPlanes[4].x) <= 0.0 ) xEdgeList[i++] = 2; // front-right
+ if( (clipPlanes[3].x * clipPlanes[4].x) <= 0.0 ) xEdgeList[i++] = 3; // front-bottom
+ if( (clipPlanes[0].x * clipPlanes[3].x) <= 0.0 ) xEdgeList[i++] = 4; // middle-left
+ if( (clipPlanes[0].x * clipPlanes[2].x) <= 0.0 ) xEdgeList[i++] = 5; // middle-top
+ if( (clipPlanes[1].x * clipPlanes[2].x) <= 0.0 ) xEdgeList[i++] = 6; // middle-right
+ if( (clipPlanes[1].x * clipPlanes[3].x) <= 0.0 ) xEdgeList[i++] = 7; // middle-bottom
+ if( (clipPlanes[0].x * clipPlanes[5].x) <= 0.0 ) xEdgeList[i++] = 8; // back-left
+ if( (clipPlanes[2].x * clipPlanes[5].x) <= 0.0 ) xEdgeList[i++] = 9; // back-top
+ if( (clipPlanes[1].x * clipPlanes[5].x) <= 0.0 ) xEdgeList[i++] =10; // back-right
+ if( (clipPlanes[3].x * clipPlanes[5].x) <= 0.0 ) xEdgeList[i++] =11; // back-bottom
+ nxEdges = i;
+
+ // xy plane
+ i = 0;
+ if( (clipPlanes[0].z * clipPlanes[4].z) <= 0.0 ) zEdgeList[i++] = 0; // front-left
+ if( (clipPlanes[2].z * clipPlanes[4].z) <= 0.0 ) zEdgeList[i++] = 1; // front-top
+ if( (clipPlanes[1].z * clipPlanes[4].z) <= 0.0 ) zEdgeList[i++] = 2; // front-right
+ if( (clipPlanes[3].z * clipPlanes[4].z) <= 0.0 ) zEdgeList[i++] = 3; // front-bottom
+ if( (clipPlanes[0].z * clipPlanes[3].z) <= 0.0 ) zEdgeList[i++] = 4; // middle-left
+ if( (clipPlanes[0].z * clipPlanes[2].z) <= 0.0 ) zEdgeList[i++] = 5; // middle-top
+ if( (clipPlanes[1].z * clipPlanes[2].z) <= 0.0 ) zEdgeList[i++] = 6; // middle-right
+ if( (clipPlanes[1].z * clipPlanes[3].z) <= 0.0 ) zEdgeList[i++] = 7; // middle-bottom
+ if( (clipPlanes[0].z * clipPlanes[5].z) <= 0.0 ) zEdgeList[i++] = 8; // back-left
+ if( (clipPlanes[2].z * clipPlanes[5].z) <= 0.0 ) zEdgeList[i++] = 9; // back-top
+ if( (clipPlanes[1].z * clipPlanes[5].z) <= 0.0 ) zEdgeList[i++] =10; // back-right
+ if( (clipPlanes[3].z * clipPlanes[5].z) <= 0.0 ) zEdgeList[i++] =11; // back-bottom
+ nzEdges = i;
+
+ // compute each edge
+ computeEdges( clipPlanes, 0, 4, xEdges[0], yEdges[0], zEdges[0]); // front-left
+ computeEdges( clipPlanes, 2, 4, xEdges[1], yEdges[1], zEdges[1]); // front-top
+ computeEdges( clipPlanes, 1, 4, xEdges[2], yEdges[2], zEdges[2]);
+ computeEdges( clipPlanes, 3, 4, xEdges[3], yEdges[3], zEdges[3]);
+ computeEdges( clipPlanes, 0, 3, xEdges[4], yEdges[4], zEdges[4]);
+ computeEdges( clipPlanes, 0, 2, xEdges[5], yEdges[5], zEdges[5]);
+ computeEdges( clipPlanes, 1, 2, xEdges[6], yEdges[6], zEdges[6]);
+ computeEdges( clipPlanes, 1, 3, xEdges[7], yEdges[7], zEdges[7]);
+ computeEdges( clipPlanes, 0, 5, xEdges[8], yEdges[8], zEdges[8]);
+ computeEdges( clipPlanes, 2, 5, xEdges[9], yEdges[9], zEdges[9]);
+ computeEdges( clipPlanes, 1, 5, xEdges[10], yEdges[10], zEdges[10]);
+ computeEdges( clipPlanes, 3, 5, xEdges[11], yEdges[11], zEdges[11]);
+
+ /*
+ int k;
+ System.out.println("clipPlanes=");
+ for( k=0;k<6;k++){
+ System.out.println(clipPlanes[k].toString());
+ }
+ System.out.println("corners="+"\n"+
+ verts[0].toString()+"\n"+
+ verts[1].toString()+"\n"+
+ verts[2].toString()+"\n"+
+ verts[3].toString()+"\n"+
+ verts[4].toString()+"\n"+
+ verts[5].toString()+"\n"+
+ verts[6].toString()+"\n"+
+ verts[7].toString());
+ System.out.println("\nxEdges=");
+ for(k=0;k<nxEdges;k++){
+ System.out.println(xEdges[xEdgeList[k]].toString());
+ }
+ System.out.println("\nyEdges=");
+ for(k=0;k<nxEdges;k++){
+ System.out.println(yEdges[xEdgeList[k]].toString());
+ }
+ System.out.println("\nzEdges=");
+ for(k=0;k<nxEdges;k++){
+ System.out.println(zEdges[xEdgeList[k]].toString());
+ }
+ */
+
+ }
+ private void computeEdges( Vector4d[] planes, int i, int j, Point4d xEdge,
+ Point4d yEdge, Point4d zEdge) {
+
+ double mag,x,y,z,xm,ym,zm,w;
+
+ // compute vector that is intersection of two planes
+
+ edge.x = planes[i].y*planes[j].z - planes[j].y*planes[i].z;
+ edge.y = planes[j].x*planes[i].z - planes[i].x*planes[j].z;
+ edge.z = planes[i].x*planes[j].y - planes[j].x*planes[i].y;
+
+ mag = 1.0/Math.sqrt( edge.x*edge.x + edge.y*edge.y + edge.z*edge.z);
+
+ edge.x = mag*edge.x;
+ edge.y = mag*edge.y;
+ edge.z = mag*edge.z;
+
+ xm = Math.abs(edge.x);
+ ym = Math.abs(edge.y);
+ zm = Math.abs(edge.z);
+
+ // compute point on the intersection vector
+ // see Graphics Gems III pg. 233
+
+ if( zm >= xm && zm >= ym ){ // z greatest magnitude
+ w = (planes[i].x*planes[j].y + planes[i].z*planes[j].y);
+ if( w == 0.0)
+ w = 1.0;
+ else
+ w = 1.0/w;
+ x = (planes[i].y*planes[j].w - planes[j].y*planes[i].w) * w;
+ y = (planes[j].x*planes[i].w - planes[i].x*planes[j].w) * w;
+ z = 0.0;
+ } else if( xm >= ym && xm >= zm){ // x greatest magnitude
+ w = (planes[i].y*planes[j].z + planes[i].z*planes[j].y);
+ if( w == 0.0)
+ w = 1.0;
+ else
+ w = 1.0/w;
+ x = 0.0;
+ y = (planes[i].z*planes[j].w - planes[j].z*planes[i].w) * w;
+ z = (planes[j].y*planes[i].w - planes[i].y*planes[j].w) * w;
+ } else { // y greatest magnitude
+ w = (planes[i].x*planes[j].z + planes[i].z*planes[j].x);
+ if( w == 0.0)
+ w = 1.0;
+ else
+ w = 1.0/w;
+ x = (planes[i].z*planes[j].w - planes[j].z*planes[i].w) * w;
+ y = 0.0;
+ z = (planes[j].x*planes[i].w - planes[i].x*planes[j].w) * w;
+ }
+
+ // compute the noramls to the edges in for silhouette testing
+ /*
+ System.out.println("\nplane1="+planes[i].toString());
+ System.out.println("plane2="+planes[j].toString());
+ System.out.println("point="+x+" "+y+" "+z);
+ System.out.println("edge="+edge.x+" "+edge.y+" "+edge.z);
+ */
+ // x=0 edges
+
+ xEdge.x = 0.0;
+ xEdge.y = -edge.z;
+ xEdge.z = edge.y;
+ xEdge.w = -(xEdge.y*y + xEdge.z*z);
+
+ if( (center.y*xEdge.y + center.z*xEdge.z + xEdge.w) > 0.0 ){
+ xEdge.y = edge.z;
+ xEdge.z = -edge.y;
+ xEdge.w = -(xEdge.y*y + xEdge.z*z);
+ }
+
+ // y=0 edges
+ yEdge.x = -edge.z;
+ yEdge.y = 0.0;
+ yEdge.z = edge.x;
+ yEdge.w = -(yEdge.x*x + yEdge.z*z);
+ if( (center.y*yEdge.y + center.z*yEdge.z + yEdge.w) > 0.0 ){
+ yEdge.x = edge.z;
+ yEdge.z = -edge.x;
+ yEdge.w = -(yEdge.x*x + yEdge.z*z);
+ }
+
+ // z=0 edges
+ zEdge.x = -edge.y;
+ zEdge.y = edge.x;
+ zEdge.z = 0.0;
+ zEdge.w = -(zEdge.y*y + zEdge.x*x);
+
+ if( (center.x*zEdge.x + center.y*zEdge.y + zEdge.w) > 0.0 ){
+ zEdge.x = edge.y;
+ zEdge.y = -edge.x;
+ zEdge.w = -(zEdge.y*y + zEdge.x*x);
+ }
+ /*
+ System.out.println("xedge="+xEdge.x+" "+xEdge.y+" "+xEdge.z+" "+xEdge.w);
+ System.out.println("yedge="+yEdge.y+" "+yEdge.y+" "+yEdge.z+" "+yEdge.w);
+ System.out.println("zedge="+zEdge.z+" "+zEdge.y+" "+zEdge.z+" "+zEdge.w);
+ */
+ }
+ private void computeVertex( int a, int b, int c, Point3d vert) {
+ double det,x,y,z;
+
+ det = clipPlanes[a].x*clipPlanes[b].y*clipPlanes[c].z + clipPlanes[a].y*clipPlanes[b].z*clipPlanes[c].x +
+ clipPlanes[a].z*clipPlanes[b].x*clipPlanes[c].y - clipPlanes[a].z*clipPlanes[b].y*clipPlanes[c].x -
+ clipPlanes[a].y*clipPlanes[b].x*clipPlanes[c].z - clipPlanes[a].x*clipPlanes[b].z*clipPlanes[c].y;
+
+ if( det*det < EPSILON ){
+ return; // two planes are parallel
+ }
+
+ det = 1.0/det;
+
+ vert.x = (clipPlanes[b].y*clipPlanes[c].z - clipPlanes[b].z*clipPlanes[c].y) * -clipPlanes[a].w;
+ vert.y = (clipPlanes[b].z*clipPlanes[c].x - clipPlanes[b].x*clipPlanes[c].z) * -clipPlanes[a].w;
+ vert.z = (clipPlanes[b].x*clipPlanes[c].y - clipPlanes[b].y*clipPlanes[c].x) * -clipPlanes[a].w;
+
+ vert.x += (clipPlanes[c].y*clipPlanes[a].z - clipPlanes[c].z*clipPlanes[a].y) * -clipPlanes[b].w;
+ vert.y += (clipPlanes[c].z*clipPlanes[a].x - clipPlanes[c].x*clipPlanes[a].z) * -clipPlanes[b].w;
+ vert.z += (clipPlanes[c].x*clipPlanes[a].y - clipPlanes[c].y*clipPlanes[a].x) * -clipPlanes[b].w;
+
+ vert.x += (clipPlanes[a].y*clipPlanes[b].z - clipPlanes[a].z*clipPlanes[b].y) * -clipPlanes[c].w;
+ vert.y += (clipPlanes[a].z*clipPlanes[b].x - clipPlanes[a].x*clipPlanes[b].z) * -clipPlanes[c].w;
+ vert.z += (clipPlanes[a].x*clipPlanes[b].y - clipPlanes[a].y*clipPlanes[b].x) * -clipPlanes[c].w;
+
+ vert.x = vert.x*det;
+ vert.y = vert.y*det;
+ vert.z = vert.z*det;
+
+ }
+
+
+ /**
+ * Tests for intersection of six sided hull and the frustum
+ * @param six sided bounding box ( lower (x,y,z), upper (x,y,z))
+ * @return true if they intersect
+ */
+ boolean intersect( double lx, double ly, double lz,
+ double ux, double uy, double uz) {
+ int i,index;
+
+ // System.out.println("intersect frustum with box : lower ( " + lx + ", " + ly + ", " + lz +
+ // ") upper( " + ux + ", " + uy + ", " + uz + ")");
+ // System.out.println("frustum "+this.toString());
+ // check if box and bounding box of frustum intersect
+ if( ux > this.lower.x &&
+ lx < this.upper.x &&
+ uy > this.lower.y &&
+ ly < this.upper.y &&
+ uz > this.lower.z &&
+ lz < this.upper.z ) {
+ } else {
+ // System.out.println("false box and bounding box of frustum do not intersect");
+ return false;
+ }
+
+ // check if all box points out any frustum plane
+ for(i=0;i<6;i++){
+ if(( ux*this.clipPlanes[i].x +
+ uy*this.clipPlanes[i].y +
+ uz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ) {
+ continue; // corner inside plane
+ }
+ if(( ux*this.clipPlanes[i].x +
+ ly*this.clipPlanes[i].y +
+ uz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ){
+ continue;
+ }
+ if(( ux*this.clipPlanes[i].x +
+ ly*this.clipPlanes[i].y +
+ lz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ){
+ continue;
+ }
+ if(( ux*this.clipPlanes[i].x +
+ uy*this.clipPlanes[i].y +
+ lz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ) {
+ continue;
+ }
+ if(( lx*this.clipPlanes[i].x +
+ uy*this.clipPlanes[i].y +
+ uz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ) {
+ continue;
+ }
+ if(( lx*this.clipPlanes[i].x +
+ ly*this.clipPlanes[i].y +
+ uz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ) {
+ continue;
+ }
+ if(( lx*this.clipPlanes[i].x +
+ ly*this.clipPlanes[i].y +
+ lz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ) {
+ continue;
+ }
+ if(( lx*this.clipPlanes[i].x +
+ uy*this.clipPlanes[i].y +
+ lz*this.clipPlanes[i].z + this.clipPlanes[i].w ) > -EPSILON ) {
+ continue;
+ }
+ // System.out.println("false all corners outside this frustum plane"+frustum.clipPlanes[i].toString());
+ return false; // all corners outside this frustum plane
+ }
+
+ // check if any box corner is inside of the frustum silhoette edges in the 3 views
+ // y-z
+ for(i=0;i<this.nxEdges;i++){
+ index = this.xEdgeList[i];
+ if(( uy*this.xEdges[index].y +
+ uz*this.xEdges[index].z + this.xEdges[index].w ) < EPSILON ) break; // corner inside ege
+ if(( uy*this.xEdges[index].y +
+ lz*this.xEdges[index].z + this.xEdges[index].w ) < EPSILON ) break;
+ if(( ly*this.xEdges[index].y +
+ uz*this.xEdges[index].z + this.xEdges[index].w ) < EPSILON ) break;
+ if(( ly*this.xEdges[index].y +
+ lz*this.xEdges[index].z + this.xEdges[index].w ) < EPSILON ) break;
+ if( i == this.nxEdges-1) {
+ // System.out.println("false all box corners outside yz silhouette edges ");
+ return false; // all box corners outside yz silhouette edges
+ }
+ }
+ // x-z
+ for(i=0;i<this.nyEdges;i++){
+ index = this.yEdgeList[i];
+ if(( ux*this.yEdges[index].x +
+ uz*this.yEdges[index].z + this.yEdges[index].w ) < EPSILON ) break;
+ if(( ux*this.yEdges[index].x +
+ lz*this.yEdges[index].z + this.yEdges[index].w ) < EPSILON ) break;
+ if(( lx*this.yEdges[index].x +
+ uz*this.yEdges[index].z + this.yEdges[index].w ) < EPSILON ) break;
+ if(( lx*this.yEdges[index].x +
+ lz*this.yEdges[index].z + this.yEdges[index].w ) < EPSILON ) break;
+ if( i == this.nyEdges-1) {
+ // System.out.println("false all box corners outside xz silhouette edges");
+ return false; // all box corners outside xz silhouette edges
+ }
+ }
+ // x-y
+ for(i=0;i<this.nzEdges;i++){
+ index = this.zEdgeList[i];
+ if(( uy*this.zEdges[index].y +
+ uz*this.zEdges[index].z + this.zEdges[index].w ) < EPSILON ) break;
+ if(( uy*this.zEdges[index].y +
+ lz*this.zEdges[index].z + this.zEdges[index].w ) < EPSILON ) break;
+ if(( ly*this.zEdges[index].y +
+ uz*this.zEdges[index].z + this.zEdges[index].w ) < EPSILON ) break;
+ if(( ly*this.zEdges[index].y +
+ lz*this.zEdges[index].z + this.zEdges[index].w ) < EPSILON ) break;
+ if( i == this.nzEdges-1) {
+ /*
+ System.out.println("false all box corners outside xy silhouette edges");
+ System.out.println("xy silhouette edges=");
+ for(int j=0;j<this.nzEdges;j++){
+ System.out.println(this.zEdges[j].toString());
+ }
+ */
+ return false; // all box corners outside xy silhouette edges
+ }
+ }
+ // System.out.println("true");
+ return true;
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/CachedTargets.java b/src/classes/share/javax/media/j3d/CachedTargets.java
new file mode 100644
index 0000000..f10bc7f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CachedTargets.java
@@ -0,0 +1,109 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class CachedTargets {
+ // cached targets, used by J3d threads
+
+ // 0 - Data type is GeometryAtom.
+ // 1 - Data type is Light, Fog, Background, ModelClip, AlternateAppearance,
+ // Clip
+ // 2 - Data type is BehaviorRetained.
+ // 3 - Data type is Sound or Soundscape
+ // 4 - Data type is ViewPlatformRetained.
+ // 5 - Data type is BoundingLeafRetained.
+ // 6 - Data type is GroupRetained.
+
+ // Order of index is as above.
+ // The handling of BoundingLeaf isn't optimize. Target threads should be
+ // more specific.
+
+ static String typeString[] = {
+ "GEO_TARGETS",
+ "ENV_TARGETS",
+ "BEH_TARGETS",
+ "SND_TARGETS",
+ "VPF_TARGETS",
+ "BLN_TARGETS",
+ "GRP_TARGETS",
+ };
+
+ static int updateTargetThreads[] = {
+ // GEO
+ J3dThread.UPDATE_TRANSFORM | J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_GEOMETRY,
+
+ // ENV
+ J3dThread.UPDATE_RENDER | J3dThread.UPDATE_RENDERING_ENVIRONMENT,
+
+ // BEH
+ J3dThread.UPDATE_BEHAVIOR,
+
+ // SND
+ J3dThread.UPDATE_SOUND | J3dThread.SOUND_SCHEDULER,
+
+ // VPF
+ J3dThread.UPDATE_RENDER | J3dThread.UPDATE_BEHAVIOR |
+ J3dThread.UPDATE_SOUND | J3dThread.SOUND_SCHEDULER,
+
+ // BLN
+ J3dThread.UPDATE_RENDER | J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_BEHAVIOR | J3dThread.UPDATE_SOUND,
+
+ // GRP
+ J3dThread.UPDATE_TRANSFORM | J3dThread.UPDATE_GEOMETRY
+ };
+
+
+ NnuId targetArr[][] = new NnuId[Targets.MAX_NODELIST][];
+
+ int computeTargetThreads() {
+ int targetThreads = 0;
+
+ for (int i=0; i < Targets.MAX_NODELIST; i++) {
+ if (targetArr[i] != null) {
+ targetThreads |= updateTargetThreads[i];
+ }
+ }
+ return targetThreads;
+ }
+
+ void copy( CachedTargets ct ) {
+
+ for(int i=0; i<Targets.MAX_NODELIST; i++) {
+ targetArr[i] = ct.targetArr[i];
+ }
+ }
+
+ void replace(NnuId oldObj, NnuId newObj, int type) {
+
+ NnuId[] newArr = new NnuId[targetArr[type].length];
+ System.arraycopy(targetArr[type], 0, newArr,
+ 0, targetArr[type].length);
+ targetArr[type] = newArr;
+ NnuIdManager.replace((NnuId)oldObj, (NnuId)newObj,
+ (NnuId[])targetArr[type]);
+ }
+
+ void dump() {
+ for(int i=0; i<Targets.MAX_NODELIST; i++) {
+ if (targetArr[i] != null) {
+ System.out.println(" " + typeString[i]);
+ for(int j=0; j<targetArr[i].length; j++) {
+ System.out.println(" " + targetArr[i][j]);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Canvas3D.java b/src/classes/share/javax/media/j3d/Canvas3D.java
new file mode 100644
index 0000000..4d23891
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Canvas3D.java
@@ -0,0 +1,4209 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.util.*;
+
+
+
+/**
+ * The Canvas3D class provides a drawing canvas for 3D rendering. It
+ * is used either for on-screen rendering or off-screen rendering.
+ * Canvas3D is an extension of the AWT Canvas class that users may
+ * further subclass to implement additional functionality.
+ * <p>
+ * The Canvas3D object extends the Canvas object to include
+ * 3D-related information such as the size of the canvas in pixels,
+ * the Canvas3D's location, also in pixels, within a Screen3D object,
+ * and whether or not the canvas has stereo enabled.
+ * <p>
+ * Because all Canvas3D objects contain a
+ * reference to a Screen3D object and because Screen3D objects define
+ * the size of a pixel in physical units, Java 3D can convert a Canvas3D
+ * size in pixels to a physical world size in meters. It can also
+ * determine the Canvas3D's position and orientation in the
+ * physical world.
+ * <p>
+ * <b>On-screen Rendering vs. Off-screen Rendering</b>
+ * <p>
+ * The Canvas3D class is used either for on-screen rendering or
+ * off-screen rendering.
+ * On-screen Canvas3Ds are added to AWT or Swing Container objects
+ * like any other canvas. Java 3D automatically and continuously
+ * renders to all on-screen canvases that are attached to an active
+ * View object. On-screen Canvas3Ds can be either single or double
+ * buffered and they can be either stereo or monoscopic.
+ * <p>
+ * Off-screen Canvas3Ds must not be added to any Container. Java 3D
+ * renders to off-screen canvases in response to the
+ * <code>renderOffScreenBuffer</code> method. Off-screen Canvas3Ds
+ * are single buffered. However, on many systems, the actual
+ * rendering is done to an off-screen hardware buffer or to a 3D
+ * library-specific buffer and only copied to the off-screen buffer of
+ * the Canvas when the rendering is complete, at "buffer swap" time.
+ * Off-screen Canvas3Ds are monoscopic.
+ * <p>
+ * The setOffScreenBuffer method sets the off-screen buffer for this
+ * Canvas3D. The specified image is written into by the Java 3D renderer.
+ * The size of the specified ImageComponent determines the size, in
+ * pixels, of this Canvas3D - the size inherited from Component is
+ * ignored. Note that the size, physical width, and physical height of the
+ * associated Screen3D must be set
+ * explicitly prior to rendering. Failure to do so will result in an
+ * exception.
+ * <p>
+ * The getOffScreenBuffer method retrieves the off-screen
+ * buffer for this Canvas3D.
+ * <p>
+ * The renderOffScreenBuffer method schedules the rendering of a frame
+ * into this Canvas3D's off-screen buffer. The rendering is done from
+ * the point of view of the View object to which this Canvas3D has been
+ * added. No rendering is performed if this Canvas3D object has not been
+ * added to an active View. This method does not wait for the rendering
+ * to actually happen. An application that wishes to know when the
+ * rendering is complete must either subclass Canvas3D and
+ * override the postSwap method, or call waitForOffScreenRendering.
+ * <p>
+ * The setOfScreenLocation methods set the location of this off-screen
+ * Canvas3D. The location is the upper-left corner of the Canvas3D
+ * relative to the upper-left corner of the corresponding off-screen
+ * Screen3D. The function of these methods is similar to that of
+ * Component.setLocation for on-screen Canvas3D objects. The default
+ * location is (0,0).
+ * <p>
+ * <b>Accessing and Modifying an Eye's Image Plate Position</b>
+ * <p>
+ * A Canvas3D object provides sophisticated applications with access
+ * to the eye's position information in head-tracked, room-mounted
+ * runtime environments. It also allows applications to manipulate
+ * the position of an eye relative to an image plate in non-head-tracked
+ * runtime environments.
+ * <p>
+ * The setLeftManualEyeInImagePlate and setRightManualEyeInImagePlate
+ * methods set the position of the manual left and right eyes in image
+ * plate coordinates. These values determine eye placement when a head
+ * tracker is not in use and the application is directly controlling the
+ * eye position in image plate coordinates. In head-tracked mode or
+ * when the windowEyepointPolicy is RELATIVE_TO_FIELD_OF_VIEW or
+ * RELATIVE_TO_COEXISTENCE, this
+ * value is ignored. When the windowEyepointPolicy is RELATIVE_TO_WINDOW,
+ * only the Z value is used.
+ * <p>
+ * The getLeftEyeInImagePlate, getRightEyeInImagePlate, and
+ * getCenterEyeInImagePlate methods retrieve the actual position of the
+ * left eye, right eye, and center eye in image plate coordinates and
+ * copy that value into the object provided. The center eye is the
+ * fictional eye half-way between the left and right eye. These three
+ * values are a function of the windowEyepointPolicy, the tracking
+ * enable flag, and the manual left, right, and center eye positions.
+ * <p>
+ * <b>Monoscopic View Policy</b>
+ * <p>
+ * The setMonoscopicViewPolicy and getMonoscopicViewPolicy methods
+ * set and retrieve the policy regarding how Java 3D generates monoscopic
+ * view. If the policy is set to View.LEFT_EYE_VIEW, the view generated
+ * corresponds to the view as seen from the left eye. If set to
+ * View.RIGHT_EYE_VIEW, the view generated corresponds to the view as
+ * seen from the right eye. If set to View.CYCLOPEAN_EYE_VIEW, the view
+ * generated corresponds to the view as seen from the "center eye," the
+ * fictional eye half-way between the left and right eye. The default
+ * monoscopic view policy is View.CYCLOPEAN_EYE_VIEW.
+ * <p>
+ * <b>Immediate Mode Rendering</b>
+ * <p>
+ * Pure immediate-mode rendering provides for those applications and
+ * applets that do not want Java 3D to do any automatic rendering of
+ * the scene graph. Such applications may not even wish to build a
+ * scene graph to represent their graphical data. However, they use
+ * Java 3D's attribute objects to set graphics state and Java 3D's
+ * geometric objects to render geometry.
+ * <p>
+ * A pure immediate mode application must create a minimal set of
+ * Java 3D objects before rendering. In addition to a Canvas3D object,
+ * the application must create a View object, with its associated
+ * PhysicalBody and PhysicalEnvironment objects, and the following
+ * scene graph elements: a VirtualUniverse object, a high-resolution
+ * Locale object, a BranchGroup node object, a TransformGroup node
+ * object with associated transform, and a ViewPlatform
+ * leaf node object that defines the position and orientation within
+ * the virtual universe that generates the view.
+ * <p>
+ * In immediate mode, all rendering is done completely under user
+ * control. It is necessary for the user to clear the 3D canvas,
+ * render all geometry, and swap the buffers. Additionally,
+ * rendering the right and left eye for stereo viewing becomes the
+ * sole responsibility of the application. In pure immediate mode,
+ * the user must stop the Java 3D renderer, via the
+ * Canvas3D object <code>stopRenderer</code> method, prior to adding the
+ * Canvas3D object to an active View object (that is, one that is
+ * attached to a live ViewPlatform object).
+ * <p>
+ * Other Canvas3D methods related to immediate mode rendering are:
+ * <p>
+ * <ul>
+ * <code>getGraphicsContext3D</code> retrieves the immediate-mode
+ * 3D graphics context associated with this Canvas3D. It creates a
+ * new graphics context if one does not already exist.
+ * <p>
+ * <code>getGraphics2D</code> retrieves the
+ * 2D graphics object associated with this Canvas3D. It creates a
+ * new 2D graphics object if one does not already exist.
+ * <p>
+ * <code>swap</code> synchronizes and swaps buffers on a
+ * double-buffered canvas for this Canvas3D object. This method
+ * should only be called if the Java 3D renderer has been stopped.
+ * In the normal case, the renderer automatically swaps
+ * the buffer.
+ * </ul>
+ *
+ * <p>
+ * <b>Mixed Mode Rendering</b>
+ * <p>
+ * Mixing immediate mode and retained or compiled-retained mode
+ * requires more structure than pure immediate mode. In mixed mode,
+ * the Java 3D renderer is running continuously, rendering the scene
+ * graph into the canvas.
+ *
+ * <p>
+ * Canvas3D methods related to mixed mode rendering are:
+ *
+ * <p>
+ * <ul>
+ * <code>preRender</code> called by the Java 3D rendering loop after
+ * clearing the canvas and before any rendering has been done for
+ * this frame.
+ * <p>
+ * <code>postRender</code> called by the Java 3D rendering loop after
+ * completing all rendering to the canvas for this frame and before
+ * the buffer swap.
+ * <p>
+ * <code>postSwap</code> called by the Java 3D rendering loop after
+ * completing all rendering to the canvas, and all other canvases
+ * associated with this view, for this frame following the
+ * buffer swap.
+ * <p>
+ * <code>renderField</code> called by the Java 3D rendering loop
+ * during the execution of the rendering loop. It is called once
+ * for each field (i.e., once per frame on a mono system or once
+ * each for the right eye and left eye on a two-pass stereo system.
+ * </ul>
+ * <p>
+ * The above callback methods are called by the Java 3D rendering system
+ * and should <i>not</i> be called by an application directly.
+ *
+ * <p>
+ * The basic Java 3D <i>stereo</i> rendering loop,
+ * executed for each Canvas3D, is as follows:
+ * <ul><pre>
+ * clear canvas (both eyes)
+ * call preRender() // user-supplied method
+ * set left eye view
+ * render opaque scene graph objects
+ * call renderField(FIELD_LEFT) // user-supplied method
+ * render transparent scene graph objects
+ * set right eye view
+ * render opaque scene graph objects again
+ * call renderField(FIELD_RIGHT) // user-supplied method
+ * render transparent scene graph objects again
+ * call postRender() // user-supplied method
+ * synchronize and swap buffers
+ * call postSwap() // user-supplied method
+ * </pre></ul>
+ * <p>
+ * The basic Java 3D <i>monoscopic</i> rendering loop is as follows:
+ * <ul><pre>
+ * clear canvas
+ * call preRender() // user-supplied method
+ * set view
+ * render opaque scene graph objects
+ * call renderField(FIELD_ALL) // user-supplied method
+ * render transparent scene graph objects
+ * call postRender() // user-supplied method
+ * synchronize and swap buffers
+ * call postSwap() // user-supplied method
+ * </pre></ul>
+ * <p>
+ * In both cases, the entire loop, beginning with clearing the canvas
+ * and ending with swapping the buffers, defines a frame. The application
+ * is given the opportunity to render immediate-mode geometry at any of
+ * the clearly identified spots in the rendering loop. A user specifies
+ * his or her own rendering methods by extending the Canvas3D class and
+ * overriding the preRender, postRender, postSwap, and/or renderField
+ * methods.
+ * Updates to live Geometry, Texture, and ImageComponent objects
+ * in the scene graph are not allowed from any of these callback
+ * methods.
+ *
+ * <p>
+ * <b>Serialization</b>
+ * <p>
+ * Canvas3D does <i>not</i> support serialization. An attempt to
+ * serialize a Canvas3D object will result in an
+ * UnsupportedOperationException being thrown.
+ *
+ * @see Screen3D
+ * @see View
+ * @see GraphicsContext3D
+ */
+public class Canvas3D extends Canvas {
+ /**
+ * Specifies the left field of a field-sequential stereo rendering loop.
+ * A left field always precedes a right field.
+ */
+ public static final int FIELD_LEFT = 0;
+
+ /**
+ * Specifies the right field of a field-sequential stereo rendering loop.
+ * A right field always follows a left field.
+ */
+ public static final int FIELD_RIGHT = 1;
+
+ /**
+ * Specifies a single-field rendering loop.
+ */
+ public static final int FIELD_ALL = 2;
+
+ //
+ // The following constants are bit masks to specify which of the node
+ // components are dirty and need updates.
+ //
+ // Values for the geometryType field.
+ static final int POLYGONATTRS_DIRTY = 0x01;
+ static final int LINEATTRS_DIRTY = 0x02;
+ static final int POINTATTRS_DIRTY = 0x04;
+ static final int MATERIAL_DIRTY = 0x08;
+ static final int TRANSPARENCYATTRS_DIRTY = 0x10;
+ static final int COLORINGATTRS_DIRTY = 0x20;
+
+ // Values for lightbin, env set, texture, texture setting etc.
+ static final int LIGHTBIN_DIRTY = 0x40;
+ static final int LIGHTENABLES_DIRTY = 0x80;
+ static final int AMBIENTLIGHT_DIRTY = 0x100;
+ static final int ATTRIBUTEBIN_DIRTY = 0x200;
+ static final int TEXTUREBIN_DIRTY = 0x400;
+ static final int TEXTUREATTRIBUTES_DIRTY = 0x800;
+ static final int RENDERMOLECULE_DIRTY = 0x1000;
+ static final int FOG_DIRTY = 0x2000;
+ static final int MODELCLIP_DIRTY = 0x4000;
+ static final int VWORLD_SCALE_DIRTY = 0x8000;
+
+ // Use to notify D3D Canvas when window change
+ static final int RESIZE = 1;
+ static final int TOGGLEFULLSCREEN = 2;
+ static final int NOCHANGE = 0;
+ static final int RESETSURFACE = 1;
+ static final int RECREATEDDRAW = 2;
+
+ //
+ // Flag that indicates whether this Canvas3D is an off-screen Canvas3D
+ //
+ boolean offScreen = false;
+
+ // user specified offScreen Canvas location
+ Point offScreenCanvasLoc;
+
+ // user specified offScreen Canvas dimension
+ Dimension offScreenCanvasSize;
+
+ // clipped offscreen canvas
+ Point offScreenCanvasClippedLoc;
+ Dimension offScreenCanvasClippedSize;
+
+ //
+ // Flag that indicates whether off-screen rendering is in progress or not
+ //
+ volatile boolean offScreenRendering = false;
+
+ // Flag that indicates whether canvas is waiting for off-screen
+ // rendering to be completed
+ boolean waitingForOffScreenRendering = false;
+
+ //
+ // ImageComponent used for off-screen rendering
+ //
+ ImageComponent2D offScreenBuffer = null;
+
+ // read buffer for reading off screen buffer
+ byte[] byteBuffer = new byte[1];
+
+ // flag that indicates whether this canvas will use shared context
+ boolean useSharedCtx = true;
+
+ //
+ // Read-only flag that indicates whether stereo is supported for this
+ // canvas. This is always false for off-screen canvases.
+ //
+ boolean stereoAvailable;
+
+ //
+ // Flag to enable stereo rendering, if allowed by the
+ // stereoAvailable flag.
+ //
+ boolean stereoEnable = true;
+
+ //
+ // This flag is set when stereo mode is both enabled and
+ // available. Code that looks at stereo mode should use this
+ // flag.
+ //
+ boolean useStereo;
+
+ // Indicate whether it is left or right stereo pass currently
+ boolean rightStereoPass = false;
+
+ //
+ // Specifies how Java 3D generates monoscopic view
+ // (LEFT_EYE_VIEW, RIGHT_EYE_VIEW, or CYCLOPEAN_EYE_VIEW).
+ //
+ int monoscopicViewPolicy = View.CYCLOPEAN_EYE_VIEW;
+
+ //
+ // Read-only flag that indicates whether double buffering is supported
+ // for this canvas. This is always false for off-screen canvases.
+ //
+ boolean doubleBufferAvailable;
+
+ //
+ // Flag to enable double buffered rendering, if allowed by the
+ // doubleBufferAvailable flag.
+ //
+ boolean doubleBufferEnable = true;
+
+ //
+ // This flag is set when doubleBuffering is both enabled and
+ // available Code that enables or disables double buffering should
+ // use this flag.
+ //
+ boolean useDoubleBuffer;
+
+ //
+ // Read-only flag that indicates whether scene antialiasing
+ // is supported for this canvas.
+ //
+ boolean sceneAntialiasingAvailable;
+ boolean sceneAntialiasingMultiSamplesAvailable;
+
+ // Use to see whether antialiasing is already set
+ boolean antialiasingSet = false;
+
+ //
+ // Read-only flag that indicates the size of the texture color
+ // table for this canvas. A value of 0 indicates that the texture
+ // color table is not supported.
+ //
+ int textureColorTableSize;
+
+
+ boolean multiTexAccelerated = false;
+
+ // number of simultaneous Texture unit support for this canvas.
+ int numTexUnitSupported = 1;
+
+ // number of texture coords unit support for multi-texture.
+ int numTexCoordSupported = 1;
+
+ // a mapping between underlying graphics library texture unit and
+ // texture unit state in j3d
+ int[] texUnitStateMap = null;
+
+ // number of active/enabled texture unit
+ int numActiveTexUnit = 0;
+
+ // index iof last enabled texture unit
+ int lastActiveTexUnit = -1;
+
+ // Query properties
+ J3dQueryProps queryProps;
+
+ //
+ // The positions of the manual left and right eyes in image-plate
+ // coordinates.
+ // By default, we will use the center of the screen for X and Y values
+ // (X values are adjusted for default eye separation), and
+ // 0.4572 meters (18 inches) for the Z value.
+ // These match defaults elsewhere in the system.
+ //
+ Point3d leftManualEyeInImagePlate = new Point3d(0.142, 0.135, 0.4572);
+ Point3d rightManualEyeInImagePlate = new Point3d(0.208, 0.135, 0.4572);
+
+ //
+ // View that is attached to this Canvas3D.
+ //
+ View view = null;
+
+ // View waiting to be set
+ View pendingView;
+
+ //
+ // View cache for this canvas and its associated view.
+ //
+ CanvasViewCache canvasViewCache = null;
+
+ // Since multiple renderAtomListInfo, share the same vecBounds
+ // we want to do the intersection test only once per renderAtom
+ // this flag is set to true after the first intersect and set to
+ // false during checkForCompaction in renderBin
+ boolean raIsVisible = false;
+
+ RenderAtom ra = null;
+
+ // Stereo related field has changed.
+ static final int STEREO_DIRTY = 0x01;
+ // MonoscopicViewPolicy field has changed.
+ static final int MONOSCOPIC_VIEW_POLICY_DIRTY = 0x02;
+ // Left/right eye in image plate field has changed.
+ static final int EYE_IN_IMAGE_PLATE_DIRTY = 0x04;
+ // Canvas has moved/resized.
+ static final int MOVED_OR_RESIZED_DIRTY = 0x08;
+
+ // Canvas Background changed (this may affect doInfinite flag)
+ static final int BACKGROUND_DIRTY = 0x10;
+
+ // Canvas Background Image changed
+ static final int BACKGROUND_IMAGE_DIRTY = 0x20;
+
+
+ // Mask that indicates this Canvas view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ static final int VIEW_INFO_DIRTY = (STEREO_DIRTY |
+ MONOSCOPIC_VIEW_POLICY_DIRTY |
+ EYE_IN_IMAGE_PLATE_DIRTY |
+ MOVED_OR_RESIZED_DIRTY |
+ BACKGROUND_DIRTY |
+ BACKGROUND_IMAGE_DIRTY);
+
+ int cvDirtyMask = VIEW_INFO_DIRTY;
+
+ // This boolean informs the J3DGraphics2DImpl that the window is resized
+ boolean resizeGraphics2D = true;
+ //
+ // This boolean allows an application to start and stop the render
+ // loop on this canvas.
+ //
+ volatile boolean isRunning = true;
+
+ // This is used by MasterControl only. MC relay on this in a
+ // single loop to set renderer thread. During this time,
+ // the isRunningStatus can't change by user thread.
+ volatile boolean isRunningStatus = true;
+
+ // This is true when the canvas is ready to be rendered into
+ boolean active = false;
+
+ // This is true when the canvas is visible
+ boolean visible = false;
+
+ // This is true if context need to recreate
+ boolean ctxReset = true;
+
+ // The Screen3D that corresponds to this Canvas3D
+ Screen3D screen = null;
+
+ // Flag to indicate that image is render completely
+ // so swap is valid.
+ boolean imageReady = false;
+
+
+ //
+ // The current fog enable state
+ //
+ int fogOn = 0;
+
+ // The 3D Graphics context used for immediate mode rendering
+ // into this canvas.
+ GraphicsContext3D graphicsContext3D = null;
+ boolean waiting = false;
+ boolean swapDone = false;
+
+ GraphicsConfiguration graphicsConfiguration;
+
+ // The Java 3D Graphics2D object used for Java2D/AWT rendering
+ // into this Canvas3D
+ J3DGraphics2DImpl graphics2D = null;
+
+ // Lock used to synchronize the creation of the 2D and 3D
+ // graphics context objects
+ Object gfxCreationLock = new Object();
+
+ // The source of the currently loaded localToVWorld for this Canvas3D
+ // (used to only update the model matrix when it changes)
+ // Transform3D localToVWorldSrc = null;
+
+ // The current vworldToEc Transform
+ Transform3D vworldToEc = new Transform3D();
+
+ // The view transform (VPC to EC) for the current eye.
+ // NOTE that this is *read-only*
+ Transform3D vpcToEc;
+
+ // The window field when running Windows is the native HDC. With X11 it
+ // is the handle to the native X11 drawable.
+ int window = 0;
+
+ // The vid field when running Windows is the pixel format. With X11 it is
+ // the visual id.
+ int vid = 0;
+
+ // The visInfo field is only used when running X11. It contains a pointer
+ // to the native XVisualInfo structure, since in some cases the visual id
+ // alone isn't sufficient for the native OpenGL renderer (e.g., when using
+ // Solaris OpenGL with Xinerama mode disabled).
+ long visInfo = 0;
+
+ // visInfoTable is a static hashtable which allows getBestConfiguration()
+ // in NativeConfigTemplate3D to map a GraphicsConfiguration to the pointer
+ // to the actual XVisualInfo that glXChooseVisual() returns. The Canvas3D
+ // doesn't exist at the time getBestConfiguration() is called, and
+ // X11GraphicsConfig neither maintains this pointer nor provides a public
+ // constructor to allow Java 3D to extend it.
+ static Hashtable visInfoTable = new Hashtable();
+
+ // The native graphics version and renderer information
+ String nativeGraphicsVersion = null;
+
+ NativeWSInfo nativeWSobj = new NativeWSInfo();
+ boolean firstPaintCalled = false;
+
+ // This reflects whether or not this canvas has seen an addNotify.
+ boolean added = false;
+
+ // This is the id for the underlying graphics context structure.
+ long ctx = 0;
+
+ // since the ctx id can be the same as the previous one,
+ // we need to keep a time stamp to differentiate the contexts with the
+ // same id
+ volatile long ctxTimeStamp = 0;
+
+ // The current context setting for local eye lighting
+ boolean ctxEyeLightingEnable = false;
+
+ // This AppearanceRetained Object refelects the current state of this
+ // canvas. It is used to optimize setting of attributes at render time.
+ AppearanceRetained currentAppear = new AppearanceRetained();
+
+ // This MaterialRetained Object refelects the current state of this canvas.
+ // It is used to optimize setting of attributes at render time.
+ MaterialRetained currentMaterial = new MaterialRetained();
+
+ /**
+ * The object used for View Frustum Culling
+ */
+ CachedFrustum viewFrustum = new CachedFrustum();
+
+ /**
+ * The RenderBin bundle references used to decide what the underlying
+ * context contains.
+ */
+ LightBin lightBin = null;
+ EnvironmentSet environmentSet = null;
+ AttributeBin attributeBin = null;
+ RenderMolecule renderMolecule = null;
+ PolygonAttributesRetained polygonAttributes = null;
+ LineAttributesRetained lineAttributes = null;
+ PointAttributesRetained pointAttributes = null;
+ MaterialRetained material = null;
+ boolean enableLighting = false;
+ TransparencyAttributesRetained transparency = null;
+ ColoringAttributesRetained coloringAttributes = null;
+ Transform3D modelMatrix = null;
+ TextureBin textureBin = null;
+
+
+ /**
+ * cached RenderBin states for lazy native states update
+ */
+ LightRetained lights[] = null;
+ int frameCount[] = null;
+ long enableMask = -1;
+ FogRetained fog = null;
+ ModelClipRetained modelClip = null;
+ Color3f sceneAmbient = new Color3f();
+ TextureUnitStateRetained texUnitState[] = null;
+
+ /**
+ * cached View states for lazy native states update
+ */
+ // - DVR support.
+ float cachedDvrFactor = 1.0f;
+ boolean cachedDvrResizeCompensation = true;
+
+ /**
+ * These cached values are only used in Pure Immediate and Mixed Mode rendering
+ */
+ TextureRetained texture = null;
+ TextureAttributesRetained texAttrs = null;
+ TexCoordGenerationRetained texCoordGeneration = null;
+ RenderingAttributesRetained renderingAttrs = null;
+ AppearanceRetained appearance = null;
+
+ // only used in Mixed Mode rendering
+ Object appHandle = null;
+
+ /**
+ * Set to true when any one of texture state use
+ * Texture Generation linear mode. This is used for D3D
+ * temporary turn displayList off and do its own coordinate
+ * generation since D3D don't support it.
+ */
+ boolean texLinearMode = false;
+
+ /**
+ * Dirty bit to determine if the NodeComponent needs to be re-sent
+ * down to the underlying API
+ */
+ int canvasDirty = 0xffff;
+
+ // True when either one of dirtyRenderMoleculeList,
+ // dirtyDlistPerRinfoList, dirtyRenderAtomList size > 0
+ boolean dirtyDisplayList = false;
+
+ ArrayList dirtyRenderMoleculeList = new ArrayList();
+ ArrayList dirtyRenderAtomList = new ArrayList();
+ // List of (Rm, rInfo) pair of individual dlists that need to be rebuilt
+ ArrayList dirtyDlistPerRinfoList = new ArrayList();
+
+ ArrayList displayListResourceFreeList = new ArrayList();
+ ArrayList textureIdResourceFreeList = new ArrayList();
+
+ // an unique bit to identify this canvas
+ int canvasBit = 0;
+
+ // Avoid using this as lock, it cause deadlock
+ Object cvLock = new Object();
+ Object evaluateLock = new Object();
+ Object dirtyMaskLock = new Object();
+
+ // Use by D3D when toggle between window/fullscreen mode.
+ // Note that in fullscreen mode, the width and height get
+ // by canvas is smaller than expected.
+ boolean fullScreenMode = false;
+ int fullscreenWidth;
+ int fullscreenHeight;
+
+ // For D3D, instead of using the same variable in Renderer,
+ // each canvas has to build its own displayList.
+ boolean needToRebuildDisplayList = false;
+
+ // Use by D3D when canvas resize/toggle in pure immediate mode
+ int reEvaluateCanvasCmd = 0;
+
+ // Read-only flag that indicates whether the following texture features
+ // are supported for this canvas.
+
+ static final int TEXTURE_3D = 0x0001;
+ static final int TEXTURE_COLOR_TABLE = 0x0002;
+ static final int TEXTURE_MULTI_TEXTURE = 0x0004;
+ static final int TEXTURE_COMBINE = 0x0008;
+ static final int TEXTURE_COMBINE_DOT3 = 0x0010;
+ static final int TEXTURE_COMBINE_SUBTRACT = 0x0020;
+ static final int TEXTURE_REGISTER_COMBINERS = 0x0040;
+ static final int TEXTURE_CUBE_MAP = 0x0080;
+ static final int TEXTURE_SHARPEN = 0x0100;
+ static final int TEXTURE_DETAIL = 0x0200;
+ static final int TEXTURE_FILTER4 = 0x0400;
+ static final int TEXTURE_ANISOTROPIC_FILTER = 0x0800;
+ static final int TEXTURE_LOD_RANGE = 0x1000;
+ static final int TEXTURE_LOD_OFFSET = 0x2000;
+ // Use by D3D to indicate using one pass Blend mode
+ // if Texture interpolation mode is support.
+ static final int TEXTURE_LERP = 0x4000;
+
+ int textureExtendedFeatures = 0;
+
+ // Extensions supported by the underlying canvas
+ static final int SUN_GLOBAL_ALPHA = 0x1;
+ static final int EXT_ABGR = 0x2;
+ static final int EXT_BGR = 0x4;
+ static final int EXT_RESCALE_NORMAL = 0x8;
+ static final int EXT_MULTI_DRAW_ARRAYS = 0x10;
+ static final int SUN_MULTI_DRAW_ARRAYS = 0x20;
+ static final int SUN_CONSTANT_DATA = 0x40;
+ static final int EXT_SEPARATE_SPECULAR_COLOR = 0x80;
+ static final int ARB_TRANSPOSE_MATRIX = 0x100;
+ static final int ARB_MULTISAMPLE = 0x200;
+ static final int EXT_COMPILED_VERTEX_ARRAYS = 0x400;
+ static final int SUN_VIDEO_RESIZE = 0x800;
+ static final int STENCIL_BUFFER = 0x1000;
+
+ // The following three variables are set by
+ // createContext()/createQueryContext() callback
+ // Supported Extensions
+ int extensionsSupported = 0;
+
+ // Anisotropic Filter degree
+ float anisotropicDegreeMax = 1.0f;
+
+ // Texture Boundary Width Max
+ int textureBoundaryWidthMax = 0;
+
+ // Texture Width Max
+ int textureWidthMax = 0;
+
+ // Texture Height Max
+ int textureHeightMax = 0;
+
+ // Cached position & size for CanvasViewCache.
+ // We don't want to call canvas.getxx method in Renderer
+ // since it will cause deadlock as removeNotify() need to get
+ // component lock of Canvas also and need to wait Renderer to
+ // finish before continue. So we invoke the method now in
+ // CanvasViewEventCatcher.
+ Point newPosition = new Point();
+ Dimension newSize = new Dimension();
+
+ // Remember OGL context resources to free
+ // before context is destroy.
+ // It is used when sharedCtx = false;
+ ArrayList textureIDResourceTable = new ArrayList(5);
+
+ // The following variables are used by the lazy download of
+ // states code to keep track of the set of current to be update bins
+
+ static final int LIGHTBIN_BIT = 0x0;
+ static final int ENVIRONMENTSET_BIT = 0x1;
+ static final int ATTRIBUTEBIN_BIT = 0x2;
+ static final int TEXTUREBIN_BIT = 0x3;
+ static final int RENDERMOLECULE_BIT = 0x4;
+ static final int TRANSPARENCY_BIT = 0x5;
+
+ // bitmask to specify if the corresponding "bin" needs to be updated
+ int stateUpdateMask = 0;
+
+ // the set of current "bins" that is to be updated, the stateUpdateMask
+ // specifies if each bin in this set is updated or not.
+ Object curStateToUpdate[] = new Object[6];
+
+
+ // Native method for determining the number of texture unit supported
+ native int getTextureUnitCount(long ctx);
+
+ // Native method for determining the texture color table size
+ // in the underlying API for this Canvas3D.
+ /* native int getTextureColorTableSize(long ctx); */
+
+ // This is the native method for creating the underlying graphics context.
+ native long createContext(long display, int window, int vid, long visInfo,
+ long shareCtx, boolean isSharedCtx,
+ boolean offScreen);
+
+ native void createQueryContext(long display, int window, int vid, boolean offScreen, int width, int height);
+ native static void destroyContext(long display, int window, long context);
+
+ // This is the native for creating offscreen buffer
+ native int createOffScreenBuffer(long ctx, long display, int vid, int width, int height);
+ native void destroyOffScreenBuffer(long ctx, long display, int window);
+
+ // This is the native for reading the image from the offscreen buffer
+ native void readOffScreenBuffer(long ctx, int format, int width, int height);
+
+ // This is the native method for doing accumulation.
+ native void accum(long ctx, float value );
+
+ // This is the native method for doing accumulation return.
+ native void accumReturn(long ctx);
+
+ // This is the native method for clearing the accumulation buffer.
+ native void clearAccum(long ctx);
+
+ // This is the native method for getting the number of lights the underlying
+ // native library can support.
+ native int getNumCtxLights(long ctx);
+
+ // Native method for decal 1st child setup
+ native boolean decal1stChildSetup(long ctx);
+
+ // Native method for decal nth child setup
+ native void decalNthChildSetup(long ctx);
+
+ // Native method for decal reset
+ native void decalReset(long ctx, boolean depthBufferEnable);
+
+ // Native method for decal reset
+ native void ctxUpdateEyeLightingEnable(long ctx, boolean localEyeLightingEnable);
+
+ // The following three methods are used in multi-pass case
+
+ // Native method for setting the depth func
+ native void setDepthFunc(long ctx, int func);
+
+ // native method for setting blend color
+ native void setBlendColor(long ctx, float red, float green,
+ float blue, float alpha);
+
+ // native method for setting blend func
+ native void setBlendFunc(long ctx, int src, int dst);
+
+ // native method for setting fog enable flag
+ native void setFogEnableFlag(long ctx, boolean enableFlag);
+
+ // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle supported
+ native void setFullSceneAntialiasing(long ctx, boolean enable);
+
+ // notify D3D that Canvas is resize
+ native int resizeD3DCanvas(long ctx);
+
+ // notify D3D to toggle between FullScreen and window mode
+ native int toggleFullScreenMode(long ctx);
+
+ native void setGlobalAlpha(long ctx, float alpha);
+ native void disableGlobalAlpha(long ctx);
+
+ // Native method to update separate specular color control
+ native void updateSeparateSpecularColorEnable(long ctx, boolean control);
+
+ // Initialization for D3D when scene begin
+ native void beginScene(long ctx);
+ native void endScene(long ctx);
+
+ // True under Solaris,
+ // False under windows when display mode <= 8 bit
+ native boolean validGraphicsMode();
+
+ /**
+ * The list of lights that are currently being represented in the native
+ * graphics context.
+ */
+ LightRetained[] currentLights = null;
+
+ /**
+ * Flag to override RenderAttributes.depthBufferWriteEnable
+ */
+ boolean depthBufferWriteEnableOverride = false;
+
+ /**
+ * Flag to override RenderAttributes.depthBufferEnable
+ */
+ boolean depthBufferEnableOverride = false;
+
+ // current state of depthBufferWriteEnable
+ boolean depthBufferWriteEnable = true;
+
+ boolean vfPlanesValid = false;
+
+ // The event catcher for this canvas.
+ EventCatcher eventCatcher;
+
+ // The view event catcher for this canvas.
+ CanvasViewEventCatcher canvasViewEventCatcher;
+
+ // The parent window for this canvas.
+ Container parent;
+
+ // flag that indicates if light has changed
+ boolean lightChanged = false;
+
+ // resource control object
+ DrawingSurfaceObject drawingSurfaceObject;
+
+ // true if context is valid for rendering
+ boolean validCtx = false;
+
+ // true if canvas is valid for rendering
+ boolean validCanvas = false;
+
+ // true if ctx changed between render and swap. In this case
+ // cv.canvasDirty flag will not reset in Renderer.
+ // This case happen when GraphicsContext3D invoked doClear()
+ // and canvas removeNotify() called while Renderer is running
+ boolean ctxChanged = false;
+
+ // native method for setting light enables
+ native void setLightEnables(long ctx, long enableMask, int maxLights);
+
+ // native method for setting scene ambient
+ native void setSceneAmbient(long ctx, float red, float green, float blue);
+
+ // native method for disabling fog
+ native void disableFog(long ctx);
+
+ // native method for disabling modelClip
+ native void disableModelClip(long ctx);
+
+ // native method for setting default RenderingAttributes
+ native void resetRenderingAttributes(long ctx,
+ boolean depthBufferWriteEnableOverride,
+ boolean depthBufferEnableOverride);
+
+ // native method for setting default texture
+ native void resetTextureNative(long ctx, int texUnitIndex);
+
+ // native method for activating a particular texture unit
+ native void activeTextureUnit(long ctx, int texUnitIndex);
+
+ // native method for setting default TexCoordGeneration
+ native void resetTexCoordGeneration(long ctx);
+
+ // native method for setting default TextureAttributes
+ native void resetTextureAttributes(long ctx);
+
+ // native method for setting default PolygonAttributes
+ native void resetPolygonAttributes(long ctx);
+
+ // native method for setting default LineAttributes
+ native void resetLineAttributes(long ctx);
+
+ // native method for setting default PointAttributes
+ native void resetPointAttributes(long ctx);
+
+ // native method for setting default TransparencyAttributes
+ native void resetTransparency(long ctx, int geometryType,
+ int polygonMode, boolean lineAA,
+ boolean pointAA);
+
+ // native method for setting default ColoringAttributes
+ native void resetColoringAttributes(long ctx,
+ float r, float g,
+ float b, float a,
+ boolean enableLight);
+
+ // native method for setting Material when no material is present
+ native void updateMaterial(long ctx, float r, float g, float b, float a);
+
+
+ // native method for updating the texture unit state map
+ native void updateTexUnitStateMap(long ctx, int numActiveTexUnit,
+ int[] texUnitStateMap);
+
+ /**
+ * This native method makes sure that the rendering for this canvas
+ * gets done now.
+ */
+ native void syncRender(long ctx, boolean wait);
+
+ // Default graphics configuration
+ private static GraphicsConfiguration defaultGcfg = null;
+
+ // Returns default graphics configuration if user passes null
+ // into the Canvas3D constructor
+ private static synchronized GraphicsConfiguration defaultGraphicsConfiguration() {
+ if (defaultGcfg == null) {
+ GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
+ defaultGcfg = GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getBestConfiguration(template);
+ }
+ return defaultGcfg;
+ }
+
+ private static synchronized GraphicsConfiguration
+ checkForValidGraphicsConfig(GraphicsConfiguration gconfig) {
+
+ if (gconfig == null) {
+ // Print out warning if Canvas3D is called with a
+ // null GraphicsConfiguration
+ System.err.println("************************************************************************");
+ System.err.println(J3dI18N.getString("Canvas3D7"));
+ System.err.println(J3dI18N.getString("Canvas3D18"));
+ System.err.println("************************************************************************");
+ return defaultGraphicsConfiguration();
+ }
+
+ if (!J3dGraphicsConfig.isValidConfig(gconfig)) {
+ // Print out warning if Canvas3D is called with a
+ // GraphicsConfiguration that wasn't created from a
+ // GraphicsConfigTemplate3D (Solaris only).
+ System.err.println("************************************************************************");
+ System.err.println(J3dI18N.getString("Canvas3D21"));
+ System.err.println(J3dI18N.getString("Canvas3D22"));
+ System.err.println("************************************************************************");
+ return defaultGraphicsConfiguration();
+ }
+
+ return gconfig;
+ }
+
+ /**
+ * Constructs and initializes a new Canvas3D object that Java 3D
+ * can render into. The following Canvas3D attributes are initialized
+ * to default values as shown:
+ * <ul>
+ * left manual eye in image plate : (0.142, 0.135, 0.4572)<br>
+ * right manual eye in image plate : (0.208, 0.135, 0.4572)<br>
+ * stereo enable : true<br>
+ * double buffer enable : true<br>
+ * monoscopic view policy : View.CYCLOPEAN_EYE_VIEW<br>
+ * off-screen mode : false<br>
+ * off-screen buffer : null<br>
+ * off-screen location : (0,0)<br>
+ * </ul>
+ *
+ * @param graphicsConfiguration a valid GraphicsConfiguration object that
+ * will be used to create the canvas. This object should not be null and
+ * should be created using a GraphicsConfigTemplate3D or the
+ * getPreferredConfiguration() method of the SimpleUniverse utility. For
+ * backward compatibility with earlier versions of Java 3D, a null or
+ * default GraphicsConfiguration will still work when used to create a
+ * Canvas3D on the default screen, but an error message will be printed.
+ * A NullPointerException or IllegalArgumentException will be thrown in a
+ * subsequent release.
+ *
+ * @exception IllegalArgumentException if the specified
+ * GraphicsConfiguration does not support 3D rendering
+ */
+ public Canvas3D(GraphicsConfiguration graphicsConfiguration) {
+ this(checkForValidGraphicsConfig(graphicsConfiguration), false);
+
+ // TODO 1.4: remove call to checkForValidGraphicsConfig.
+ // Call should then be:
+ // this(graphicsConfiguration, false);
+ }
+
+ /**
+ * Constructs and initializes a new Canvas3D object that Java 3D
+ * can render into.
+ *
+ * @param graphicsConfiguration a valid GraphicsConfiguration object
+ * that will be used to create the canvas. This must be created either
+ * with a GraphicsConfigTemplate3D or by using the
+ * getPreferredConfiguration() method of the SimpleUniverse utility.
+ *
+ * @param offScreen a flag that indicates whether this canvas is
+ * an off-screen 3D rendering canvas. Note that if offScreen
+ * is set to true, this Canvas3D object cannot be used for normal
+ * rendering; it should not be added to any Container object.
+ *
+ * @exception NullPointerException if the GraphicsConfiguration
+ * is null.
+ *
+ * @exception IllegalArgumentException if the specified
+ * GraphicsConfiguration does not support 3D rendering
+ *
+ * @since Java 3D 1.2
+ */
+ public Canvas3D(GraphicsConfiguration graphicsConfiguration,
+ boolean offScreen) {
+
+ super(graphicsConfiguration);
+
+ if (graphicsConfiguration == null) {
+ throw new NullPointerException
+ (J3dI18N.getString("Canvas3D19"));
+ }
+
+ if (!J3dGraphicsConfig.isValidConfig(graphicsConfiguration)) {
+ throw new IllegalArgumentException
+ (J3dI18N.getString("Canvas3D17"));
+ }
+
+ if (!J3dGraphicsConfig.isValidPixelFormat(graphicsConfiguration)) {
+ throw new IllegalArgumentException
+ (J3dI18N.getString("Canvas3D17"));
+ }
+
+ VirtualUniverse.createMC();
+
+ this.offScreen = offScreen;
+ this.graphicsConfiguration = graphicsConfiguration;
+
+ Object visInfoObject;
+ vid = nativeWSobj.getCanvasVid(graphicsConfiguration);
+ visInfoObject = visInfoTable.get(graphicsConfiguration);
+
+ if ((visInfoObject != null) && (visInfoObject instanceof Long)) {
+ visInfo = ((Long)visInfoObject).longValue();
+ }
+
+ if (offScreen) {
+ screen = new Screen3D(graphicsConfiguration, offScreen);
+
+ // TODO: keep a list of off-screen Screen3D objects?
+ // Does this list need to be grouped by GraphicsDevice?
+
+
+ // since this canvas will not receive the addNotify
+ // callback from AWT, set the added flag here
+ added = true;
+ synchronized(dirtyMaskLock) {
+ cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+ }
+
+ // this canvas will not receive the paint callback either,
+ // so set the necessary flags here as well
+ firstPaintCalled = true;
+ ctx = 0;
+ evaluateActive();
+
+ // create the rendererStructure object
+ //rendererStructure = new RendererStructure();
+ offScreenCanvasLoc = new Point(0, 0);
+ offScreenCanvasSize = new Dimension(0, 0);
+ offScreenCanvasClippedLoc = new Point(0, 0);
+ offScreenCanvasClippedSize = new Dimension(0, 0);
+
+ this.setLocation(offScreenCanvasLoc);
+ this.setSize(offScreenCanvasSize);
+ newSize = offScreenCanvasSize;
+ newPosition = offScreenCanvasLoc;
+
+ } else {
+ GraphicsDevice graphicsDevice;
+ graphicsDevice = graphicsConfiguration.getDevice();
+
+ eventCatcher = new EventCatcher(this);
+ canvasViewEventCatcher = new CanvasViewEventCatcher(this);
+
+ synchronized(VirtualUniverse.mc.deviceScreenMap) {
+ screen = (Screen3D) VirtualUniverse.mc.
+ deviceScreenMap.get(graphicsDevice);
+
+ if (screen == null) {
+ screen = new Screen3D(graphicsConfiguration, offScreen);
+ VirtualUniverse.mc.deviceScreenMap.put(graphicsDevice,
+ screen);
+ }
+ }
+
+ }
+
+ drawingSurfaceObject = new DrawingSurfaceObjectAWT
+ (this, VirtualUniverse.mc.awt, screen.display, screen.screen,
+ VirtualUniverse.mc.xineramaDisabled);
+
+ lights = new LightRetained[VirtualUniverse.mc.maxLights];
+ frameCount = new int[VirtualUniverse.mc.maxLights];
+ for (int i=0; i<frameCount.length;i++) {
+ frameCount[i] = -1;
+ }
+
+ // Get double buffer, stereo available, scene antialiasing
+ // flags from template.
+ GraphicsConfigTemplate3D.getGraphicsConfigFeatures(this);
+
+ useDoubleBuffer = doubleBufferEnable && doubleBufferAvailable;
+ useStereo = stereoEnable && stereoAvailable;
+ useSharedCtx = VirtualUniverse.mc.isSharedCtx;
+
+ }
+
+ /**
+ * This method overrides AWT's handleEvent class...
+ */
+ void sendEventToBehaviorScheduler(AWTEvent evt) {
+
+ ViewPlatform vp;
+
+
+ if ((view != null) && ((vp = view.getViewPlatform()) != null)) {
+ VirtualUniverse univ =
+ ((ViewPlatformRetained)(vp.retained)).universe;
+ if (univ != null) {
+ univ.behaviorStructure.handleAWTEvent(evt);
+ }
+ }
+ }
+
+ /**
+ * This version looks for the view and notifies it.
+ */
+ void redraw() {
+ if ((view != null) && active && isRunning) {
+ view.repaint();
+ }
+ }
+
+ /**
+ * Canvas3D uses the paint callback to track when it is possible to
+ * render into the canvas. Subclasses of Canvas3D that override this
+ * method need to call super.paint() in their paint method for Java 3D
+ * to function properly.
+ * @param g the graphics context
+ */
+ public void paint(Graphics g) {
+
+ if (!firstPaintCalled && added && validCanvas &&
+ validGraphicsMode()) {
+
+ try {
+ newSize = getSize();
+ newPosition = getLocationOnScreen();
+ } catch (IllegalComponentStateException e) {
+ return;
+ }
+
+ synchronized (drawingSurfaceObject) {
+ drawingSurfaceObject.getDrawingSurfaceObjectInfo();
+ }
+
+ firstPaintCalled = true;
+ visible = true;
+ evaluateActive();
+ }
+ redraw();
+ }
+
+ // When this canvas is added to a frame, this notification gets called. We
+ // can get drawing surface information at this time. Note: we cannot get
+ // the X11 window id yet, unless it is a reset condition.
+ /**
+ * Canvas3D uses the addNotify callback to track when it is added
+ * to a container. Subclasses of Canvas3D that override this
+ * method need to call super.addNotify() in their addNotify() method for Java 3D
+ * to function properly.
+ */
+ public void addNotify() {
+ Renderer rdr = null;
+
+ if (isRunning && (screen != null)) {
+ // If there is other Canvas3D in the same screen
+ // rendering, stop it before JDK create new Canvas
+
+ rdr = screen.renderer;
+ if (rdr != null) {
+ VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, rdr);
+ while (!rdr.userStop) {
+ MasterControl.threadYield();
+ }
+ }
+ }
+
+
+ super.addNotify();
+ screen.addUser(this);
+
+ parent = this.getParent();
+ while (!(parent instanceof Window)) {
+ parent = parent.getParent();
+ }
+
+ ((Window)parent).addWindowListener(eventCatcher);
+
+ if (VirtualUniverse.mc.isD3D()) {
+ ((Window)parent).addComponentListener(eventCatcher);
+ }
+
+ if (!offScreen) {
+ if(canvasViewEventCatcher.parentList.size() > 0) {
+ Component comp;
+ //Release and clear.
+ for(int i=0; i<canvasViewEventCatcher.parentList.size(); i++) {
+ comp = (Component)canvasViewEventCatcher.parentList.get(i);
+ comp.removeComponentListener(canvasViewEventCatcher);
+ }
+ canvasViewEventCatcher.parentList.clear();
+ }
+
+ Component parent = (Component) this.getParent();
+ while(parent != null) {
+ parent.addComponentListener(canvasViewEventCatcher);
+ canvasViewEventCatcher.parentList.add(parent);
+ parent = parent.getParent();
+ }
+ // Need to traverse up the parent and add listener.
+ this.addComponentListener(canvasViewEventCatcher);
+ }
+
+ synchronized(dirtyMaskLock) {
+ cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+ }
+
+ canvasBit = VirtualUniverse.mc.getCanvasBit();
+ validCanvas = true;
+ added = true;
+
+ // In case the same canvas is removed and add back,
+ // we have to change isRunningStatus back to true;
+ if (isRunning) {
+ isRunningStatus = true;
+ }
+
+
+ if (rdr != null) {
+ rdr.userStop = false;
+ }
+ ctxTimeStamp = 0;
+ if ((view != null) && (view.universe != null)) {
+ view.universe.checkForEnableEvents();
+ }
+
+ }
+
+ // When this canvas is removed a frame, this notification gets called. We
+ // need to release the native context at this time. The underlying window
+ // is about to go away.
+ /**
+ * Canvas3D uses the removeNotify callback to track when it is removed
+ * from a container. Subclasses of Canvas3D that override this
+ * method need to call super.removeNotify() in their removeNotify()
+ * method for Java 3D to function properly.
+ */
+
+ public void removeNotify() {
+
+ Renderer rdr = null;
+
+ if (isRunning && (screen != null)) {
+ // If there is other Canvas3D in the same screen
+ // rendering, stop it before JDK create new Canvas
+
+ rdr = screen.renderer;
+ if (rdr != null) {
+ VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, rdr);
+ while (!rdr.userStop) {
+ MasterControl.threadYield();
+ }
+ }
+ }
+
+ if (!offScreen) {
+ firstPaintCalled = false;
+ }
+
+
+ // Note that although renderer userStop is true,
+ // MasterControl can still schedule renderer to run through
+ // runMonotor(RUN_RENDERER_CLEANUP) which skip userStop
+ // thread checking.
+ // For non-offscreen rendering the following call will
+ // block waiting until all resources is free before
+ // continue
+
+ synchronized (drawingSurfaceObject) {
+ validCtx = false;
+ validCanvas = false;
+ }
+
+ removeCtx(false);
+
+ synchronized (drawingSurfaceObject) {
+
+ DrawingSurfaceObjectAWT dso =
+ (DrawingSurfaceObjectAWT)drawingSurfaceObject;
+ // get nativeDS before it is set to 0 in invalidate()
+ long ds = dso.getDS();
+ long ds_struct[] = {ds, dso.getDSI()};
+ if (ds != 0) {
+ VirtualUniverse.mc.postRequest(
+ MasterControl.FREE_DRAWING_SURFACE,
+ ds_struct);
+ }
+
+ drawingSurfaceObject.invalidate();
+
+ }
+
+ visible = false;
+
+ screen.removeUser(this);
+ evaluateActive();
+
+ VirtualUniverse.mc.freeCanvasBit(canvasBit);
+
+ ra = null;
+ graphicsContext3D = null;
+
+ ctx = 0;
+ // must be after removeCtx() because
+ // it will free graphics2D textureID
+ graphics2D = null;
+
+ super.removeNotify();
+
+ // This may happen if user explicity call this before
+ // addNotify()
+
+ if (eventCatcher != null) {
+ this.removeComponentListener(eventCatcher);
+ this.removeFocusListener(eventCatcher);
+ this.removeKeyListener(eventCatcher);
+ this.removeMouseListener(eventCatcher);
+ this.removeMouseMotionListener(eventCatcher);
+ eventCatcher.reset();
+ }
+
+
+ if (canvasViewEventCatcher != null) {
+ if (canvasViewEventCatcher.parentList.size() > 0) {
+ //Release and clear.
+ for(int i=canvasViewEventCatcher.parentList.size()-1; i>=0; i--) {
+ Component comp =
+ (Component)canvasViewEventCatcher.parentList.get(i);
+ comp.removeComponentListener(canvasViewEventCatcher);
+ }
+ canvasViewEventCatcher.parentList.clear();
+ }
+
+ // Need to traverse up the parent and remove listener.
+ this.removeComponentListener(canvasViewEventCatcher);
+ }
+
+ if (parent != null) {
+ ((Window)parent).removeWindowListener(eventCatcher);
+ if (VirtualUniverse.mc.isD3D()) {
+ ((Window)parent).removeComponentListener(eventCatcher);
+ }
+ }
+
+ if (parent != null) {
+ parent.requestFocus();
+ }
+
+ if (!offScreen) {
+ added = false;
+ }
+
+ if (rdr != null) {
+ rdr.userStop = false;
+ }
+ }
+
+ // This decides if the canvas is active
+ void evaluateActive() {
+ // Note that no need to check for isRunning, we want
+ // view register in order to create scheduler in pure immedite mode
+ // Also we can't use this as lock, otherwise there is a
+ // deadlock where updateViewCache get a lock of this and
+ // get a lock of this component. But Container
+ // remove will get a lock of this componet follows by evaluateActive.
+
+ synchronized (evaluateLock) {
+ if ((visible || offScreen) && firstPaintCalled) {
+
+ if (!active) {
+ active = true;
+ if (pendingView != null) {
+ pendingView.evaluateActive();
+ }
+ } else {
+ if ((pendingView != null) &&
+ !pendingView.activeStatus) {
+ pendingView.evaluateActive();
+ }
+ }
+ } else {
+ if (active) {
+ active = false;
+ if (view != null) {
+ view.evaluateActive();
+ }
+ }
+ }
+ }
+
+ if ((view != null) && (!active)) {
+ VirtualUniverse u = view.universe;
+ if ((u != null) && !u.isSceneGraphLock) {
+ u.waitForMC();
+ }
+ }
+ }
+
+ void setFrustumPlanes(Vector4d[] planes) {
+ viewFrustum.set(planes);
+ }
+
+
+ /**
+ * Retrieve the Screen3D object that this Canvas3D is attached to.
+ * If this Canvas3D is an off-screen buffer, a new Screen3D object
+ * is created corresponding to the off-screen buffer.
+ * @return the 3D screen object that this Canvas3D is attached to
+ */
+ public Screen3D getScreen3D() {
+ return screen;
+ }
+
+ /**
+ * Get the immediate mode 3D graphics context associated with
+ * this Canvas3D. A new graphics context object is created if one does
+ * not already exist.
+ * @return a GraphicsContext3D object that can be used for immediate
+ * mode rendering to this Canvas3D.
+ */
+ public GraphicsContext3D getGraphicsContext3D() {
+
+ synchronized(gfxCreationLock) {
+ if (graphicsContext3D == null)
+ graphicsContext3D = new GraphicsContext3D(this);
+ }
+
+ return graphicsContext3D;
+ }
+
+ /**
+ * Get the 2D graphics object associated with
+ * this Canvas3D. A new 2D graphics object is created if one does
+ * not already exist.
+ *
+ * @return a Graphics2D object that can be used for Java 2D
+ * rendering into this Canvas3D.
+ *
+ * @since Java 3D 1.2
+ */
+ public J3DGraphics2D getGraphics2D() {
+ synchronized(gfxCreationLock) {
+ if (graphics2D == null)
+ graphics2D = new J3DGraphics2DImpl(this);
+ }
+
+ return graphics2D;
+ }
+
+ /**
+ * This routine is called by the Java 3D rendering loop after clearing
+ * the canvas and before any rendering has been done for this frame.
+ * Applications that wish to perform operations in the rendering loop,
+ * prior to any actual rendering may override this function.
+ *
+ * <p>
+ * Updates to live Geometry, Texture, and ImageComponent objects
+ * in the scene graph are not allowed from this method.
+ *
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method.
+ */
+ public void preRender() {
+ // Do nothing; the user overrides this to cause some action
+ }
+
+ /**
+ * This routine is called by the Java 3D rendering loop after completing
+ * all rendering to the canvas for this frame and before the buffer swap.
+ * Applications that wish to perform operations in the rendering loop,
+ * following any actual rendering may override this function.
+ *
+ * <p>
+ * Updates to live Geometry, Texture, and ImageComponent objects
+ * in the scene graph are not allowed from this method.
+ *
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method.
+ */
+ public void postRender() {
+ // Do nothing; the user overrides this to cause some action
+ }
+
+ /**
+ * This routine is called by the Java 3D rendering loop after completing
+ * all rendering to the canvas, and all other canvases associated with
+ * this view, for this frame following the buffer swap.
+ * Applications that wish to perform operations at the very
+ * end of the rendering loop may override this function.
+ * In off-screen mode, all rendering is copied to the off-screen
+ * buffer before this method is called.
+ *
+ * <p>
+ * Updates to live Geometry, Texture, and ImageComponent objects
+ * in the scene graph are not allowed from this method.
+ *
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method.
+ */
+ public void postSwap() {
+ // Do nothing; the user overrides this to cause some action
+ }
+
+ /**
+ * This routine is called by the Java 3D rendering loop during the
+ * execution of the rendering loop. It is called once for each
+ * field (i.e., once per frame on
+ * a mono system or once each for the right eye and left eye on a
+ * two-pass stereo system. This is intended for use by applications that
+ * want to mix retained/compiled-retained mode rendering with some
+ * immediate mode rendering. Applications that wish to perform
+ * operations during the rendering loop, may override this
+ * function.
+ *
+ * <p>
+ * Updates to live Geometry, Texture, and ImageComponent objects
+ * in the scene graph are not allowed from this method.
+ *
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method.
+ * <p>
+ *
+ * @param fieldDesc field description, one of: FIELD_LEFT, FIELD_RIGHT or
+ * FIELD_ALL. Applications that wish to work correctly in stereo mode
+ * should render the same image for both FIELD_LEFT and FIELD_RIGHT calls.
+ * If Java 3D calls the renderer with FIELD_ALL then the immediate mode
+ * rendering only needs to be done once.
+ */
+ public void renderField(int fieldDesc) {
+ // Do nothing; the user overrides this to cause some action
+ }
+
+ /**
+ * Stop the Java 3D renderer on this Canvas3D object. If the
+ * Java 3D renderer is currently running, the rendering will be
+ * synchronized before being stopped. No further rendering will be done
+ * to this canvas by Java 3D until the renderer is started again.
+ * In pure immediate mode this method should be called prior to adding
+ * this canvas to an active View object.
+ *
+ * @exception IllegalStateException if this Canvas3D is in
+ * off-screen mode.
+ */
+ public final void stopRenderer() {
+ if (offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D14"));
+
+ if (isRunning) {
+ VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, this);
+ isRunning = false;
+ }
+ }
+
+
+ /**
+ * Start the Java 3D renderer on this Canvas3D object. If the
+ * Java 3D renderer is not currently running, any rendering to other
+ * Canvas3D objects sharing the same View will be synchronized before this
+ * Canvas3D's renderer is (re)started. When a Canvas3D is created, it is
+ * initially marked as being started. This means that as soon as the
+ * Canvas3D is added to an active View object, the rendering loop will
+ * render the scene graph to the canvas.
+ */
+ public final void startRenderer() {
+ if (!isRunning) {
+ VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, this);
+ isRunning = true;
+ redraw();
+ }
+ }
+
+ /**
+ * Retrieves the state of the renderer for this Canvas3D object.
+ * @return the state of the renderer
+ *
+ * @since Java 3D 1.2
+ */
+ public final boolean isRendererRunning() {
+ return isRunning;
+ }
+
+
+ /**
+ * Retrieves a flag indicating whether this Canvas3D is an
+ * off-screen canvas.
+ *
+ * @return <code>true</code> if this Canvas3D is an off-screen canvas;
+ * <code>false</code> if this is an on-screen canvas.
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean isOffScreen() {
+ return offScreen;
+ }
+
+
+ /**
+ * Sets the off-screen buffer for this Canvas3D. The specified
+ * image is written into by the Java 3D renderer. The size of the
+ * specified ImageComponent determines the size, in pixels, of
+ * this Canvas3D--the size inherited from Component is ignored.
+ * <p>
+ * NOTE: the size, physical width, and physical height of the associated
+ * Screen3D must be set explicitly prior to rendering.
+ * Failure to do so will result in an exception.
+ * <p>
+ *
+ * @param buffer the image component that will be rendered into by
+ * subsequent calls to renderOffScreenBuffer.
+ *
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode.
+ * @exception RestrictedAccessException if an off-screen rendering
+ * is in process for this Canvas3D.
+ * @exception IllegalSharingException if the specified
+ * ImageComponent2D is used by more than one Canvas3D.
+ * @exception IllegalArgumentException if the specified
+ * ImageComponent2D is in by-reference mode and its
+ * RenderedImage is not an instance of a BufferedImage or
+ * if the ImageComponent2D format is FORMAT_CHANNEL8.
+ *
+ * @see #renderOffScreenBuffer
+ * @see Screen3D#setSize(int, int)
+ * @see Screen3D#setSize(Dimension)
+ * @see Screen3D#setPhysicalScreenWidth
+ * @see Screen3D#setPhysicalScreenHeight
+ *
+ * @since Java 3D 1.2
+ */
+ public void setOffScreenBuffer(ImageComponent2D buffer) {
+ ImageComponent2DRetained bufferRetained =
+ (ImageComponent2DRetained)buffer.retained;
+
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
+
+ if (offScreenRendering)
+ throw new RestrictedAccessException(J3dI18N.getString("Canvas3D2"));
+
+ if (bufferRetained.byReference &&
+ !(bufferRetained.bImage[0] instanceof BufferedImage)) {
+
+ throw new IllegalArgumentException(J3dI18N.getString("Canvas3D15"));
+ }
+
+ if (bufferRetained.format == ImageComponent.FORMAT_CHANNEL8) {
+ throw new IllegalArgumentException(J3dI18N.getString("Canvas3D16"));
+ }
+
+ // TODO: illegalSharing
+
+ if ((offScreenCanvasSize.width != bufferRetained.width) ||
+ (offScreenCanvasSize.height != bufferRetained.height)) {
+
+ if (window != 0) {
+ destroyOffScreenBuffer(ctx, screen.display, window);
+ removeCtx(true);
+ window = 0;
+ }
+
+ // set the canvas dimension according to the buffer dimension
+ offScreenCanvasSize.setSize(bufferRetained.width,
+ bufferRetained.height);
+ this.setSize(offScreenCanvasSize);
+
+ // create off screen buffer
+ window = createOffScreenBuffer(ctx, screen.display, vid,
+ offScreenCanvasSize.width, offScreenCanvasSize.height);
+ ctx = 0;
+ }
+ if (ctx != 0) {
+ removeCtx(true);
+ }
+
+ offScreenBuffer = buffer;
+
+ synchronized(dirtyMaskLock) {
+ cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+ }
+
+ }
+
+
+ /**
+ * Retrieves the off-screen buffer for this Canvas3D.
+ *
+ * @return the current off-screen buffer for this Canvas3D.
+ *
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent2D getOffScreenBuffer() {
+
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
+
+ return (offScreenBuffer);
+ }
+
+
+ /**
+ * Schedules the rendering of a frame into this Canvas3D's
+ * off-screen buffer. The rendering is done from the point of
+ * view of the View object to which this Canvas3D has been added.
+ * No rendering is performed if this Canvas3D object has not been
+ * added to an active View. This method does not wait for the rendering
+ * to actually happen. An application that wishes to know when
+ * the rendering is complete must either subclass Canvas3D and
+ * override the <code>postSwap</code> method, or call
+ * <code>waitForOffScreenRendering</code>.
+ *
+ * @exception NullPointerException if the off-screen buffer is null.
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode, or if either the width or the height of
+ * the associated Screen3D's size is <= 0, or if the associated
+ * Screen3D's physical width or height is <= 0.
+ * @exception RestrictedAccessException if an off-screen rendering
+ * is already in process for this Canvas3D or if the Java 3D renderer
+ * is stopped.
+ *
+ * @see #setOffScreenBuffer
+ * @see Screen3D#setSize(int, int)
+ * @see Screen3D#setSize(Dimension)
+ * @see Screen3D#setPhysicalScreenWidth
+ * @see Screen3D#setPhysicalScreenHeight
+ * @see #waitForOffScreenRendering
+ * @see #postSwap
+ *
+ * @since Java 3D 1.2
+ */
+ public void renderOffScreenBuffer() {
+
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
+
+ if (offScreenBuffer == null)
+ throw new NullPointerException(J3dI18N.getString("Canvas3D10"));
+
+ Dimension screenSize = screen.getSize();
+
+ if (screenSize.width <= 0)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D8"));
+
+ if (screenSize.height <= 0)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D9"));
+
+ if (screen.getPhysicalScreenWidth() <= 0.0)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D12"));
+
+ if (screen.getPhysicalScreenHeight() <= 0.0)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D13"));
+
+ if (offScreenRendering)
+ throw new RestrictedAccessException(J3dI18N.getString("Canvas3D2"));
+
+ if (!isRunning)
+ throw new RestrictedAccessException(J3dI18N.getString("Canvas3D11"));
+
+ if (!active)
+ return;
+
+ // determine the offScreen boundary
+ // do the boundary determination here because setCanvasLocation can
+ // be done at any time.
+
+ if ((offScreenCanvasLoc.x >= screenSize.width) ||
+ (offScreenCanvasLoc.y >= screenSize.height))
+ return;
+
+ if (offScreenCanvasLoc.x < 0) {
+ offScreenCanvasClippedLoc.x = 0 - offScreenCanvasLoc.x;
+ offScreenCanvasClippedSize.width =
+ offScreenCanvasSize.width - offScreenCanvasClippedLoc.x;
+ if (offScreenCanvasClippedSize.width > screenSize.width)
+ offScreenCanvasClippedSize.width = screenSize.width;
+ } else {
+ offScreenCanvasClippedLoc.x = 0;
+ offScreenCanvasClippedSize.width = offScreenCanvasSize.width;
+ if ((offScreenCanvasLoc.x + offScreenCanvasClippedSize.width)
+ > screenSize.width)
+ offScreenCanvasClippedSize.width =
+ screenSize.width - offScreenCanvasLoc.x;
+ }
+
+
+ int lly = offScreenCanvasLoc.y + offScreenCanvasSize.height;
+ if (lly < 0) {
+ return;
+ } else if (lly <= screenSize.height) {
+ offScreenCanvasClippedLoc.y = 0;
+ if (offScreenCanvasLoc.y < 0)
+ offScreenCanvasClippedSize.height = lly;
+ else
+ offScreenCanvasClippedSize.height = offScreenCanvasSize.height;
+ } else if (lly > screenSize.height) {
+ offScreenCanvasClippedSize.height =
+ screenSize.height - offScreenCanvasLoc.y;
+ offScreenCanvasClippedLoc.y = lly - screenSize.height;
+ }
+
+ offScreenRendering = true;
+
+ if (view.inCanvasCallback) {
+ if (screen.renderer == null) {
+ // It is possible that screen.renderer = null when this View
+ // is shared by another onScreen Canvas and this callback
+ // is from that Canvas. In this case it need one more
+ // round before the renderer.
+ screen.renderer = (Renderer) screen.deviceRendererMap.get(
+ screen.graphicsDevice);
+ // screen.renderer may equal to null when multiple
+ // screen is used and this Canvas3D is in different
+ // screen sharing the same View not yet initialize.
+ }
+
+ // if called from render call back, send a message directly to
+ // the renderer message queue, and call renderer doWork
+ // to do the offscreen rendering now
+ if (Thread.currentThread() == screen.renderer) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.RENDER_THREAD;
+ createMessage.type = J3dMessage.RENDER_OFFSCREEN;
+ createMessage.universe = this.view.universe;
+ createMessage.view = this.view;
+ createMessage.args[0] = this;
+
+ screen.renderer.rendererStructure.addMessage(createMessage);
+
+ // modify the args to reflect offScreen rendering
+ screen.renderer.args = new Object[4];
+ ((Object[])screen.renderer.args)[0] =
+ new Integer(Renderer.REQUESTRENDER);
+ ((Object[])screen.renderer.args)[1] = this;
+ ((Object[])screen.renderer.args)[2] = view;
+ // This extra argument 3 is needed in MasterControl to
+ // test whether offscreen Rendering is used or not
+ ((Object[])screen.renderer.args)[3] = null;
+
+ // call renderer doWork directly since we are already in
+ // the renderer thread
+ screen.renderer.doWork(0);
+ } else {
+ // TODO:
+ // Now we are in trouble, this will cause deadlock if
+ // waitForOffScreenRendering() is invoked
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.RENDER_THREAD;
+ createMessage.type = J3dMessage.RENDER_OFFSCREEN;
+ createMessage.universe = this.view.universe;
+ createMessage.view = this.view;
+ createMessage.args[0] = this;
+ screen.renderer.rendererStructure.addMessage(createMessage);
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+
+ } else if (Thread.currentThread() instanceof BehaviorScheduler) {
+ // If called from behavior scheduler, send a message directly to
+ // the renderer message queue.
+ // Note that we didn't use
+ // currentThread() == view.universe.behaviorScheduler
+ // since the caller may be another universe Behavior
+ // scheduler.
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.RENDER_THREAD;
+ createMessage.type = J3dMessage.RENDER_OFFSCREEN;
+ createMessage.universe = this.view.universe;
+ createMessage.view = this.view;
+ createMessage.args[0] = this;
+ screen.renderer.rendererStructure.addMessage(createMessage);
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+
+ } else {
+ // send a message to renderBin
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.RENDER_OFFSCREEN;
+ createMessage.universe = this.view.universe;
+ createMessage.view = this.view;
+ createMessage.args[0] = this;
+ createMessage.args[1] = offScreenBuffer;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+
+ /**
+ * Waits for this Canvas3D's off-screen rendering to be done.
+ * This method will wait until the <code>postSwap</code> method of this
+ * off-screen Canvas3D has completed. If this Canvas3D has not
+ * been added to an active view or if the renderer is stopped for this
+ * Canvas3D, then this method will return
+ * immediately. This method must not be called from a render
+ * callback method of an off-screen Canvas3D.
+ *
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode, or if this method is called from a render
+ * callback method of an off-screen Canvas3D.
+ *
+ * @see #renderOffScreenBuffer
+ * @see #postSwap
+ *
+ * @since Java 3D 1.2
+ */
+ public void waitForOffScreenRendering() {
+ while (offScreenRendering) {
+ MasterControl.threadYield();
+ }
+ }
+
+
+ /**
+ * Sets the location of this off-screen Canvas3D. The location is
+ * the upper-left corner of the Canvas3D relative to the
+ * upper-left corner of the corresponding off-screen Screen3D.
+ * The function of this method is similar to that of
+ * <code>Component.setLocation</code> for on-screen Canvas3D
+ * objects. The default location is (0,0).
+ *
+ * @param x the <i>x</i> coordinate of the upper-left corner of
+ * the new location.
+ * @param y the <i>y</i> coordinate of the upper-left corner of
+ * the new location.
+ *
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setOffScreenLocation(int x, int y) {
+
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
+
+ synchronized(cvLock) {
+ offScreenCanvasLoc.setLocation(x, y);
+ }
+ }
+
+
+ /**
+ * Sets the location of this off-screen Canvas3D. The location is
+ * the upper-left corner of the Canvas3D relative to the
+ * upper-left corner of the corresponding off-screen Screen3D.
+ * The function of this method is similar to that of
+ * <code>Component.setLocation</code> for on-screen Canvas3D
+ * objects. The default location is (0,0).
+ *
+ * @param p the point defining the upper-left corner of the new
+ * location.
+ *
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setOffScreenLocation(Point p) {
+
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
+
+ synchronized(cvLock) {
+ offScreenCanvasLoc.setLocation(p);
+ }
+ }
+
+
+ /**
+ * Retrieves the location of this off-screen Canvas3D. The
+ * location is the upper-left corner of the Canvas3D relative to
+ * the upper-left corner of the corresponding off-screen Screen3D.
+ * The function of this method is similar to that of
+ * <code>Component.getLocation</code> for on-screen Canvas3D
+ * objects.
+ *
+ * @return a new point representing the upper-left corner of the
+ * location of this off-screen Canvas3D.
+ *
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public Point getOffScreenLocation() {
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
+
+ return (new Point(offScreenCanvasLoc));
+ }
+
+
+ /**
+ * Retrieves the location of this off-screen Canvas3D and stores
+ * it in the specified Point object. The location is the
+ * upper-left corner of the Canvas3D relative to the upper-left
+ * corner of the corresponding off-screen Screen3D. The function
+ * of this method is similar to that of
+ * <code>Component.getLocation</code> for on-screen Canvas3D
+ * objects. This version of <code>getOffScreenLocation</code> is
+ * useful if the caller wants to avoid allocating a new Point
+ * object on the heap.
+ *
+ * @param rv Point object into which the upper-left corner of the
+ * location of this off-screen Canvas3D is copied.
+ * If <code>rv</code> is null, a new Point is allocated.
+ *
+ * @return <code>rv</code>
+ *
+ * @exception IllegalStateException if this Canvas3D is not in
+ * off-screen mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public Point getOffScreenLocation(Point rv) {
+
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
+
+ if (rv == null)
+ return (new Point(offScreenCanvasLoc));
+
+ else {
+ rv.setLocation(offScreenCanvasLoc);
+ return rv;
+ }
+ }
+
+ void endOffScreenRendering() {
+
+
+
+
+ // Evaluate what the stored format is before reading to offscreen buffer
+ if (((ImageComponent2DRetained)offScreenBuffer.retained).isByReference()) {
+ ((ImageComponent2DRetained)offScreenBuffer.retained).geomLock.getLock();
+ ((ImageComponent2DRetained)offScreenBuffer.retained).evaluateExtensions(extensionsSupported);
+ ((ImageComponent2DRetained)offScreenBuffer.retained).geomLock.unLock();
+ }
+
+
+
+ int bytesPerPixel = ((ImageComponent2DRetained)offScreenBuffer.retained).getEffectiveBytesPerPixel();
+ int format = ((ImageComponent2DRetained)offScreenBuffer.retained).getEffectiveFormat();
+
+
+ // allocate read buffer space
+
+ int size =
+ offScreenCanvasSize.width * offScreenCanvasSize.height * bytesPerPixel;
+ if (byteBuffer.length < size)
+ byteBuffer = new byte[size];
+
+
+ // read the image from the offscreen buffer
+
+ readOffScreenBuffer(ctx,
+ format,
+ offScreenCanvasSize.width, offScreenCanvasSize.height);
+
+
+ // copy it into the ImageComponent
+
+ ((ImageComponent2DRetained)offScreenBuffer.retained).retrieveImage(
+ byteBuffer, offScreenCanvasClippedLoc.x, offScreenCanvasClippedLoc.y,
+ offScreenCanvasClippedSize.width, offScreenCanvasClippedSize.height);
+ }
+
+
+ /**
+ * Synchronize and swap buffers on a double buffered canvas for
+ * this Canvas3D object. This method should only be called if the
+ * Java 3D renderer has been stopped. In the normal case, the renderer
+ * automatically swaps the buffer.
+ * This method calls the <code>flush(true)</code> methods of the
+ * associated 2D and 3D graphics contexts, if they have been allocated.
+ *
+ * @exception RestrictedAccessException if the Java 3D renderer is
+ * running.
+ * @exception IllegalStateException if this Canvas3D is in
+ * off-screen mode.
+ *
+ * @see #stopRenderer
+ * @see GraphicsContext3D#flush
+ * @see J3DGraphics2D#flush
+ */
+ public void swap() {
+ if (offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Canvas3D14"));
+
+ if (isRunning)
+ throw new RestrictedAccessException(J3dI18N.getString("Canvas3D0"));
+
+ if (!firstPaintCalled) {
+ return;
+ }
+
+ if (view != null && graphicsContext3D != null) {
+ if ((view.universe != null) &&
+ (Thread.currentThread() == view.universe.behaviorScheduler)) {
+ graphicsContext3D.sendRenderMessage(false, GraphicsContext3D.SWAP, null, null);
+ } else {
+ graphicsContext3D.sendRenderMessage(true, GraphicsContext3D.SWAP, null, null);
+ }
+ graphicsContext3D.runMonitor(J3dThread.WAIT);
+ }
+ }
+
+ void doSwap() {
+ if (firstPaintCalled && useDoubleBuffer) {
+ try {
+ if (validCtx && (ctx != 0) && (view != null)) {
+ synchronized (drawingSurfaceObject) {
+ if (validCtx) {
+ if (!drawingSurfaceObject.renderLock()) {
+ graphicsContext3D.runMonitor(J3dThread.NOTIFY);
+ return;
+ }
+ this.syncRender(ctx, true);
+ int status = swapBuffers(ctx, screen.display, window);
+ if (status != NOCHANGE) {
+ resetImmediateRendering(status);
+ }
+ drawingSurfaceObject.unLock();
+ }
+ }
+ }
+ } catch (NullPointerException ne) {
+ drawingSurfaceObject.unLock();
+ }
+ }
+ // Increment the elapsedFrame for the behavior structure
+ // to trigger any interpolators
+ view.universe.behaviorStructure.incElapsedFrames();
+ // waitup user thread in PureImmediate mode to continue
+ if (reEvaluateCanvasCmd != 0) {
+ int status;
+
+ antialiasingSet = false;
+ if (reEvaluateCanvasCmd == RESIZE) {
+ status = resizeD3DCanvas(ctx);
+ } else {
+ status = toggleFullScreenMode(ctx);
+ }
+ // reset everything
+ if (status != NOCHANGE) {
+ resetImmediateRendering(status);
+ }
+ reEvaluateCanvasCmd = 0;
+ }
+ graphicsContext3D.runMonitor(J3dThread.NOTIFY);
+ }
+
+ /**
+ * Make the context associated with the specified canvas current.
+ */
+ final void makeCtxCurrent() {
+ makeCtxCurrent(ctx, screen.display, window);
+ }
+
+ /**
+ * Make the specified context current.
+ */
+ final void makeCtxCurrent(long ctx) {
+ makeCtxCurrent(ctx, screen.display, window);
+ }
+
+ final void makeCtxCurrent(long ctx, long dpy, int win) {
+ if (ctx != screen.renderer.currentCtx) {
+ if (!drawingSurfaceObject.isLocked()) {
+ drawingSurfaceObject.renderLock();
+ useCtx(ctx, dpy, win);
+ drawingSurfaceObject.unLock();
+ } else {
+ useCtx(ctx, dpy, win);
+ }
+ screen.renderer.currentCtx = ctx;
+ }
+ }
+
+
+ // The native method that sets this ctx to be the current one
+ static native void useCtx(long ctx, long display, int window);
+
+ native void clear(long ctx, float r, float g, float b, int winWidth, int winHeight,
+ ImageComponent2DRetained image, int imageScaleMode, byte[] imageYdown);
+ native void textureclear(long ctx, int maxX, int maxY,
+ float r, float g, float b,
+ int winWidth, int winHeight,
+ int objectId, int scalemode,
+ ImageComponent2DRetained image,
+ boolean update);
+
+
+ // The native method for swapBuffers
+ native int swapBuffers(long ctx, long dpy, int win);
+
+ // The native method for videoResize. -- Support DVR.
+ native void videoResize(long ctx, long dpy, int win, float dvrFactor);
+
+ // The native method for videoResizeCompensation. -- Support DVR.
+ native void videoResizeCompensation( long ctx, boolean enable);
+
+ // The native method for setting the ModelView matrix.
+ native void setModelViewMatrix(long ctx, double[] viewMatrix, double[] modelMatrix);
+
+ // The native method for setting the Projection matrix.
+ native void setProjectionMatrix(long ctx, double[] projMatrix);
+
+ // The native method for setting the Viewport.
+ native void setViewport(long ctx, int x, int y, int width, int height);
+
+ // used for display Lists
+ native void newDisplayList(long ctx, int displayListId);
+ native void endDisplayList(long ctx);
+ native void callDisplayList(long ctx, int id, boolean isNonUniformScale);
+
+ native static void freeDisplayList(long ctx, int id);
+ native static void freeTexture(long ctx, int id);
+
+ native void composite(long ctx, int px, int py,
+ int xmin, int ymin, int xmax, int ymax,
+ int rasWidth, byte[] image,
+ int winWidth, int winHeight);
+
+ native void texturemapping(long ctx,
+ int px, int py,
+ int xmin, int ymin, int xmax, int ymax,
+ int texWidth, int texHeight,
+ int rasWidth,
+ int format, int objectId,
+ byte[] image,
+ int winWidth, int winHeight);
+
+ native boolean initTexturemapping(long ctx, int texWidth,
+ int texHeight, int objectId);
+
+ /**
+ * Sets the position of the manual left eye in image-plate
+ * coordinates. This value determines eye placement when a head
+ * tracker is not in use and the application is directly controlling
+ * the eye position in image-plate coordinates.
+ * In head-tracked mode or when the windowEyePointPolicy is
+ * RELATIVE_TO_FIELD_OF_VIEW or RELATIVE_TO_COEXISTENCE, this value
+ * is ignored. When the
+ * windowEyepointPolicy is RELATIVE_TO_WINDOW only the Z value is
+ * used.
+ * @param position the new manual left eye position
+ */
+ public void setLeftManualEyeInImagePlate(Point3d position) {
+
+ this.leftManualEyeInImagePlate.set(position);
+ synchronized(dirtyMaskLock) {
+ cvDirtyMask |= Canvas3D.EYE_IN_IMAGE_PLATE_DIRTY;
+ }
+ redraw();
+ }
+
+ /**
+ * Sets the position of the manual right eye in image-plate
+ * coordinates. This value determines eye placement when a head
+ * tracker is not in use and the application is directly controlling
+ * the eye position in image-plate coordinates.
+ * In head-tracked mode or when the windowEyePointPolicy is
+ * RELATIVE_TO_FIELD_OF_VIEW or RELATIVE_TO_COEXISTENCE, this value
+ * is ignored. When the
+ * windowEyepointPolicy is RELATIVE_TO_WINDOW only the Z value is
+ * used.
+ * @param position the new manual right eye position
+ */
+ public void setRightManualEyeInImagePlate(Point3d position) {
+
+ this.rightManualEyeInImagePlate.set(position);
+ synchronized(dirtyMaskLock) {
+ cvDirtyMask |= Canvas3D.EYE_IN_IMAGE_PLATE_DIRTY;
+ }
+ redraw();
+ }
+
+ /**
+ * Retrieves the position of the user-specified, manual left eye
+ * in image-plate
+ * coordinates and copies that value into the object provided.
+ * @param position the object that will receive the position
+ */
+ public void getLeftManualEyeInImagePlate(Point3d position) {
+ position.set(this.leftManualEyeInImagePlate);
+ }
+
+ /**
+ * Retrieves the position of the user-specified, manual right eye
+ * in image-plate
+ * coordinates and copies that value into the object provided.
+ * @param position the object that will receive the position
+ */
+ public void getRightManualEyeInImagePlate(Point3d position) {
+ position.set(this.rightManualEyeInImagePlate);
+ }
+
+ /**
+ * Retrieves the actual position of the left eye
+ * in image-plate
+ * coordinates and copies that value into the object provided.
+ * This value is a function of the windowEyepointPolicy, the tracking
+ * enable flag, and the manual left eye position.
+ * @param position the object that will receive the position
+ */
+ public void getLeftEyeInImagePlate(Point3d position) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ position.set(canvasViewCache.getLeftEyeInImagePlate());
+ }
+ }
+ else {
+ position.set(leftManualEyeInImagePlate);
+ }
+ }
+
+ /**
+ * Retrieves the actual position of the right eye
+ * in image-plate
+ * coordinates and copies that value into the object provided.
+ * This value is a function of the windowEyepointPolicy, the tracking
+ * enable flag, and the manual right eye position.
+ * @param position the object that will receive the position
+ */
+ public void getRightEyeInImagePlate(Point3d position) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ position.set(canvasViewCache.getRightEyeInImagePlate());
+ }
+ }
+ else {
+ position.set(rightManualEyeInImagePlate);
+ }
+ }
+
+ /**
+ * Retrieves the actual position of the center eye
+ * in image-plate
+ * coordinates and copies that value into the object provided.
+ * The center eye is the fictional eye half-way between the left and
+ * right eye.
+ * This value is a function of the windowEyepointPolicy, the tracking
+ * enable flag, and the manual right and left eye positions.
+ * @param position the object that will receive the position
+ * @see #setMonoscopicViewPolicy
+ */
+ // TODO: This might not make sense for field-sequential HMD.
+ public void getCenterEyeInImagePlate(Point3d position) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ position.set(canvasViewCache.getCenterEyeInImagePlate());
+ }
+ }
+ else {
+ Point3d cenEye = new Point3d();
+ cenEye.add(leftManualEyeInImagePlate, rightManualEyeInImagePlate);
+ cenEye.scale(0.5);
+ position.set(cenEye);
+ }
+ }
+
+ /**
+ * Retrieves the current ImagePlate coordinates to Virtual World
+ * coordinates transform and places it into the specified object.
+ * @param t the Transform3D object that will receive the
+ * transform
+ */
+ // TODO: Document -- This will return the transform of left plate.
+ public void getImagePlateToVworld(Transform3D t) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ t.set(canvasViewCache.getImagePlateToVworld());
+ }
+ }
+ else {
+ t.setIdentity();
+ }
+ }
+
+ /**
+ * Computes the position of the specified AWT pixel value
+ * in image-plate
+ * coordinates and copies that value into the object provided.
+ * @param x the X coordinate of the pixel relative to the upper-left
+ * hand corner of the window.
+ * @param y the Y coordinate of the pixel relative to the upper-left
+ * hand corner of the window.
+ * @param imagePlatePoint the object that will receive the position in
+ * physical image plate coordinates (relative to the lower-left
+ * corner of the screen).
+ */
+ // TODO: Document -- This transform the pixel location to the left image plate.
+ public void getPixelLocationInImagePlate(int x, int y,
+ Point3d imagePlatePoint) {
+
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ imagePlatePoint.x =
+ canvasViewCache.getWindowXInImagePlate((double)x);
+ imagePlatePoint.y =
+ canvasViewCache.getWindowYInImagePlate((double)y);
+ imagePlatePoint.z = 0.0;
+ }
+ } else {
+ imagePlatePoint.set(0.0, 0.0, 0.0);
+ }
+ }
+
+
+ void getPixelLocationInImagePlate(double x, double y, double z,
+ Point3d imagePlatePoint) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ canvasViewCache.getPixelLocationInImagePlate(
+ x, y, z, imagePlatePoint);
+ }
+ } else {
+ imagePlatePoint.set(0.0, 0.0, 0.0);
+ }
+ }
+
+
+ /**
+ * Computes the position of the specified AWT pixel value
+ * in image-plate
+ * coordinates and copies that value into the object provided.
+ * @param pixelLocation the coordinates of the pixel relative to
+ * the upper-left hand corner of the window.
+ * @param imagePlatePoint the object that will receive the position in
+ * physical image plate coordinates (relative to the lower-left
+ * corner of the screen).
+ *
+ * @since Java 3D 1.2
+ */
+ // TODO: Document -- This transform the pixel location to the left image plate.
+ public void getPixelLocationInImagePlate(Point2d pixelLocation,
+ Point3d imagePlatePoint) {
+
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ imagePlatePoint.x =
+ canvasViewCache.getWindowXInImagePlate(pixelLocation.x);
+ imagePlatePoint.y =
+ canvasViewCache.getWindowYInImagePlate(pixelLocation.y);
+ imagePlatePoint.z = 0.0;
+ }
+ }
+ else {
+ imagePlatePoint.set(0.0, 0.0, 0.0);
+ }
+ }
+
+
+ /**
+ * Projects the specified point from image plate coordinates
+ * into AWT pixel coordinates. The AWT pixel coordinates are
+ * copied into the object provided.
+ * @param imagePlatePoint the position in
+ * physical image plate coordinates (relative to the lower-left
+ * corner of the screen).
+ * @param pixelLocation the object that will receive the coordinates
+ * of the pixel relative to the upper-left hand corner of the window.
+ *
+ * @since Java 3D 1.2
+ */
+ // TODO: Document -- This transform the pixel location from the left image plate.
+ public void getPixelLocationFromImagePlate(Point3d imagePlatePoint,
+ Point2d pixelLocation) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ canvasViewCache.getPixelLocationFromImagePlate(
+ imagePlatePoint, pixelLocation);
+ }
+ }
+ else {
+ pixelLocation.set(0.0, 0.0);
+ }
+ }
+
+ /**
+ * Copies the current Vworld projection transform for each eye
+ * into the specified Transform3D objects. This transform takes
+ * points in virtual world coordinates and projects them into
+ * clipping coordinates, which are in the range [-1,1] in
+ * <i>X</i>, <i>Y</i>, and <i>Z</i> after clipping and perspective
+ * division.
+ * In monoscopic mode, the same projection transform will be
+ * copied into both the right and left eye Transform3D objects.
+ *
+ * @param leftProjection the Transform3D object that will receive
+ * a copy of the current projection transform for the left eye.
+ *
+ * @param rightProjection the Transform3D object that will receive
+ * a copy of the current projection transform for the right eye.
+ *
+ * @since Java 3D 1.3
+ */
+ public void getVworldProjection(Transform3D leftProjection,
+ Transform3D rightProjection) {
+ if (canvasViewCache != null) {
+ ViewPlatformRetained viewPlatformRetained =
+ (ViewPlatformRetained)view.getViewPlatform().retained;
+
+ synchronized(canvasViewCache) {
+ leftProjection.mul(canvasViewCache.getLeftProjection(),
+ canvasViewCache.getLeftVpcToEc());
+ leftProjection.mul(viewPlatformRetained.getVworldToVpc());
+
+ // caluclate right eye if in stereo, otherwise
+ // this is the same as the left eye.
+ if (useStereo) {
+ rightProjection.mul(canvasViewCache.getRightProjection(),
+ canvasViewCache.getRightVpcToEc());
+ rightProjection.mul(viewPlatformRetained.getVworldToVpc());
+ }
+ else {
+ rightProjection.set(leftProjection);
+ }
+ }
+ }
+ else {
+ leftProjection.setIdentity();
+ rightProjection.setIdentity();
+ }
+ }
+
+ /**
+ * Copies the inverse of the current Vworld projection transform
+ * for each eye into the specified Transform3D objects. This
+ * transform takes points in clipping coordinates, which are in
+ * the range [-1,1] in <i>X</i>, <i>Y</i>, and <i>Z</i> after
+ * clipping and perspective division, and transforms them into
+ * virtual world coordinates.
+ * In monoscopic mode, the same inverse projection transform will
+ * be copied into both the right and left eye Transform3D objects.
+ *
+ * @param leftInverseProjection the Transform3D object that will
+ * receive a copy of the current inverse projection transform for
+ * the left eye.
+ * @param rightInverseProjection the Transform3D object that will
+ * receive a copy of the current inverse projection transform for
+ * the right eye.
+ *
+ * @since Java 3D 1.3
+ */
+ public void getInverseVworldProjection(Transform3D leftInverseProjection,
+ Transform3D rightInverseProjection) {
+ if (canvasViewCache != null) {
+ ViewPlatformRetained viewPlatformRetained =
+ (ViewPlatformRetained)view.getViewPlatform().retained;
+
+ synchronized(canvasViewCache) {
+ leftInverseProjection.set(
+ canvasViewCache.getLeftCcToVworld());
+
+ // caluclate right eye if in stereo, otherwise
+ // this is the same as the left eye.
+ if (useStereo) {
+ rightInverseProjection.set(
+ canvasViewCache.getRightCcToVworld());
+ }
+ else {
+ rightInverseProjection.set(leftInverseProjection);
+ }
+ }
+
+ }
+ else {
+ leftInverseProjection.setIdentity();
+ rightInverseProjection.setIdentity();
+ }
+ }
+
+
+ /**
+ * Retrieves the physical width of this canvas window in meters.
+ * @return the physical window width in meters.
+ */
+ public double getPhysicalWidth() {
+ double width = 0.0;
+
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ width = canvasViewCache.getPhysicalWindowWidth();
+ }
+ }
+
+ return width;
+ }
+
+ /**
+ * Retrieves the physical height of this canvas window in meters.
+ * @return the physical window height in meters.
+ */
+ public double getPhysicalHeight() {
+ double height = 0.0;
+
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ height = canvasViewCache.getPhysicalWindowHeight();
+ }
+ }
+
+ return height;
+ }
+
+ /**
+ * Retrieves the current Virtual World coordinates to ImagePlate
+ * coordinates transform and places it into the specified object.
+ * @param t the Transform3D object that will receive the
+ * transform
+ */
+ // TODO: Document -- This will return the transform of left plate.
+ public void getVworldToImagePlate(Transform3D t) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ t.set(canvasViewCache.getVworldToImagePlate());
+ }
+ }
+ else {
+ t.setIdentity();
+ }
+ }
+
+ void getLastVworldToImagePlate(Transform3D t) {
+ if (canvasViewCache != null) {
+ synchronized(canvasViewCache) {
+ t.set(canvasViewCache.getLastVworldToImagePlate());
+ }
+ }
+ else {
+ t.setIdentity();
+ }
+ }
+
+ /**
+ * Sets view that points to this Canvas3D.
+ * @param view view object that points to this Canvas3D
+ */
+ void setView(View view) {
+ pendingView = view;
+
+ // We can't set View directly here in user thread since
+ // other threads may using canvas.view
+ // e.g. In Renderer, we use canvas3d.view.inCallBack
+ // before and after postSwap(), if view change in between
+ // than view.inCallBack may never reset to false.
+ VirtualUniverse.mc.postRequest(MasterControl.SET_VIEW, this);
+ evaluateActive();
+ }
+
+ void computeViewCache() {
+ synchronized(cvLock) {
+ if (view == null) {
+ canvasViewCache = null;
+ } else {
+
+ canvasViewCache = new CanvasViewCache(this,
+ screen.screenViewCache,
+ view.viewCache);
+ synchronized (dirtyMaskLock) {
+ cvDirtyMask = (STEREO_DIRTY | MONOSCOPIC_VIEW_POLICY_DIRTY
+ | EYE_IN_IMAGE_PLATE_DIRTY |
+ MOVED_OR_RESIZED_DIRTY);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets view that points to this Canvas3D.
+ * @return view object that points to this Canvas3D
+ */
+ public View getView() {
+ return pendingView;
+ }
+
+ /**
+ * Returns a status flag indicating whether or not stereo
+ * is available.
+ * This is equivalent to:
+ * <ul>
+ * <code>
+ * ((Boolean)queryProperties().
+ * get("stereoAvailable")).
+ * booleanValue()
+ * </code>
+ * </ul>
+ *
+ * @return a flag indicating whether stereo is available
+ */
+ public boolean getStereoAvailable() {
+ return ((Boolean)queryProperties().get("stereoAvailable")).
+ booleanValue();
+ }
+
+ /**
+ * Turns stereo on or off. Note that this attribute is used
+ * only when stereo is available. Enabling stereo on a Canvas3D
+ * that does not support stereo has no effect.
+ * @param flag enables or disables the display of stereo
+ *
+ * @see #queryProperties
+ */
+ public void setStereoEnable(boolean flag) {
+ stereoEnable = flag;
+ useStereo = stereoEnable && stereoAvailable;
+ synchronized(dirtyMaskLock) {
+ cvDirtyMask |= Canvas3D.STEREO_DIRTY;
+ }
+ redraw();
+ }
+
+ /**
+ * Returns a status flag indicating whether or not stereo
+ * is enabled.
+ * @return a flag indicating whether stereo is enabled
+ */
+ public boolean getStereoEnable() {
+ return this.stereoEnable;
+ }
+
+
+ /**
+ * Specifies how Java 3D generates monoscopic view. If set to
+ * View.LEFT_EYE_VIEW, the view generated corresponds to the view as
+ * seen from the left eye. If set to View.RIGHT_EYE_VIEW, the view
+ * generated corresponds to the view as seen from the right
+ * eye. If set to View.CYCLOPEAN_EYE_VIEW, the view generated
+ * corresponds to the view as seen from the 'center eye', the
+ * fictional eye half-way between the left and right eye. The
+ * default monoscopic view policy is View.CYCLOPEAN_EYE_VIEW.
+ * <p>
+ * NOTE: for backward compatibility with Java 3D 1.1, if this
+ * attribute is set to its default value of
+ * View.CYCLOPEAN_EYE_VIEW, the monoscopic view policy in the
+ * View object will be used. An application should not use both
+ * the deprecated View method and this Canvas3D method at the same
+ * time.
+ * @param policy one of View.LEFT_EYE_VIEW, View.RIGHT_EYE_VIEW, or
+ * View.CYCLOPEAN_EYE_VIEW.
+ *
+ * @exception IllegalStateException if the specified
+ * policy is CYCLOPEAN_EYE_VIEW, the canvas is a stereo canvas,
+ * and the viewPolicy for the associated view is HMD_VIEW
+ *
+ * @since Java 3D 1.2
+ */
+ public void setMonoscopicViewPolicy(int policy) {
+
+
+ if((view !=null) && (view.viewPolicy == View.HMD_VIEW) &&
+ (monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
+ (!useStereo)) {
+ throw new
+ IllegalStateException(J3dI18N.getString("View31"));
+ }
+
+ monoscopicViewPolicy = policy;
+ synchronized(dirtyMaskLock) {
+ cvDirtyMask |= Canvas3D.MONOSCOPIC_VIEW_POLICY_DIRTY;
+ }
+ redraw();
+ }
+
+
+ /**
+ * Returns policy on how Java 3D generates monoscopic view.
+ * @return policy one of View.LEFT_EYE_VIEW, View.RIGHT_EYE_VIEW or
+ * View.CYCLOPEAN_EYE_VIEW.
+ *
+ * @since Java 3D 1.2
+ */
+ public int getMonoscopicViewPolicy() {
+ return this.monoscopicViewPolicy;
+ }
+
+
+ /**
+ * Returns a status flag indicating whether or not double
+ * buffering is available.
+ * This is equivalent to:
+ * <ul>
+ * <code>
+ * ((Boolean)queryProperties().
+ * get("doubleBufferAvailable")).
+ * booleanValue()
+ * </code>
+ * </ul>
+ *
+ * @return a flag indicating whether double buffering is available.
+ */
+ public boolean getDoubleBufferAvailable() {
+ return ((Boolean)queryProperties().get("doubleBufferAvailable")).
+ booleanValue();
+ }
+
+ /**
+ * Turns double buffering on or off. If double buffering
+ * is off, all drawing is to the front buffer and no buffer swap
+ * is done between frames. It should be stressed that running
+ * Java 3D with double buffering disabled is not recommended.
+ * Enabling double buffering on a Canvas3D
+ * that does not support double buffering has no effect.
+ *
+ * @param flag enables or disables double buffering.
+ *
+ * @see #queryProperties
+ */
+ public void setDoubleBufferEnable(boolean flag) {
+ doubleBufferEnable = flag;
+ useDoubleBuffer = doubleBufferEnable && doubleBufferAvailable;
+ if (Thread.currentThread() == screen.renderer) {
+ setRenderMode(ctx, FIELD_ALL, useDoubleBuffer);
+ }
+ redraw();
+ }
+
+ /**
+ * Returns a status flag indicating whether or not double
+ * buffering is enabled.
+ * @return a flag indicating if double buffering is enabled.
+ */
+ public boolean getDoubleBufferEnable() {
+ return doubleBufferEnable;
+ }
+
+ /**
+ * Returns a status flag indicating whether or not scene
+ * antialiasing is available.
+ * This is equivalent to:
+ * <ul>
+ * <code>
+ * ((Boolean)queryProperties().
+ * get("sceneAntialiasingAvailable")).
+ * booleanValue()
+ * </code>
+ * </ul>
+ *
+ * @return a flag indicating whether scene antialiasing is available.
+ */
+ public boolean getSceneAntialiasingAvailable() {
+ return ((Boolean)queryProperties().get("sceneAntialiasingAvailable")).
+ booleanValue();
+ }
+
+
+ /**
+ * Returns a read-only Map object containing key-value pairs that define
+ * various properties for this Canvas3D. All of the keys are
+ * String objects. The values are key-specific, but most will be
+ * Boolean, Integer, Float, Double, or String objects.
+ *
+ * <p>
+ * The currently defined keys are:
+ *
+ * <p>
+ * <ul>
+ * <table BORDER=1 CELLSPACING=1 CELLPADDING=1>
+ * <tr>
+ * <td><b>Key (String)</b></td>
+ * <td><b>Value Type</b></td>
+ * </tr>
+ * <tr>
+ * <td><code>doubleBufferAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>stereoAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>sceneAntialiasingAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>sceneAntialiasingNumPasses</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>texture3DAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureColorTableSize</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureLodRangeAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureLodOffsetAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureWidthMax</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureHeightMax</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureBoundaryWidthMax</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureEnvCombineAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureCombineDot3Available</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureCombineSubtractAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureUnitStateMax</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureCubeMapAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureDetailAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureSharpenAvailable</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureFilter4Available</code></td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr>
+ * <td><code>textureAnisotropicFilterDegreeMax</code></td>
+ * <td>Float</td>
+ * </tr>
+ * <tr>
+ * <td><code>compressedGeometry.majorVersionNumber</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>compressedGeometry.minorVersionNumber</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>compressedGeometry.minorMinorVersionNumber</code></td>
+ * <td>Integer</td>
+ * </tr>
+ * <tr>
+ * <td><code>native.version</code></td>
+ * <td>String</td>
+ * </tr>
+ * </table>
+ * </ul>
+ *
+ * <p>
+ * The descriptions of the values returned for each key are as follows:
+ *
+ * <p>
+ * <ul>
+ * <li>
+ * <code>doubleBufferAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not double buffering
+ * is available for this Canvas3D. This is equivalent to
+ * the getDoubleBufferAvailable method. If this flag is false,
+ * the Canvas3D will be rendered in single buffer mode; requests
+ * to enable double buffering will be ignored.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>stereoAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not stereo
+ * is available for this Canvas3D. This is equivalent to
+ * the getStereoAvailable method. If this flag is false,
+ * the Canvas3D will be rendered in monoscopic mode; requests
+ * to enable stereo will be ignored.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>sceneAntialiasingAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not scene antialiasing
+ * is available for this Canvas3D. This is equivalent to
+ * the getSceneAntialiasingAvailable method. If this flag is false,
+ * requests to enable scene antialiasing will be ignored.
+ * </ul>
+ * </li>
+ *
+ *
+ * <li>
+ * <code>sceneAntialiasingNumPasses</code>
+ * <ul>
+ * An Integer indicating the number of passes scene antialiasing
+ * requires to render a single frame for this Canvas3D.
+ * If this value is zero, scene antialiasing is not supported.
+ * If this value is one, multisampling antialiasing is used.
+ * Otherwise, the number indicates the number of rendering passes
+ * needed.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>texture3DAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not 3D Texture mapping
+ * is available for this Canvas3D. If this flag is false,
+ * 3D texture mapping is either not supported by the underlying
+ * rendering layer or is otherwise unavailable for this
+ * particular Canvas3D. All use of 3D texture mapping will be
+ * ignored in this case.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureColorTableSize</code>
+ * <ul>
+ * An Integer indicating the maximum size of the texture color
+ * table for this Canvas3D. If the size is 0, the texture
+ * color table is either not supported by the underlying rendering
+ * layer or is otherwise unavailable for this particular
+ * Canvas3D. An attempt to use a texture color table larger than
+ * textureColorTableSize will be ignored; no color lookup will be
+ * performed.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureLodRangeAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not setting only a subset of mipmap
+ * levels and setting a range of texture LOD are available for this
+ * Canvas3D.
+ * If it indicates false, setting a subset of mipmap levels and
+ * setting a texture LOD range are not supported by the underlying
+ * rendering layer, and an attempt to set base level, or maximum level,
+ * or minimum LOD, or maximum LOD will be ignored. In this case,
+ * images for all mipmap levels must be defined for the texture to be
+ * valid.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureLodOffsetAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not setting texture LOD offset is
+ * available for this Canvas3D. If it indicates false, setting
+ * texture LOD offset is not supported by the underlying rendering
+ * layer, and an attempt to set the texture LOD offset will be ignored.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureWidthMax</code>
+ * <ul>
+ * An Integer indicating the maximum texture width supported by
+ * this Canvas3D. If the width of a texture exceeds the maximum texture
+ * width for a Canvas3D, then the texture will be effectively disabled
+ * for that Canvas3D.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureHeightMax</code>
+ * <ul>
+ * An Integer indicating the maximum texture height supported by
+ * this Canvas3D. If the height of a texture exceeds the maximum texture
+ * height for a Canvas3D, then the texture will be effectively disabled
+ * for that Canvas3D.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureBoundaryWidthMax</code>
+ * <ul>
+ * An Integer indicating the maximum texture boundary width
+ * supported by the underlying rendering layer for this Canvas3D. If
+ * the maximum supported texture boundary width is 0, then texture
+ * boundary is not supported by the underlying rendering layer.
+ * An attempt to specify a texture boundary width > the
+ * textureBoundaryWidthMax will effectively disable the texture.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureEnvCombineAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not texture environment combine
+ * operation is supported for this Canvas3D. If it indicates false,
+ * then texture environment combine is not supported by the
+ * underlying rendering layer, and an attempt to specify COMBINE
+ * as the texture mode will be ignored. The texture mode in effect
+ * will be REPLACE.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureCombineDot3Available</code>
+ * <ul>
+ * A Boolean indicating whether or not texture combine mode
+ * COMBINE_DOT3 is
+ * supported for this Canvas3D. If it indicates false, then
+ * texture combine mode COMBINE_DOT3 is not supported by
+ * the underlying rendering layer, and an attempt to specify
+ * COMBINE_DOT3 as the texture combine mode will be ignored.
+ * The texture combine mode in effect will be COMBINE_REPLACE.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureCombineSubtractAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not texture combine mode
+ * COMBINE_SUBTRACT is
+ * supported for this Canvas3D. If it indicates false, then
+ * texture combine mode COMBINE_SUBTRACT is not supported by
+ * the underlying rendering layer, and an attempt to specify
+ * COMBINE_SUBTRACT as the texture combine mode will be ignored.
+ * The texture combine mode in effect will be COMBINE_REPLACE.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureUnitStateMax</code>
+ * <ul>
+ * An Integer indicating the maximum number of texture unit states
+ * supported by the underlying rendering layer. Java3D allows an
+ * application to specify number of texture unit states more than
+ * what the underlying rendering layer supports; in this case, Java3D
+ * will use multi-pass to support the specified number of texture
+ * unit states.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureCubeMapAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not texture cube map is supported
+ * for this Canvas3D. If it indicates false, then texture cube map
+ * is not supported by the underlying rendering layer, and an attempt
+ * to specify NORMAL_MAP or REFLECTION_MAP as the texture generation
+ * mode will be ignored. The texture generation mode in effect will
+ * be SPHERE_MAP.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureDetailAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not detail texture is supported
+ * for this Canvas3D. If it indicates false, then detail texture is
+ * not supported by the underlying rendering layer, and an attempt
+ * to specify LINEAR_DETAIL, LINEAR_DETAIL_ALPHA or
+ * LINEAR_DETAIL_RGB as the texture magnification filter mode will
+ * be ignored. The texture magnification filter mode in effect will
+ * be BASE_LEVEL_LINEAR.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureSharpenAvailable</code>
+ * <ul>
+ * A Boolean indicating whether or not sharpen texture is supported
+ * for this Canvas3D. If it indicates false, then sharpen texture
+ * is not supported by the underlying rendering layer, and an attempt
+ * to specify LINEAR_SHARPEN, LINEAR_SHARPEN_ALPHA or
+ * LINEAR_SHARPEN_RGB as the texture magnification filter mode
+ * will be ignored. The texture magnification filter mode in effect
+ * will be BASE_LEVEL_LINEAR.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureFilter4Available</code>
+ * <ul>
+ * A Boolean indicating whether or not filter4 is supported for this
+ * Canvas3D. If it indicates flase, then filter4 is not supported
+ * by the underlying rendering layer, and an attempt to specify
+ * FILTER_4 as the texture minification filter mode or texture
+ * magnification filter mode will be ignored. The texture filter mode
+ * in effect will be BASE_LEVEL_LINEAR.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>textureAnisotropicFilterDegreeMax</code>
+ * <ul>
+ * A Float indicating the maximum degree of anisotropic filter
+ * available for this Canvas3D. If it indicates 1.0, setting
+ * anisotropic filter is not supported by the underlying rendering
+ * layer, and an attempt to set anisotropic filter degree will be ignored.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>compressedGeometry.majorVersionNumber</code><br>
+ * <code>compressedGeometry.minorVersionNumber</code><br>
+ * <code>compressedGeometry.minorMinorVersionNumber</code>
+ * <ul>
+ * Integers indicating the major, minor, and minor-minor
+ * version numbers, respectively, of the version of compressed
+ * geometry supported by this version of Java 3D.
+ * </ul>
+ * </li>
+ *
+ * <li>
+ * <code>native.version</code>
+ * <ul>
+ * A String indicating the version number of the native graphics
+ * library. The format of this string is defined by the native
+ * library.
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @return the properties of this Canavs3D
+ *
+ * @since Java 3D 1.2
+ */
+ public final Map queryProperties() {
+ if (queryProps == null) {
+ boolean createDummyCtx = false;
+
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ if (ctx == 0) {
+ createDummyCtx = true;
+ }
+ }
+
+ if (createDummyCtx) {
+ GraphicsConfigTemplate3D.setQueryProps(this);
+
+ }
+ //create query Properties
+ createQueryProps();
+ }
+ return queryProps;
+ }
+
+ void createQueryContext() {
+ // create a dummy context to query for support of certain
+ // extensions, the context will destroy immediately
+ // inside the native code after setting the various
+ // fields in this object
+ createQueryContext(screen.display, window, vid,
+ offScreen, 10, 10);
+ }
+
+ /**
+ * Creates the query properties for this Canvas.
+ */
+ private void createQueryProps() {
+ // Create lists of keys and values
+ ArrayList keys = new ArrayList();
+ ArrayList values = new ArrayList();
+ int pass = 0;
+
+ // properties not associated with graphics context
+ keys.add("doubleBufferAvailable");
+ values.add(new Boolean(doubleBufferAvailable));
+
+ keys.add("stereoAvailable");
+ values.add(new Boolean(stereoAvailable));
+
+ keys.add("sceneAntialiasingAvailable");
+ values.add(new Boolean(sceneAntialiasingAvailable));
+
+ keys.add("sceneAntialiasingNumPasses");
+
+ if (sceneAntialiasingAvailable) {
+ pass = (sceneAntialiasingMultiSamplesAvailable ?
+ 1: Renderer.NUM_ACCUMULATION_SAMPLES);
+ }
+ values.add(new Integer(pass));
+
+ keys.add("compressedGeometry.majorVersionNumber");
+ values.add(new Integer(GeometryDecompressor.majorVersionNumber));
+ keys.add("compressedGeometry.minorVersionNumber");
+ values.add(new Integer(GeometryDecompressor.minorVersionNumber));
+ keys.add("compressedGeometry.minorMinorVersionNumber");
+ values.add(new Integer(GeometryDecompressor.minorMinorVersionNumber));
+
+ // Properties associated with graphics context
+ keys.add("texture3DAvailable");
+ values.add(new Boolean((textureExtendedFeatures & TEXTURE_3D) != 0));
+
+ keys.add("textureColorTableSize");
+ values.add(new Integer(textureColorTableSize));
+
+ keys.add("textureEnvCombineAvailable");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_COMBINE) != 0));
+
+ keys.add("textureCombineDot3Available");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_COMBINE_DOT3) != 0));
+
+ keys.add("textureCombineSubtractAvailable");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_COMBINE_SUBTRACT) != 0));
+
+ keys.add("textureCubeMapAvailable");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_CUBE_MAP) != 0));
+
+ keys.add("textureSharpenAvailable");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_SHARPEN) != 0));
+
+ keys.add("textureDetailAvailable");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_DETAIL) != 0));
+
+ keys.add("textureFilter4Available");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_FILTER4) != 0));
+
+ keys.add("textureAnisotropicFilterDegreeMax");
+ values.add(new Float(anisotropicDegreeMax));
+
+ keys.add("textureWidthMax");
+ values.add(new Integer(textureWidthMax));
+
+ keys.add("textureHeightMax");
+ values.add(new Integer(textureHeightMax));
+
+ keys.add("textureBoundaryWidthMax");
+ values.add(new Integer(textureBoundaryWidthMax));
+
+ keys.add("textureLodRangeAvailable");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_LOD_RANGE) != 0));
+
+ keys.add("textureLodOffsetAvailable");
+ values.add(new Boolean(
+ (textureExtendedFeatures & TEXTURE_LOD_OFFSET) != 0));
+
+ keys.add("textureUnitStateMax");
+ values.add(new Integer(numTexUnitSupported));
+
+ keys.add("native.version");
+ if(nativeGraphicsVersion == null)
+ nativeGraphicsVersion = "";
+ values.add(nativeGraphicsVersion);
+
+ // Now Create read-only properties object
+ queryProps =
+ new J3dQueryProps((String[]) keys.toArray(new String[0]),
+ values.toArray());
+ }
+
+
+ // Set internal render mode to one of FIELD_ALL, FIELD_LEFT or
+ // FIELD_RIGHT. Note that it is up to the caller to ensure that
+ // stereo is available before setting the mode to FIELD_LEFT or
+ // FIELD_RIGHT. The boolean isTRUE for double buffered mode, FALSE
+ // foe single buffering.
+ native void setRenderMode(long ctx, int mode, boolean doubleBuffer);
+
+ // Set glDepthMask.
+ native void setDepthBufferWriteEnable(long ctx, boolean mode);
+
+ /**
+ * Update the view cache associated with this canvas.
+ */
+ void updateViewCache(boolean flag, CanvasViewCache cvc,
+ BoundingBox frustumBBox, boolean doInfinite) {
+
+ synchronized(cvLock) {
+ if (firstPaintCalled && (canvasViewCache != null)) {
+ canvasViewCache.snapshot();
+ canvasViewCache.computeDerivedData(flag, cvc, frustumBBox,
+ doInfinite);
+ }
+ }
+ }
+
+ /**
+ * Set depthBufferWriteEnableOverride flag
+ */
+ void setDepthBufferWriteEnableOverride(boolean flag) {
+ depthBufferWriteEnableOverride = flag;
+ }
+
+ /**
+ * Set depthBufferEnableOverride flag
+ */
+ void setDepthBufferEnableOverride(boolean flag) {
+ depthBufferEnableOverride = flag;
+ }
+
+ // Static initializer for Canvas3D class
+ static {
+ VirtualUniverse.loadLibraries();
+ }
+
+
+ void resetTexture(long ctx, int texUnitIndex) {
+ // D3D also need to reset texture attributes
+ this.resetTextureNative(ctx, texUnitIndex);
+
+ if (texUnitIndex < 0) {
+ texUnitIndex = 0;
+ }
+ texUnitState[texUnitIndex].mirror = null;
+ texUnitState[texUnitIndex].texture = null;
+
+ if (VirtualUniverse.mc.isD3D()) {
+ texUnitState[texUnitIndex].texAttrs = null;
+ texUnitState[texUnitIndex].texGen = null;
+ }
+ }
+
+
+ // use by D3D only
+ void resetTextureBin() {
+ Object obj;
+ TextureRetained tex;
+ DetailTextureImage detailTex;
+
+ // We don't use rdr.objectId for background texture in D3D
+ // so there is no need to handle rdr.objectId
+ if ((graphics2D != null) &&
+ (graphics2D.objectId != -1)) {
+ VirtualUniverse.mc.freeTexture2DId(graphics2D.objectId);
+ // let J3DGraphics2DImpl to initialize texture again
+ graphics2D.objectId = -1;
+ }
+
+ for (int id = textureIDResourceTable.size()-1; id > 0; id--) {
+ obj = textureIDResourceTable.get(id);
+ if (obj != null) {
+ if (obj instanceof TextureRetained) {
+ tex = (TextureRetained) obj;
+ tex.resourceCreationMask &= ~canvasBit;
+ } else {
+ detailTex = (DetailTextureImage) obj;
+ for (int i=0; i < detailTex.resourceCreationMask.length; i++) {
+ detailTex.resourceCreationMask[i] &= ~canvasBit;
+ }
+ }
+ }
+ }
+ }
+
+
+ void d3dResize() {
+ int status = resizeD3DCanvas(ctx);
+
+ antialiasingSet = false;
+
+ // We need to reevaluate everything since d3d may create
+ // a new ctx
+ if (status != NOCHANGE) {
+ resetRendering(status);
+ }
+ }
+
+ void d3dToggle() {
+ int status = toggleFullScreenMode(ctx);
+
+ antialiasingSet = false;
+ if (status != NOCHANGE) {
+ resetRendering(status);
+ }
+ }
+
+ // use by D3D only
+ void notifyD3DPeer(int cmd) {
+ if (active) {
+ if (isRunning) {
+ if ((view != null) &&
+ (view.active) &&
+ // it is possible that view is set active by MC
+ // but renderer not yet set
+ (screen.renderer != null)) {
+ VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, this);
+
+ while (isRunningStatus) {
+ MasterControl.threadYield();
+ }
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ if (cmd == RESIZE) {
+ renderMessage.type = J3dMessage.RESIZE_CANVAS;
+ } else {
+ renderMessage.type = J3dMessage.TOGGLE_CANVAS;
+ }
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = this;
+
+ screen.renderer.rendererStructure.addMessage(renderMessage);
+ VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, this);
+ VirtualUniverse.mc.sendRunMessage(view,
+ J3dThread.RENDER_THREAD);
+ }
+ } else {
+ // may be in immediate mode
+ reEvaluateCanvasCmd = cmd;
+ }
+ }
+ }
+
+ // reset all attributes so that everything e.g. display list,
+ // texture will recreate again in the next frame
+ void resetRendering(int status) {
+
+ if (status == RECREATEDDRAW) {
+ // D3D use MANAGE_POOL when createTexture, so there
+ // is no need to download texture again in case of RESETSURFACE
+ resetTextureBin();
+ screen.renderer.needToResendTextureDown = true;
+ }
+
+ reset();
+
+ cvDirtyMask |= VIEW_INFO_DIRTY;
+ needToRebuildDisplayList = true;
+
+ ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp();
+ }
+
+
+ void reset() {
+ int i;
+
+ byteBuffer = new byte[1];
+ currentAppear = new AppearanceRetained();
+ currentMaterial = new MaterialRetained();
+ viewFrustum = new CachedFrustum();
+ canvasDirty = 0xffff;
+ lightBin = null;
+ environmentSet = null;
+ attributeBin = null;
+ textureBin = null;
+ renderMolecule = null;
+ polygonAttributes = null;
+ lineAttributes = null;
+ pointAttributes = null;
+ material = null;
+ enableLighting = false;
+ transparency = null;
+ coloringAttributes = null;
+ texture = null;
+ texAttrs = null;
+ if (texUnitState != null) {
+ TextureUnitStateRetained tus;
+ for (i=0; i < texUnitState.length; i++) {
+ tus = texUnitState[i];
+ if (tus != null) {
+ tus.texAttrs = null;
+ tus.texGen = null;
+ }
+ }
+ }
+ texCoordGeneration = null;
+ renderingAttrs = null;
+ appearance = null;
+ appHandle = null;
+ dirtyRenderMoleculeList.clear();
+ displayListResourceFreeList.clear();
+
+ dirtyDlistPerRinfoList.clear();
+ textureIdResourceFreeList.clear();
+
+ lightChanged = true;
+ modelMatrix = null;
+ modelClip = null;
+ fog = null;
+ texLinearMode = false;
+ sceneAmbient = new Color3f();
+
+
+ for (i=0; i< frameCount.length;i++) {
+ frameCount[i] = -1;
+ }
+
+ for (i=0; i < lights.length; i++) {
+ lights[i] = null;
+ }
+
+ if (currentLights != null) {
+ for (i=0; i < currentLights.length; i++) {
+ currentLights[i] = null;
+ }
+ }
+
+ enableMask = -1;
+ stateUpdateMask = 0;
+ depthBufferWriteEnableOverride = false;
+ depthBufferEnableOverride = false;
+ depthBufferWriteEnable = true;
+ vfPlanesValid = false;
+ lightChanged = false;
+
+ for (i=0; i < curStateToUpdate.length; i++) {
+ curStateToUpdate[i] = null;
+ }
+
+ }
+
+
+ void resetImmediateRendering(int status) {
+ canvasDirty = 0xffff;
+ ra = null;
+
+ setSceneAmbient(ctx, 0.0f, 0.0f, 0.0f);
+ disableFog(ctx);
+ resetRenderingAttributes(ctx, false, false);
+
+ resetTexture(ctx, -1);
+ resetTexCoordGeneration(ctx);
+ resetTextureAttributes(ctx);
+ texUnitState[0].texAttrs = null;
+ texUnitState[0].texGen = null;
+
+ resetPolygonAttributes(ctx);
+ resetLineAttributes(ctx);
+ resetPointAttributes(ctx);
+ resetTransparency(ctx,
+ RenderMolecule.SURFACE,
+ PolygonAttributes.POLYGON_FILL,
+ false, false);
+ resetColoringAttributes(ctx,
+ 1.0f, 1.0f,
+ 1.0f, 1.0f, false);
+ updateMaterial(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
+ resetRendering(NOCHANGE);
+ makeCtxCurrent();
+ cvDirtyMask |= VIEW_INFO_DIRTY;
+ needToRebuildDisplayList = true;
+
+ ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp();
+ if (status == RECREATEDDRAW) {
+ screen.renderer.needToResendTextureDown = true;
+ }
+ }
+
+
+ // overide Canvas.getSize()
+ public Dimension getSize() {
+ if (!fullScreenMode) {
+ return super.getSize();
+ } else {
+ return new Dimension(fullscreenWidth, fullscreenHeight);
+ }
+ }
+
+ public Dimension getSize(Dimension rv) {
+ if (!fullScreenMode) {
+ return super.getSize(rv);
+ } else {
+ if (rv == null) {
+ return new Dimension(fullscreenWidth, fullscreenHeight);
+ } else {
+ rv.setSize(fullscreenWidth, fullscreenHeight);
+ return rv;
+ }
+ }
+ }
+
+ public Point getLocationOnScreen() {
+ if (!fullScreenMode) {
+ try {
+ return super.getLocationOnScreen();
+ } catch (IllegalComponentStateException e) {}
+ }
+ return new Point();
+ }
+
+ public int getX() {
+ if (!fullScreenMode) {
+ return super.getX();
+ } else {
+ return 0;
+ }
+ }
+
+
+ public int getY() {
+ if (!fullScreenMode) {
+ return super.getY();
+ } else {
+ return 0;
+ }
+ }
+
+ public int getWidth() {
+ if (!fullScreenMode) {
+ return super.getWidth();
+ } else {
+ return screen.screenSize.width;
+ }
+ }
+
+ public int getHeight() {
+ if (!fullScreenMode) {
+ return super.getHeight();
+ } else {
+ return screen.screenSize.height;
+ }
+ }
+
+ public Point getLocation(Point rv) {
+ if (!fullScreenMode) {
+ return super.getLocation(rv);
+ } else {
+ if (rv != null) {
+ rv.setLocation(0, 0);
+ return rv;
+ } else {
+ return new Point();
+ }
+ }
+ }
+
+ public Point getLocation() {
+ if (!fullScreenMode) {
+ return super.getLocation();
+ } else {
+ return new Point();
+ }
+ }
+
+ public Rectangle getBounds() {
+ if (!fullScreenMode) {
+ return super.getBounds();
+ } else {
+ return new Rectangle(0, 0,
+ screen.screenSize.width,
+ screen.screenSize.height);
+ }
+ }
+
+ public Rectangle getBounds(Rectangle rv) {
+ if (!fullScreenMode) {
+ return super.getBounds(rv);
+ } else {
+ if (rv != null) {
+ rv.setBounds(0, 0,
+ screen.screenSize.width,
+ screen.screenSize.height);
+ return rv;
+ } else {
+ return new Rectangle(0, 0,
+ screen.screenSize.width,
+ screen.screenSize.height);
+ }
+ }
+ }
+
+ void setModelViewMatrix(long ctx, double[] viewMatrix, Transform3D mTrans) {
+ setModelViewMatrix(ctx, viewMatrix, mTrans.mat);
+ if (!useStereo) {
+ this.modelMatrix = mTrans;
+ } else {
+ if (rightStereoPass) {
+ // Only set cache in right stereo pass, otherwise
+ // if the left stero pass set the cache value,
+ // setModelViewMatrix() in right stereo pass will not
+ // perform in RenderMolecules.
+ this.modelMatrix = mTrans;
+ }
+ }
+ }
+
+ void setDepthBufferWriteEnable(boolean mode) {
+ depthBufferWriteEnable = mode;
+ setDepthBufferWriteEnable(ctx, mode);
+ }
+
+ void setTexUnitStateMap(int texUnitStateIndex, int texUnitIndex) {
+ texUnitStateMap[texUnitIndex] = texUnitStateIndex;
+ }
+
+ void setNumActiveTexUnit(int n) {
+ numActiveTexUnit = n;
+ }
+
+ int getNumActiveTexUnit() {
+ return numActiveTexUnit;
+ }
+
+ void setLastActiveTexUnit(int n) {
+ lastActiveTexUnit = n;
+ }
+
+ int getLastActiveTexUnit() {
+ return lastActiveTexUnit;
+ }
+
+ // update the underlying layer of the current texture unit state map
+ void updateTexUnitStateMap() {
+ updateTexUnitStateMap(ctx, numActiveTexUnit, texUnitStateMap);
+ }
+
+ boolean supportGlobalAlpha() {
+ return ((extensionsSupported & SUN_GLOBAL_ALPHA) != 0);
+ }
+
+ boolean supportVideoResize() {
+ return ((extensionsSupported & SUN_VIDEO_RESIZE) != 0);
+ }
+
+ /** enable separate specular color if the functionality
+ * is availabe for this canvas and it is not overriden by the
+ * property j3d.disableSeparateSpecular.
+ */
+ void enableSeparateSpecularColor() {
+ if (((extensionsSupported & EXT_SEPARATE_SPECULAR_COLOR) != 0) &&
+ !VirtualUniverse.mc.disableSeparateSpecularColor) {
+ updateSeparateSpecularColorEnable(ctx, true);
+ }
+ }
+
+ final void beginScene() {
+ beginScene(ctx);
+ }
+
+ final void endScene() {
+ endScene(ctx);
+ }
+
+ void removeCtx(boolean offscreen) {
+ if ((screen != null) &&
+ (screen.renderer != null) &&
+ (ctx != 0)) {
+ VirtualUniverse.mc.postRequest(MasterControl.FREE_CONTEXT,
+ new Object[]{this,
+ new Long(screen.display),
+ new Integer(window),
+ new Long(ctx)});
+ if (!offscreen) {
+ while (ctxTimeStamp != 0) {
+ MasterControl.threadYield();
+ }
+ }
+ ctx = 0;
+ }
+ }
+
+ /**
+ * Serialization of Canvas3D objects is not supported.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ private void writeObject(java.io.ObjectOutputStream out)
+ throws java.io.IOException {
+
+ throw new UnsupportedOperationException(J3dI18N.getString("Canvas3D20"));
+ }
+
+ /**
+ * Serialization of Canvas3D objects is not supported.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ private void readObject(java.io.ObjectInputStream in)
+ throws java.io.IOException, ClassNotFoundException {
+
+ throw new UnsupportedOperationException(J3dI18N.getString("Canvas3D20"));
+ }
+
+
+ // mark that the current bin specified by the bit is already updated
+ void setStateIsUpdated(int bit) {
+ stateUpdateMask &= ~(1 << bit);
+ }
+
+ // mark that the current bin specified by the bit needs to be updated
+ void setStateToUpdate(int bit) {
+ stateUpdateMask |= 1 << bit;
+ }
+
+ // mark that the bin specified by the bit needs to be updated
+ void setStateToUpdate(int bit, Object bin) {
+ stateUpdateMask |= 1 << bit;
+ curStateToUpdate[bit] = bin;
+ }
+
+ // update LightBin, EnvironmentSet, & AttributeBin if neccessary
+ // according to the stateUpdateMask
+
+ static int ENV_STATE_MASK = (1 << LIGHTBIN_BIT) |
+ (1 << ENVIRONMENTSET_BIT) |
+ (1 << ATTRIBUTEBIN_BIT);
+
+ void updateEnvState() {
+
+ if ((stateUpdateMask & ENV_STATE_MASK) == 0)
+ return;
+
+ if ((stateUpdateMask & (1 << LIGHTBIN_BIT)) != 0) {
+ ((LightBin)curStateToUpdate[LIGHTBIN_BIT]).updateAttributes(this);
+ }
+
+ if ((stateUpdateMask & (1 << ENVIRONMENTSET_BIT)) != 0) {
+ ((EnvironmentSet)
+ curStateToUpdate[ENVIRONMENTSET_BIT]).updateAttributes(this);
+ }
+
+ if ((stateUpdateMask & (1 << ATTRIBUTEBIN_BIT)) != 0) {
+ ((AttributeBin)
+ curStateToUpdate[ATTRIBUTEBIN_BIT]).updateAttributes(this);
+ }
+
+ // reset the state update mask for those environment state bits
+ stateUpdateMask &= ~ENV_STATE_MASK;
+ }
+
+ /**
+ * update state if neccessary according to the stateUpdatedMask
+ */
+ void updateState(int pass, int dirtyBits) {
+
+
+ if (stateUpdateMask == 0)
+ return;
+
+ updateEnvState();
+
+ if ((stateUpdateMask & (1 << TEXTUREBIN_BIT)) != 0) {
+ ((TextureBin)
+ curStateToUpdate[TEXTUREBIN_BIT]).updateAttributes(this, pass);
+ }
+
+ if ((stateUpdateMask & (1 << RENDERMOLECULE_BIT)) != 0) {
+ ((RenderMolecule)
+ curStateToUpdate[RENDERMOLECULE_BIT]).updateAttributes(this,
+ dirtyBits);
+
+ }
+
+ if ((stateUpdateMask & (1 << TRANSPARENCY_BIT)) != 0) {
+ ((RenderMolecule)curStateToUpdate[RENDERMOLECULE_BIT]).updateTransparencyAttributes(this);
+ stateUpdateMask &= ~(1 << TRANSPARENCY_BIT);
+ }
+
+ // reset state update mask
+ stateUpdateMask = 0;
+ }
+
+ /**
+ * obj is either TextureRetained or DetailTextureImage
+ * if obj is DetailTextureImage then we just clear
+ * the resourceCreationMask of all the formats
+ * no matter it is create or not since we don't
+ * remember the format information for simplicity.
+ * We don't need to check duplicate value of id in the
+ * table since this procedure is invoke only when id
+ * of texture is -1 one time only.
+ * This is always call from Renderer thread.
+ */
+ void addTextureResource(int id, Object obj) {
+ if (id <= 0) {
+ return;
+ }
+
+ if (useSharedCtx) {
+ screen.renderer.addTextureResource(id, obj);
+ } else {
+ // This will replace the previous key if exists
+ if (textureIDResourceTable.size() <= id) {
+ for (int i=textureIDResourceTable.size();
+ i < id; i++) {
+ textureIDResourceTable.add(null);
+ }
+ textureIDResourceTable.add(obj);
+ } else {
+ textureIDResourceTable.set(id, obj);
+ }
+
+ }
+ }
+
+ // handle free resource in the FreeList
+ void freeResourcesInFreeList(long ctx) {
+ Iterator it;
+ ArrayList list;
+ int i, val;
+ GeometryArrayRetained geo;
+
+ // free resource for those canvases that
+ // don't use shared ctx
+ if (displayListResourceFreeList.size() > 0) {
+ for (it = displayListResourceFreeList.iterator(); it.hasNext();) {
+ val = ((Integer) it.next()).intValue();
+ if (val <= 0) {
+ continue;
+ }
+ freeDisplayList(ctx, val);
+ }
+ displayListResourceFreeList.clear();
+ }
+
+ if (textureIdResourceFreeList.size() > 0) {
+ for (it = textureIdResourceFreeList.iterator(); it.hasNext();) {
+ val = ((Integer) it.next()).intValue();
+ if (val <= 0) {
+ continue;
+ }
+ if (val >= textureIDResourceTable.size()) {
+ System.out.println("Error in freeResourcesInFreeList : ResourceIDTableSize = " +
+ textureIDResourceTable.size() +
+ " val = " + val);
+ } else {
+ textureIDResourceTable.set(val, null);
+ }
+ freeTexture(ctx, val);
+ }
+ textureIdResourceFreeList.clear();
+ }
+ }
+
+ void freeContextResources(Renderer rdr, boolean freeBackground,
+ long ctx) {
+
+
+ Object obj;
+ TextureRetained tex;
+ DetailTextureImage detailTex;
+
+ if (rdr == null) {
+ return;
+ }
+
+ if (freeBackground) {
+ // Free Background Texture
+ // Note that we use non-shared ctx to create
+ // it so there is no need to do so in
+ // Renderer.freeContextResources()
+ if (rdr.objectId > 0) {
+ Canvas3D.freeTexture(ctx, rdr.objectId);
+ VirtualUniverse.mc.freeTexture2DId(rdr.objectId);
+ rdr.objectId = -1;
+
+ }
+ // Free Graphics2D Texture
+ if ((graphics2D != null) &&
+ (graphics2D.objectId != -1)) {
+ Canvas3D.freeTexture(ctx, graphics2D.objectId);
+ VirtualUniverse.mc.freeTexture2DId(graphics2D.objectId);
+ graphics2D.objectId = -1;
+ }
+ }
+
+
+ for (int id = textureIDResourceTable.size()-1; id > 0; id--) {
+ obj = textureIDResourceTable.get(id);
+ if (obj == null) {
+ continue;
+ }
+ freeTexture(ctx, id);
+ if (obj instanceof TextureRetained) {
+ tex = (TextureRetained) obj;
+ synchronized (tex.resourceLock) {
+ tex.resourceCreationMask &= ~canvasBit;
+ if (tex.resourceCreationMask == 0) {
+
+ tex.freeTextureId(id);
+ }
+ }
+ } else if (obj instanceof DetailTextureImage) {
+ detailTex = ((DetailTextureImage) obj);
+ detailTex.freeDetailTextureId(id, canvasBit);
+ }
+ }
+ textureIDResourceTable.clear();
+
+ freeAllDisplayListResources();
+ }
+
+ void freeAllDisplayListResources() {
+ if ((view != null) && (view.renderBin != null)) {
+ view.renderBin.freeAllDisplayListResources(this);
+ if (useSharedCtx) {
+ // We need to rebuild all other Canvas3D resource
+ // shared by this Canvas3D. Since we didn't
+ // remember resource in Renderer but RenderBin only.
+ if ((screen != null) && (screen.renderer != null)) {
+ screen.renderer.needToRebuildDisplayList = true;
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/CanvasViewCache.java b/src/classes/share/javax/media/j3d/CanvasViewCache.java
new file mode 100644
index 0000000..43469bc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CanvasViewCache.java
@@ -0,0 +1,2007 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.Point;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.IllegalComponentStateException;
+import javax.vecmath.*;
+
+/**
+ * The CanvasViewCache class is used to cache all data, both API data
+ * and derived data, that is dependent on the Canvas3D or Screen3D.
+ * The final view and projection matrices are stored here.
+ */
+
+class CanvasViewCache extends Object {
+ // Used for debugging only
+ private static Object debugLock = new Object();
+
+ // The canvas associated with this canvas view cache
+ private Canvas3D canvas;
+
+ // Mask that indicates this CanvasViewCache view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int cvcDirtyMask = 0;
+
+ // The screen view cache associated with this canvas view cache
+ private ScreenViewCache screenViewCache;
+
+ // The view cache associated with this canvas view cache
+ private ViewCache viewCache;
+
+ // *************
+ // API/INPUT DATA
+ // *************
+
+ // The position and size of the canvas (in pixels)
+ private int awtCanvasX;
+ private int awtCanvasY;
+ private int awtCanvasWidth;
+ private int awtCanvasHeight;
+
+ // The current RenderBin used for rendering during the frame
+ // associated with this snapshot.
+ private RenderBin renderBin;
+
+ // Flag indicating whether or not stereo will be used. Computed by
+ // Canvas3D as: useStereo = stereoEnable && stereoAvailable
+ private boolean useStereo;
+
+ // Current monoscopic view policy from canvas
+ private int monoscopicViewPolicy;
+
+ // The manual positions of the left and right eyes in image-plate
+ // coordinates.
+ // Note that these values are only used in non-head-tracked mode
+ // when the view's window eyepoint policy is one of RELATIVE_TO_SCREEN
+ // or RELATIVE_TO_WINDOW.
+ private Point3d leftManualEyeInImagePlate = new Point3d();
+ private Point3d rightManualEyeInImagePlate = new Point3d();
+
+ // *************
+ // DERIVED DATA
+ // *************
+
+ // The width and height of the screen in meters (from ScreenViewCache)
+ double physicalScreenWidth;
+ double physicalScreenHeight;
+
+ // The width and height of the screen in pixels (from ScreenViewCache)
+ int screenWidth;
+ int screenHeight;
+
+ // Meters per pixel in the X and Y dimension (from ScreenViewCache)
+ double metersPerPixelX;
+ double metersPerPixelY;
+
+ // The position and size of the canvas (in pixels)
+ private int canvasX;
+ private int canvasY;
+ private int canvasWidth;
+ private int canvasHeight;
+
+ // Either the Canvas' or the View's monoscopicViewPolicy
+ private int effectiveMonoscopicViewPolicy;
+
+ // The current cached projection transforms.
+ private Transform3D leftProjection = new Transform3D();
+ private Transform3D rightProjection = new Transform3D();
+ private Transform3D infLeftProjection = new Transform3D();
+ private Transform3D infRightProjection = new Transform3D();
+
+ // The current cached viewing transforms.
+ private Transform3D leftVpcToEc = new Transform3D();
+ private Transform3D rightVpcToEc = new Transform3D();
+ private Transform3D infLeftVpcToEc = new Transform3D();
+ private Transform3D infRightVpcToEc = new Transform3D();
+
+ // The current cached inverse viewing transforms.
+ private Transform3D leftEcToVpc = new Transform3D();
+ private Transform3D rightEcToVpc = new Transform3D();
+ private Transform3D infLeftEcToVpc = new Transform3D();
+ private Transform3D infRightEcToVpc = new Transform3D();
+
+ // Arrays of Vector4d objects that represent the plane equations for
+ // the 6 planes in the viewing frustum in ViewPlatform coordinates.
+ private Vector4d[] leftFrustumPlanes = new Vector4d[6];
+ private Vector4d[] rightFrustumPlanes = new Vector4d[6];
+
+ // Arrays of Vector4d objects that represent the volume of viewing frustum
+ private Point4d leftFrustumPoints[] = new Point4d[8];
+ private Point4d rightFrustumPoints[] = new Point4d[8];
+
+ // Calibration matrix from Screen object for HMD mode using
+ // non-field-sequential stereo
+
+ private Transform3D headTrackerToLeftImagePlate = new Transform3D();
+ private Transform3D headTrackerToRightImagePlate = new Transform3D();
+
+ // Head tracked version of eye in imageplate
+ private Point3d leftTrackedEyeInImagePlate = new Point3d();
+ private Point3d rightTrackedEyeInImagePlate = new Point3d();
+
+ // Derived version of eye in image plate coordinates
+ private Point3d leftEyeInImagePlate = new Point3d();
+ private Point3d rightEyeInImagePlate = new Point3d();
+ private Point3d centerEyeInImagePlate = new Point3d();
+
+ // Derived version of nominalEyeOffsetFromNominalScreen
+ private double nominalEyeOffset;
+
+ // Physical window position,size and center (in image plate coordinates)
+ private double physicalWindowXLeft;
+ private double physicalWindowYBottom;
+ private double physicalWindowXRight;
+ private double physicalWindowYTop;
+ private double physicalWindowWidth;
+ private double physicalWindowHeight;
+ private Point3d physicalWindowCenter = new Point3d();
+
+ // Screen scale value from viewCache or from screen size.
+ private double screenScale;
+
+ // Window scale value that compensates for window size if
+ // the window resize policy is PHYSICAL_WORLD.
+ private double windowScale;
+
+ // ViewPlatform scale that takes coordinates from view platform
+ // coordinates and scales them to physical coordinates
+ private double viewPlatformScale;
+
+ // Various derived transforms
+
+ private Transform3D leftCcToVworld = new Transform3D();
+ private Transform3D rightCcToVworld = new Transform3D();
+
+ private Transform3D coexistenceToLeftPlate = new Transform3D();
+ private Transform3D coexistenceToRightPlate = new Transform3D();
+
+ private Transform3D vpcToCoexistence = new Transform3D();
+
+ private Transform3D vpcToLeftPlate = new Transform3D();
+ private Transform3D vpcToRightPlate = new Transform3D();
+ private Transform3D leftPlateToVpc = new Transform3D();
+ private Transform3D rightPlateToVpc = new Transform3D();
+ private Transform3D vworldToLeftPlate = new Transform3D();
+ private Transform3D lastVworldToLeftPlate = new Transform3D();
+ private Transform3D vworldToRightPlate = new Transform3D();
+ private Transform3D leftPlateToVworld = new Transform3D();
+ private Transform3D rightPlateToVworld = new Transform3D();
+ private Transform3D headToLeftImagePlate = new Transform3D();
+ private Transform3D headToRightImagePlate = new Transform3D();
+
+ private Transform3D vworldToTrackerBase = new Transform3D();
+ private Transform3D tempTrans = new Transform3D();
+ private Transform3D headToVworld = new Transform3D();
+ private Vector3d coexistenceCenter = new Vector3d();
+
+ // scale for transformimg clip and fog distances
+ private double vworldToCoexistenceScale;
+ private double infVworldToCoexistenceScale;
+
+ //
+ // Temporary matrices and vectors, so we dont generate garbage
+ //
+ private Transform3D tMat1 = new Transform3D();
+ private Transform3D tMat2 = new Transform3D();
+ private Vector3d tVec1 = new Vector3d();
+ private Vector3d tVec2 = new Vector3d();
+ private Vector3d tVec3 = new Vector3d();
+ private Point3d tPnt1 = new Point3d();
+ private Point3d tPnt2 = new Point3d();
+
+ private Matrix4d tMatrix = new Matrix4d();
+
+ /**
+ * The view platform transforms.
+ */
+ private Transform3D vworldToVpc = new Transform3D();
+ private Transform3D vpcToVworld = new Transform3D();
+ private Transform3D infVworldToVpc = new Transform3D();
+
+ // This flag is used to remember the last time doInfinite flag
+ // is true or not.
+ // If this cache is updated twice, the first time in RenderBin
+ // updateViewCache() and the second time in Renderer with
+ // geometryBackground. The first time will reset the vcDirtyMask
+ // to 0 so that geometry background will not get updated the
+ // second time doComputeDerivedData() is invoked when view change.
+ private boolean lastDoInfinite = false;
+ private boolean updateLastTime = false;
+
+ void getCanvasPositionAndSize() {
+ if(J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2) {
+ System.out.println("Get canvas position and size");
+ System.out.println("Before");
+ System.out.println("Canvas pos = (" + awtCanvasX + ", " +
+ awtCanvasY + "), size = " + awtCanvasWidth +
+ "x" + awtCanvasHeight);
+ System.out.println("After");
+ }
+ awtCanvasX = canvas.newPosition.x;
+ awtCanvasY = canvas.newPosition.y;
+ awtCanvasWidth = canvas.newSize.width;
+ awtCanvasHeight = canvas.newSize.height;
+
+ // The following works around problem when awt creates 0-size
+ // window at startup
+ if ((awtCanvasWidth <= 0) || (awtCanvasHeight <= 0)) {
+ awtCanvasWidth = 1;
+ awtCanvasHeight = 1;
+ }
+
+ if (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1) {
+ System.out.println("Canvas pos = (" + awtCanvasX + ", " +
+ awtCanvasY + "), size = " + awtCanvasWidth +
+ "x" + awtCanvasHeight);
+ }
+ }
+
+ void computefrustumBBox(BoundingBox frustumBBox) {
+ int i;
+
+ for(i = 0; i < leftFrustumPoints.length; i++) {
+ if(frustumBBox.lower.x > leftFrustumPoints[i].x)
+ frustumBBox.lower.x = leftFrustumPoints[i].x;
+ if(frustumBBox.lower.y > leftFrustumPoints[i].y)
+ frustumBBox.lower.y = leftFrustumPoints[i].y;
+ if(frustumBBox.lower.z > leftFrustumPoints[i].z)
+ frustumBBox.lower.z = leftFrustumPoints[i].z;
+
+ if(frustumBBox.upper.x < leftFrustumPoints[i].x)
+ frustumBBox.upper.x = leftFrustumPoints[i].x;
+ if(frustumBBox.upper.y < leftFrustumPoints[i].y)
+ frustumBBox.upper.y = leftFrustumPoints[i].y;
+ if(frustumBBox.upper.z < leftFrustumPoints[i].z)
+ frustumBBox.upper.z = leftFrustumPoints[i].z;
+ }
+
+ if(useStereo) {
+
+ for(i = 0; i< rightFrustumPoints.length; i++) {
+ if(frustumBBox.lower.x > rightFrustumPoints[i].x)
+ frustumBBox.lower.x = rightFrustumPoints[i].x;
+ if(frustumBBox.lower.y > rightFrustumPoints[i].y)
+ frustumBBox.lower.y = rightFrustumPoints[i].y;
+ if(frustumBBox.lower.z > rightFrustumPoints[i].z)
+ frustumBBox.lower.z = rightFrustumPoints[i].z;
+
+ if(frustumBBox.upper.x < rightFrustumPoints[i].x)
+ frustumBBox.upper.x = rightFrustumPoints[i].x;
+ if(frustumBBox.upper.y < rightFrustumPoints[i].y)
+ frustumBBox.upper.y = rightFrustumPoints[i].y;
+ if(frustumBBox.upper.z < rightFrustumPoints[i].z)
+ frustumBBox.upper.z = rightFrustumPoints[i].z;
+ }
+
+ }
+ }
+
+
+ void copyComputedCanvasViewCache(CanvasViewCache cvc, boolean doInfinite) {
+ // For performance reason, only data needed by renderer are copied.
+ // useStereo,
+ // canvasWidth,
+ // canvasHeight,
+ // leftProjection,
+ // rightProjection,
+ // leftVpcToEc,
+ // rightVpcToEc,
+ // leftFrustumPlanes,
+ // rightFrustumPlanes,
+ // vpcToVworld,
+ // vworldToVpc.
+
+ cvc.useStereo = useStereo;
+ cvc.canvasWidth = canvasWidth;
+ cvc.canvasHeight = canvasHeight;
+ cvc.leftProjection.set(leftProjection);
+ cvc.rightProjection.set(rightProjection);
+ cvc.leftVpcToEc.set(leftVpcToEc) ;
+ cvc.rightVpcToEc.set(rightVpcToEc) ;
+
+ cvc.vpcToVworld = vpcToVworld;
+ cvc.vworldToVpc.set(vworldToVpc);
+
+ if (doInfinite) {
+ cvc.infLeftProjection.set(infLeftProjection);
+ cvc.infRightProjection.set(infRightProjection);
+ cvc.infLeftVpcToEc.set(infLeftVpcToEc) ;
+ cvc.infRightVpcToEc.set(infRightVpcToEc) ;
+ cvc.infVworldToVpc.set(infVworldToVpc);
+ }
+
+ for (int i = 0; i < leftFrustumPlanes.length; i++) {
+ cvc.leftFrustumPlanes[i].x = leftFrustumPlanes[i].x;
+ cvc.leftFrustumPlanes[i].y = leftFrustumPlanes[i].y;
+ cvc.leftFrustumPlanes[i].z = leftFrustumPlanes[i].z;
+ cvc.leftFrustumPlanes[i].w = leftFrustumPlanes[i].w;
+
+ cvc.rightFrustumPlanes[i].x = rightFrustumPlanes[i].x;
+ cvc.rightFrustumPlanes[i].y = rightFrustumPlanes[i].y;
+ cvc.rightFrustumPlanes[i].z = rightFrustumPlanes[i].z;
+ cvc.rightFrustumPlanes[i].w = rightFrustumPlanes[i].w;
+ }
+ }
+
+
+ /**
+ * Take snapshot of all per-canvas API parameters and input values.
+ * NOTE: This is probably not needed, but we'll do it for symmetry
+ * with the ScreenViewCache and ViewCache objects.
+ */
+ synchronized void snapshot() {
+ cvcDirtyMask = canvas.cvDirtyMask;
+ canvas.cvDirtyMask = 0;
+ useStereo = canvas.useStereo;
+ monoscopicViewPolicy = canvas.monoscopicViewPolicy;
+ leftManualEyeInImagePlate.set(canvas.leftManualEyeInImagePlate);
+ rightManualEyeInImagePlate.set(canvas.rightManualEyeInImagePlate);
+
+ if(( cvcDirtyMask & Canvas3D.MOVED_OR_RESIZED_DIRTY) != 0) {
+ getCanvasPositionAndSize();
+ }
+
+ renderBin = canvas.view.renderBin;
+
+ }
+
+ /**
+ * Compute derived data using the snapshot of the per-canvas,
+ * per-screen and per-view data.
+ */
+ synchronized void computeDerivedData(boolean currentFlag,
+ CanvasViewCache cvc, BoundingBox frustumBBox, boolean doInfinite) {
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) {
+ synchronized(debugLock) {
+ System.out.println("------------------------------");
+ doComputeDerivedData(currentFlag,cvc,frustumBBox,doInfinite);
+ }
+ }
+ else {
+ doComputeDerivedData(currentFlag,cvc,frustumBBox,doInfinite);
+ }
+ }
+
+ /**
+ * Compute derived data using the snapshot of the per-canvas,
+ * per-screen and per-view data. Caller must synchronize before
+ * calling this method.
+ */
+ private void doComputeDerivedData(boolean currentFlag,
+ CanvasViewCache cvc, BoundingBox frustumBBox, boolean doInfinite) {
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ if(cvcDirtyMask != 0)
+ System.out.println("cvcDirtyMask : " + cvcDirtyMask);
+
+ if(screenViewCache.scrvcDirtyMask != 0)
+ System.out.println("scrvcDirtyMask : "+
+ screenViewCache.scrvcDirtyMask);
+
+ if(viewCache.vcDirtyMask != 0)
+ System.out.println("vcDirtyMask : " + viewCache.vcDirtyMask);
+ }
+
+
+ // NOTE: This fix is only fixing the symptoms, but not the
+ // root of the bug. We shouldn't have to check for null here.
+ if(viewCache.vpRetained == null) {
+ System.out.println("CanvasViewCache : Error! viewCache.vpRetained is null");
+ return;
+ }
+
+ // This flag is use to force a computation when a ViewPlatformTransform
+ // is detected. No sync. needed. We're doing a read of t/f.
+ // TODO: Peeking at the dirty flag is a hack. Need to revisit this.
+ boolean vprNotDirty = (viewCache.vpRetained.vprDirtyMask == 0);
+
+ if(!canvas.offScreen &&
+ (vprNotDirty) &&
+ (cvcDirtyMask == 0) &&
+ (screenViewCache.scrvcDirtyMask == 0) &&
+ (viewCache.vcDirtyMask == 0) &&
+ !(updateLastTime && (doInfinite != lastDoInfinite))) {
+ if(frustumBBox != null)
+ computefrustumBBox(frustumBBox);
+
+ // Copy the computed data into cvc.
+ if(cvc != null) {
+ copyComputedCanvasViewCache(cvc, doInfinite);
+ }
+ lastDoInfinite = doInfinite;
+ updateLastTime = false;
+ return;
+ }
+
+ lastDoInfinite = doInfinite;
+ updateLastTime = true;
+
+ if(currentFlag) {
+ vpcToVworld.set(viewCache.vpRetained.getCurrentLocalToVworld(null));
+ }
+ else {
+ vpcToVworld.set(viewCache.vpRetained.getLastLocalToVworld(null));
+ }
+
+ // System.out.println("vpcToVworld is \n" + vpcToVworld);
+
+ try {
+ vworldToVpc.invert(vpcToVworld);
+ }
+ catch (SingularMatrixException e) {
+ vworldToVpc.setIdentity();
+ //System.out.println("SingularMatrixException encountered when doing vworldToVpc invert");
+ }
+ if (doInfinite) {
+ vworldToVpc.getRotation(infVworldToVpc);
+ }
+
+ // Compute global flags
+ if (monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW)
+ effectiveMonoscopicViewPolicy = viewCache.monoscopicViewPolicy;
+ else
+ effectiveMonoscopicViewPolicy = monoscopicViewPolicy;
+
+ // Recompute info about current canvas window
+ computeCanvasInfo();
+
+ // Compute coexistence center (in plate coordinates)
+ computeCoexistenceCenter();
+
+ // Get Eye position in image-plate coordinates
+ cacheEyePosition();
+
+ // Compute VPC to COE and COE to PLATE transforms
+ computeVpcToCoexistence();
+ computeCoexistenceToPlate();
+
+ // Compute view and projection matrices
+ computeView(doInfinite);
+
+
+ computePlateToVworld();
+
+ if (!currentFlag) {
+ // save the result for use in RasterRetained computeWinCoord
+ lastVworldToLeftPlate.set(vworldToLeftPlate);
+ }
+ computeHeadToVworld();
+
+ if (frustumBBox != null)
+ computefrustumBBox(frustumBBox);
+
+ // Copy the computed data into cvc.
+ if(cvc != null)
+ copyComputedCanvasViewCache(cvc, doInfinite);
+
+ canvas.canvasDirty |= Canvas3D.VWORLD_SCALE_DIRTY;
+
+ // reset screen view dirty mask if canvas is offScreen. Note:
+ // there is only one canvas per offscreen, so it is ok to
+ // do the reset here.
+ if (canvas.offScreen) {
+ screenViewCache.scrvcDirtyMask = 0;
+ }
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) {
+ // Print some data :
+ System.out.println("useStereo = " + useStereo);
+ System.out.println("leftProjection:\n" + leftProjection);
+ System.out.println("rightProjection:\n " + rightProjection);
+ System.out.println("leftVpcToEc:\n" + leftVpcToEc);
+ System.out.println("rightVpcToEc:\n" + rightVpcToEc);
+ System.out.println("vpcToVworld:\n" + vpcToVworld);
+ System.out.println("vworldToVpc:\n" + vworldToVpc);
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ int i;
+ for (i = 0; i < leftFrustumPlanes.length; i++) {
+ System.out.println("leftFrustumPlanes " + i + " is " +
+ leftFrustumPlanes[i]);
+ }
+
+ for (i = 0; i < rightFrustumPlanes.length; i++) {
+ System.out.println("rightFrustumPlanes " + i + " is " +
+ rightFrustumPlanes[i]);
+ }
+ }
+ }
+
+ }
+
+ private void computeCanvasInfo() {
+ // Copy the screen width and height info into derived parameters
+ physicalScreenWidth = screenViewCache.physicalScreenWidth;
+ physicalScreenHeight = screenViewCache.physicalScreenHeight;
+
+ screenWidth = screenViewCache.screenWidth;
+ screenHeight = screenViewCache.screenHeight;
+
+ metersPerPixelX = screenViewCache.metersPerPixelX;
+ metersPerPixelY = screenViewCache.metersPerPixelY;
+
+ // If a multi-screen virtual device (e.g. Xinerama) is being used,
+ // then awtCanvasX and awtCanvasY are relative to the origin of that
+ // virtual screen. Subtract the origin of the physical screen to
+ // compute the origin in physical (image plate) coordinates.
+ Rectangle screenBounds = canvas.graphicsConfiguration.getBounds();
+ canvasX = awtCanvasX - screenBounds.x;
+ canvasY = awtCanvasY - screenBounds.y;
+
+ // Use awtCanvasWidth and awtCanvasHeight as reported.
+ canvasWidth = awtCanvasWidth;
+ canvasHeight = awtCanvasHeight;
+
+ // Convert the window system ``pixel'' coordinate location and size
+ // of the window into physical units (meters) and coordinate system.
+
+ // Window width and Height in meters
+ physicalWindowWidth = canvasWidth * metersPerPixelX;
+ physicalWindowHeight = canvasHeight * metersPerPixelY;
+
+ // Compute the 4 corners of the window in physical units
+ physicalWindowXLeft = metersPerPixelX *
+ (double) canvasX;
+ physicalWindowYBottom = metersPerPixelY *
+ (double)(screenHeight - canvasHeight - canvasY);
+
+ physicalWindowXRight = physicalWindowXLeft + physicalWindowWidth;
+ physicalWindowYTop = physicalWindowYBottom + physicalWindowHeight;
+
+ // Cache the physical location of the center of the window
+ physicalWindowCenter.x =
+ physicalWindowXLeft + physicalWindowWidth / 2.0;
+ physicalWindowCenter.y =
+ physicalWindowYBottom + physicalWindowHeight / 2.0;
+ physicalWindowCenter.z = 0.0;
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("Canvas pos = (" + awtCanvasX + ", " +
+ awtCanvasY + "), size = " + awtCanvasWidth +
+ "x" + awtCanvasHeight);
+
+ System.out.println("Window LL corner (in plate coordinates): " +
+ "(" + physicalWindowXLeft + "," + physicalWindowYBottom + ")");
+
+ System.out.println("Window size (in plate coordinates): " +
+ "(" + physicalWindowWidth + "," + physicalWindowHeight + ")");
+
+ System.out.println("Window center (in plate coordinates): " +
+ physicalWindowCenter);
+
+ System.out.println();
+ }
+
+ // Compute the view platform scale. This combines
+ // the screen scale and the window scale.
+ computeViewPlatformScale();
+
+ if (!viewCache.compatibilityModeEnable &&
+ viewCache.viewPolicy == View.HMD_VIEW) {
+ if (!useStereo) {
+ switch(effectiveMonoscopicViewPolicy) {
+ case View.CYCLOPEAN_EYE_VIEW:
+ if(J3dDebug.devPhase) {
+ System.out.println("CanvasViewCache : Should never reach here.\n" +
+ "HMD_VIEW with CYCLOPEAN_EYE_VIEW is not allowed");
+ }
+ break;
+
+ case View.LEFT_EYE_VIEW:
+ headTrackerToLeftImagePlate.set(screenViewCache.
+ headTrackerToLeftImagePlate);
+ break;
+
+ case View.RIGHT_EYE_VIEW:
+ headTrackerToLeftImagePlate.set(screenViewCache.
+ headTrackerToRightImagePlate);
+ break;
+ }
+ }
+ else {
+ headTrackerToLeftImagePlate.set(screenViewCache.
+ headTrackerToLeftImagePlate);
+
+ headTrackerToRightImagePlate.set(screenViewCache.
+ headTrackerToRightImagePlate);
+ }
+
+ }
+ }
+
+ // Routine to compute the center of coexistence coordinates in
+ // imageplate coordinates. Also compute the scale from Vpc
+ private void computeViewPlatformScale() {
+ windowScale = screenScale = 1.0;
+
+ if (!viewCache.compatibilityModeEnable) {
+ switch (viewCache.screenScalePolicy) {
+ case View.SCALE_SCREEN_SIZE:
+ screenScale = physicalScreenWidth / 2.0;
+ break;
+ case View.SCALE_EXPLICIT:
+ screenScale = viewCache.screenScale;
+ break;
+ }
+
+ if (viewCache.windowResizePolicy == View.PHYSICAL_WORLD) {
+ windowScale = physicalWindowWidth / physicalScreenWidth;
+ }
+ }
+
+ viewPlatformScale = windowScale * screenScale;
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("viewCache.windowResizePolicy = " +
+ viewCache.windowResizePolicy);
+ System.out.println("physicalWindowWidth = " + physicalWindowWidth);
+ System.out.println("physicalScreenWidth = " + physicalScreenWidth);
+ System.out.println("windowScale = " + windowScale);
+ System.out.println("screenScale = " + screenScale);
+ System.out.println("viewPlatformScale = " + viewPlatformScale);
+ }
+ }
+
+ private void cacheEyePosFixedField() {
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1))
+ System.out.println("cacheEyePosFixedField:");
+
+ // y is always the window center
+ rightEyeInImagePlate.y =
+ leftEyeInImagePlate.y =
+ physicalWindowCenter.y;
+
+ if (!useStereo) {
+ switch(effectiveMonoscopicViewPolicy) {
+ case View.CYCLOPEAN_EYE_VIEW:
+ leftEyeInImagePlate.x = physicalWindowCenter.x;
+ break;
+
+ case View.LEFT_EYE_VIEW:
+ leftEyeInImagePlate.x =
+ physicalWindowCenter.x + viewCache.leftEyePosInHead.x;
+ break;
+
+ case View.RIGHT_EYE_VIEW:
+ leftEyeInImagePlate.x =
+ physicalWindowCenter.x + viewCache.rightEyePosInHead.x;
+ break;
+ }
+
+ // Set right as well just in case
+ rightEyeInImagePlate.x = leftEyeInImagePlate.x;
+ }
+ else {
+ leftEyeInImagePlate.x =
+ physicalWindowCenter.x + viewCache.leftEyePosInHead.x;
+
+ rightEyeInImagePlate.x =
+ physicalWindowCenter.x + viewCache.rightEyePosInHead.x;
+ }
+
+ //
+ // Derive the z distance by constraining the field of view of the
+ // window width to be constant.
+ //
+ rightEyeInImagePlate.z =
+ leftEyeInImagePlate.z =
+ physicalWindowWidth /
+ (2.0 * Math.tan(viewCache.fieldOfView / 2.0));
+
+ // Denote that eyes-in-ImagePlate fields have changed so that
+ // these new values can be sent to the AudioDevice
+ if (this.viewCache.view.soundScheduler != null)
+ this.viewCache.view.soundScheduler.setListenerFlag(
+ SoundScheduler.EYE_POSITIONS_CHANGED);
+ }
+
+ /**
+ * Case of view eye position contrainted to center of window, but
+ * with z distance from plate eye pos.
+ */
+ private void cacheEyePosWindowRelative() {
+
+ if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1))
+ System.out.println("cacheEyePosWindowRelative:");
+
+ // y is always the window center
+ rightEyeInImagePlate.y =
+ leftEyeInImagePlate.y =
+ physicalWindowCenter.y;
+
+ // z is always from the existing eye pos
+ rightEyeInImagePlate.z =
+ leftEyeInImagePlate.z =
+ leftManualEyeInImagePlate.z;
+
+ if (!useStereo) {
+
+ switch(effectiveMonoscopicViewPolicy) {
+
+ case View.CYCLOPEAN_EYE_VIEW:
+ leftEyeInImagePlate.x =
+ physicalWindowCenter.x;
+ break;
+
+ case View.LEFT_EYE_VIEW:
+ leftEyeInImagePlate.x =
+ physicalWindowCenter.x +
+ viewCache.leftEyePosInHead.x;
+ break;
+
+ case View.RIGHT_EYE_VIEW:
+ leftEyeInImagePlate.x =
+ physicalWindowCenter.x +
+ viewCache.rightEyePosInHead.x;
+ break;
+
+ }
+
+ // Set right as well just in case
+ rightEyeInImagePlate.x =
+ leftEyeInImagePlate.x;
+
+ }
+ else {
+
+ leftEyeInImagePlate.x =
+ physicalWindowCenter.x +
+ viewCache.leftEyePosInHead.x;
+
+ rightEyeInImagePlate.x =
+ physicalWindowCenter.x +
+ viewCache.rightEyePosInHead.x;
+
+ // Right z gets its own value
+ rightEyeInImagePlate.z =
+ rightManualEyeInImagePlate.z;
+ }
+
+ // Denote that eyes-in-ImagePlate fields have changed so that
+ // these new values can be sent to the AudioDevice
+ if (this.viewCache.view.soundScheduler != null)
+ this.viewCache.view.soundScheduler.setListenerFlag(
+ SoundScheduler.EYE_POSITIONS_CHANGED);
+ }
+
+ /**
+ * Common routine used when head tracking and when using manual
+ * relative_to_screen eyepoint policy.
+ */
+ private void cacheEyePosScreenRelative(Point3d leftEye, Point3d rightEye) {
+ if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1))
+ System.out.println("cacheEyePosScreenRelative:");
+
+ if (!useStereo) {
+ switch(effectiveMonoscopicViewPolicy) {
+
+ case View.CYCLOPEAN_EYE_VIEW:
+ leftEyeInImagePlate.x = (leftEye.x + rightEye.x) / 2.0;
+ leftEyeInImagePlate.y = (leftEye.y + rightEye.y) / 2.0;
+ leftEyeInImagePlate.z = (leftEye.z + rightEye.z) / 2.0;
+ break;
+
+ case View.LEFT_EYE_VIEW:
+ leftEyeInImagePlate.set(leftEye);
+ break;
+
+ case View.RIGHT_EYE_VIEW:
+ leftEyeInImagePlate.set(rightEye);
+ break;
+
+ }
+
+ // Set right as well just in case
+ rightEyeInImagePlate.set(leftEyeInImagePlate);
+ }
+ else {
+ leftEyeInImagePlate.set(leftEye);
+ rightEyeInImagePlate.set(rightEye);
+ }
+
+ // Denote that eyes-in-ImagePlate fields have changed so that
+ // these new values can be sent to the AudioDevice
+ if (this.viewCache.view.soundScheduler != null)
+ this.viewCache.view.soundScheduler.setListenerFlag(
+ SoundScheduler.EYE_POSITIONS_CHANGED);
+ }
+
+ private void cacheEyePosCoexistenceRelative(Point3d leftManualEyeInCoexistence,
+ Point3d rightManualEyeInCoexistence) {
+
+ tPnt1.set(leftManualEyeInCoexistence);
+ viewCache.coexistenceToTrackerBase.transform(tPnt1);
+ screenViewCache.trackerBaseToImagePlate.transform(tPnt1);
+ tPnt1.add(coexistenceCenter);
+
+ tPnt2.set(rightManualEyeInCoexistence);
+ viewCache.coexistenceToTrackerBase.transform(tPnt2);
+ screenViewCache.trackerBaseToImagePlate.transform(tPnt2);
+ tPnt2.add(coexistenceCenter);
+
+ cacheEyePosScreenRelative(tPnt1, tPnt2);
+
+ }
+
+ /**
+ * Compute the head-tracked eye position for the right and
+ * left eyes.
+ */
+ private void computeTrackedEyePosition() {
+ if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("computeTrackedEyePosition:");
+ System.out.println("viewCache.headTrackerToTrackerBase:");
+ System.out.println(viewCache.headTrackerToTrackerBase);
+
+ System.out.println("viewCache.headToHeadTracker:");
+ System.out.println(viewCache.headToHeadTracker);
+ }
+
+ if (viewCache.viewPolicy != View.HMD_VIEW) {
+ if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("screenViewCache.trackerBaseToImagePlate:");
+ System.out.println(screenViewCache.trackerBaseToImagePlate);
+ }
+
+ headToLeftImagePlate.set(coexistenceCenter);
+ headToLeftImagePlate.mul(screenViewCache.trackerBaseToImagePlate);
+ headToLeftImagePlate.mul(viewCache.headTrackerToTrackerBase);
+ headToLeftImagePlate.mul(viewCache.headToHeadTracker);
+
+ headToLeftImagePlate.transform(viewCache.leftEyePosInHead,
+ leftTrackedEyeInImagePlate);
+
+ headToLeftImagePlate.transform(viewCache.rightEyePosInHead,
+ rightTrackedEyeInImagePlate);
+ }
+ else {
+ if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("headTrackerToLeftImagePlate:");
+ System.out.println(headTrackerToLeftImagePlate);
+ }
+
+ headToLeftImagePlate.mul(headTrackerToLeftImagePlate,
+ viewCache.headToHeadTracker);
+
+ headToLeftImagePlate.transform(viewCache.leftEyePosInHead,
+ leftTrackedEyeInImagePlate);
+
+ if(useStereo) {
+ headToRightImagePlate.mul(headTrackerToRightImagePlate,
+ viewCache.headToHeadTracker);
+
+ headToRightImagePlate.transform(viewCache.rightEyePosInHead,
+ rightTrackedEyeInImagePlate);
+ }
+ else { // HMD_VIEW with no stereo.
+ headToLeftImagePlate.transform(viewCache.rightEyePosInHead,
+ rightTrackedEyeInImagePlate);
+ }
+
+ }
+
+ if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("headToLeftImagePlate:");
+ System.out.println(headToLeftImagePlate);
+ System.out.println("headToRightImagePlate:");
+ System.out.println(headToRightImagePlate);
+
+ }
+ }
+
+ /**
+ * Routine to cache the current eye position in image plate
+ * coordinates.
+ */
+ private void cacheEyePosition() {
+ if (viewCache.compatibilityModeEnable) {
+ // TODO: Compute compatibility mode eye position in ImagePlate???
+ cacheEyePosScreenRelative(leftManualEyeInImagePlate,
+ rightManualEyeInImagePlate);
+ }
+ else if (viewCache.getDoHeadTracking()) {
+ computeTrackedEyePosition();
+ cacheEyePosScreenRelative(leftTrackedEyeInImagePlate,
+ rightTrackedEyeInImagePlate);
+ }
+ else {
+ switch (viewCache.windowEyepointPolicy) {
+
+ case View.RELATIVE_TO_FIELD_OF_VIEW:
+ cacheEyePosFixedField();
+ break;
+
+ case View.RELATIVE_TO_WINDOW:
+ cacheEyePosWindowRelative();
+ break;
+
+ case View.RELATIVE_TO_SCREEN:
+ cacheEyePosScreenRelative(leftManualEyeInImagePlate,
+ rightManualEyeInImagePlate);
+ break;
+
+ case View.RELATIVE_TO_COEXISTENCE:
+ cacheEyePosCoexistenceRelative(viewCache.leftManualEyeInCoexistence,
+ viewCache.rightManualEyeInCoexistence);
+ break;
+ }
+ }
+
+ // Compute center eye
+ centerEyeInImagePlate.add(leftEyeInImagePlate, rightEyeInImagePlate);
+ centerEyeInImagePlate.scale(0.5);
+
+ // Compute derived value of nominalEyeOffsetFromNominalScreen
+ if (viewCache.windowEyepointPolicy == View.RELATIVE_TO_FIELD_OF_VIEW)
+ nominalEyeOffset = centerEyeInImagePlate.z;
+ else
+ nominalEyeOffset = viewCache.nominalEyeOffsetFromNominalScreen;
+
+ if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) {
+ System.out.println("leftEyeInImagePlate = " +
+ leftEyeInImagePlate);
+ System.out.println("rightEyeInImagePlate = " +
+ rightEyeInImagePlate);
+ System.out.println("centerEyeInImagePlate = " +
+ centerEyeInImagePlate);
+ System.out.println("nominalEyeOffset = " +
+ nominalEyeOffset);
+ System.out.println();
+ }
+ }
+
+ private void computePlateToVworld() {
+ if (viewCache.compatibilityModeEnable) {
+ // TODO: implement this correctly for compat mode
+ leftPlateToVworld.setIdentity();
+ vworldToLeftPlate.setIdentity();
+ }
+ else {
+ try {
+ leftPlateToVpc.invert(vpcToLeftPlate);
+ }
+ catch (SingularMatrixException e) {
+ leftPlateToVpc.setIdentity();
+ /*
+ System.out.println("SingularMatrixException encountered when doing" +
+ " leftPlateToVpc invert");
+ */
+ }
+
+ leftPlateToVworld.mul(vpcToVworld, leftPlateToVpc);
+ vworldToLeftPlate.mul(vpcToLeftPlate, vworldToVpc);
+
+ if(useStereo) {
+ try {
+ rightPlateToVpc.invert(vpcToRightPlate);
+ }
+ catch (SingularMatrixException e) {
+ rightPlateToVpc.setIdentity();
+ /*
+ System.out.println("SingularMatrixException encountered when doing" +
+ " rightPlateToVpc invert");
+ */
+ }
+
+ rightPlateToVworld.mul(vpcToVworld, rightPlateToVpc);
+ vworldToRightPlate.mul(vpcToRightPlate, vworldToVpc);
+
+ }
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("vpcToVworld:");
+ System.out.println(vpcToVworld);
+ System.out.println("vpcToLeftPlate:");
+ System.out.println(vpcToLeftPlate);
+ if(useStereo) {
+ System.out.println("vpcToRightPlate:");
+ System.out.println(vpcToRightPlate);
+
+ }
+
+ }
+ }
+
+ // Denote that eyes-in-ImagePlate fields have changed so that
+ // these new values can be sent to the AudioDevice
+ if (this.viewCache.view.soundScheduler != null)
+ this.viewCache.view.soundScheduler.setListenerFlag(
+ SoundScheduler.IMAGE_PLATE_TO_VWORLD_CHANGED);
+ }
+
+
+ private void computeHeadToVworld() {
+ // Concatenate headToLeftImagePlate with leftPlateToVworld
+
+ if (viewCache.compatibilityModeEnable) {
+ // TODO: implement this correctly for compat mode
+ headToVworld.setIdentity();
+ }
+ else {
+ headToVworld.mul(leftPlateToVworld, headToLeftImagePlate);
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("leftPlateToVworld:");
+ System.out.println(leftPlateToVworld);
+ System.out.println("headToLeftImagePlate:");
+ System.out.println(headToLeftImagePlate);
+ System.out.println("...gives -> headToVworld:");
+ System.out.println(headToVworld);
+ }
+ }
+
+ // Denote that eyes-in-ImagePlate fields have changed so that
+ // these new values can be sent to the AudioDevice
+ if (this.viewCache.view.soundScheduler != null)
+ this.viewCache.view.soundScheduler.setListenerFlag(
+ SoundScheduler.HEAD_TO_VWORLD_CHANGED);
+ }
+
+ private void computeVpcToCoexistence() {
+ // Create a transform with the view platform to coexistence scale
+ tMat1.set(viewPlatformScale);
+
+ // TODO: Is this really correct to ignore HMD?
+
+ if (viewCache.viewPolicy != View.HMD_VIEW) {
+ switch (viewCache.coexistenceCenterInPworldPolicy) {
+ case View.NOMINAL_SCREEN :
+ switch (viewCache.viewAttachPolicy) {
+ case View.NOMINAL_SCREEN:
+ tMat2.setIdentity();
+ break;
+ case View.NOMINAL_HEAD:
+ tVec1.set(0.0, 0.0, nominalEyeOffset);
+ tMat2.set(tVec1);
+ break;
+ case View.NOMINAL_FEET:
+ tVec1.set(0.0, -viewCache.nominalEyeHeightFromGround,
+ nominalEyeOffset);
+ tMat2.set(tVec1);
+ break;
+ }
+
+ break;
+ case View.NOMINAL_HEAD :
+ switch (viewCache.viewAttachPolicy) {
+ case View.NOMINAL_SCREEN:
+ tVec1.set(0.0, 0.0, -nominalEyeOffset);
+ tMat2.set(tVec1);
+ break;
+ case View.NOMINAL_HEAD:
+ tMat2.setIdentity();
+ break;
+ case View.NOMINAL_FEET:
+ tVec1.set(0.0, -viewCache.nominalEyeHeightFromGround,
+ 0.0);
+ tMat2.set(tVec1);
+ break;
+ }
+ break;
+ case View.NOMINAL_FEET:
+ switch (viewCache.viewAttachPolicy) {
+ case View.NOMINAL_SCREEN:
+ tVec1.set(0.0,
+ viewCache.nominalEyeHeightFromGround, -nominalEyeOffset);
+ tMat2.set(tVec1);
+ break;
+ case View.NOMINAL_HEAD:
+ tVec1.set(0.0, viewCache.nominalEyeHeightFromGround,
+ 0.0);
+ tMat2.set(tVec1);
+
+ break;
+ case View.NOMINAL_FEET:
+ tMat2.setIdentity();
+ break;
+ }
+ break;
+ }
+
+ vpcToCoexistence.mul(tMat2, tMat1);
+ }
+ else {
+ vpcToCoexistence.set(tMat1);
+ }
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("vpcToCoexistence:");
+ System.out.println(vpcToCoexistence);
+ }
+ }
+
+ private void computeCoexistenceCenter() {
+
+ if ((!viewCache.compatibilityModeEnable) &&
+ (viewCache.viewPolicy != View.HMD_VIEW) &&
+ (viewCache.coexistenceCenteringEnable) &&
+ (viewCache.coexistenceCenterInPworldPolicy == View.NOMINAL_SCREEN)) {
+
+ // Compute the coexistence center in image plate coordinates
+
+ // Image plate cordinates have their orgin in the lower
+ // left hand corner of the CRT visiable raster.
+ // The nominal coexistence center is at the *center* of
+ // targeted area: either the window or screen, depending
+ // on policy.
+ if (viewCache.windowMovementPolicy == View.VIRTUAL_WORLD) {
+ coexistenceCenter.x = physicalScreenWidth / 2.0;
+ coexistenceCenter.y = physicalScreenHeight / 2.0;
+ coexistenceCenter.z = 0.0;
+ }
+ else { // windowMovementPolicy == PHYSICAL_WORLD
+ coexistenceCenter.x = physicalWindowCenter.x;
+ coexistenceCenter.y = physicalWindowCenter.y;
+ coexistenceCenter.z = 0.0;
+ }
+ }
+ else {
+ coexistenceCenter.set(0.0, 0.0, 0.0);
+ }
+
+ if(J3dDebug.devPhase) {
+ if (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1) {
+ System.out.println("coexistenceCenter = " + coexistenceCenter);
+ }
+ }
+ }
+
+ private void computeCoexistenceToPlate() {
+ if (viewCache.compatibilityModeEnable) {
+ // TODO: implement this correctly
+ coexistenceToLeftPlate.setIdentity();
+ return;
+ }
+
+ if (viewCache.viewPolicy != View.HMD_VIEW) {
+ coexistenceToLeftPlate.set(coexistenceCenter);
+ coexistenceToLeftPlate.mul(screenViewCache.trackerBaseToImagePlate);
+ coexistenceToLeftPlate.mul(viewCache.coexistenceToTrackerBase);
+
+ if(useStereo) {
+ coexistenceToRightPlate.set(coexistenceToLeftPlate);
+ }
+ }
+ else {
+ coexistenceToLeftPlate.mul(headTrackerToLeftImagePlate,
+ viewCache.trackerBaseToHeadTracker);
+ coexistenceToLeftPlate.mul(viewCache.coexistenceToTrackerBase);
+
+ if(useStereo) {
+ coexistenceToRightPlate.mul(headTrackerToRightImagePlate,
+ viewCache.trackerBaseToHeadTracker);
+ coexistenceToRightPlate.mul(viewCache.coexistenceToTrackerBase);
+ }
+ }
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("coexistenceToLeftPlate:");
+ System.out.println(coexistenceToLeftPlate);
+ if(useStereo) {
+ System.out.println("coexistenceToRightPlate:");
+ System.out.println(coexistenceToRightPlate);
+
+ }
+ }
+ }
+
+ /**
+ * Computes the viewing matrices.
+ *
+ * computeView computes the following:
+ *
+ * <ul>
+ * left (& right) eye viewing matrices (only left is valid for mono view)
+ * </ul>
+ *
+ * This call works for both fixed screen and HMD displays.
+ */
+ private void computeView(boolean doInfinite) {
+ int i,j;
+ int backClipPolicy;
+ double Fl, Fr, B, scale, backClipDistance;
+
+ // compute scale used for transforming clip and fog distances
+ vworldToCoexistenceScale = vworldToVpc.getDistanceScale()
+ * vpcToCoexistence.getDistanceScale();
+ if(doInfinite) {
+ infVworldToCoexistenceScale = infVworldToVpc.getDistanceScale()
+ * vpcToCoexistence.getDistanceScale();
+ }
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("vworldToCoexistenceScale = " +
+ vworldToCoexistenceScale);
+ }
+
+ // compute coexistenceToVworld transform -- dirty bit candidate!!
+ tempTrans.mul(viewCache.coexistenceToTrackerBase, vpcToCoexistence);
+ vworldToTrackerBase.mul(tempTrans, vworldToVpc);
+
+ // If we are in compatibility mode, compute the view and
+ // projection matrices accordingly
+ if (viewCache.compatibilityModeEnable) {
+ leftProjection.set(viewCache.compatLeftProjection);
+ leftVpcToEc.set(viewCache.compatVpcToEc);
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) {
+ System.out.println("Left projection and view matrices");
+ System.out.println("ecToCc (leftProjection) :");
+ System.out.println(leftProjection);
+ System.out.println("vpcToEc:");
+ System.out.println(leftVpcToEc);
+ }
+
+ computeFrustumPlanes(leftProjection, leftVpcToEc,
+ leftFrustumPlanes, leftFrustumPoints,
+ leftCcToVworld);
+
+ if(useStereo) {
+ rightProjection.set(viewCache.compatRightProjection);
+ rightVpcToEc.set(viewCache.compatVpcToEc);
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) {
+ System.out.println("Right projection and view matrices");
+ System.out.println("ecToCc:");
+ System.out.println("vpcToEc:");
+ System.out.println(rightVpcToEc);
+ }
+
+ computeFrustumPlanes(rightProjection, rightVpcToEc,
+ rightFrustumPlanes, rightFrustumPoints,
+ rightCcToVworld);
+ }
+
+ return;
+ }
+
+ //
+ // The clipping plane distances are set from the internal policy.
+ //
+ // Note that the plane distance follows the standard Z axis
+ // convention, e.g. negative numbers further away.
+ // Note that for policy from eye, the distance is negative in
+ // the direction of z in front of the eye.
+ // Note that for policy from screen, the distance is negative for
+ // locations behind the screen, and positive in front.
+ //
+ // The distance attributes are measured either in physical (plate)
+ // units, or vworld units.
+ //
+
+ // Compute scale factor for front clip plane computation
+ if (viewCache.frontClipPolicy == View.VIRTUAL_EYE ||
+ viewCache.frontClipPolicy == View.VIRTUAL_SCREEN) {
+ scale = vworldToCoexistenceScale;
+ }
+ else {
+ scale = windowScale;
+ }
+
+ // Set left and right front clipping plane distances.
+ if(viewCache.frontClipPolicy == View.PHYSICAL_EYE ||
+ viewCache.frontClipPolicy == View.VIRTUAL_EYE) {
+ Fl = leftEyeInImagePlate.z +
+ scale * -viewCache.frontClipDistance;
+ Fr = rightEyeInImagePlate.z +
+ scale * -viewCache.frontClipDistance;
+ }
+ else {
+ Fl = scale * -viewCache.frontClipDistance;
+ Fr = scale * -viewCache.frontClipDistance;
+ }
+
+ // if there is an active clip node, use it and ignore the view's
+ // backclip
+ if ((renderBin != null) && (renderBin.backClipActive)) {
+ backClipPolicy = View.VIRTUAL_EYE;
+ backClipDistance = renderBin.backClipDistanceInVworld;
+ } else {
+ backClipPolicy = viewCache.backClipPolicy;
+ backClipDistance = viewCache.backClipDistance;
+ }
+
+ // Compute scale factor for rear clip plane computation
+ if (backClipPolicy == View.VIRTUAL_EYE ||
+ backClipPolicy == View.VIRTUAL_SCREEN) {
+ scale = vworldToCoexistenceScale;
+ }
+ else {
+ scale = windowScale;
+ }
+
+ // Set left and right rear clipping plane distnaces.
+ if(backClipPolicy == View.PHYSICAL_EYE ||
+ backClipPolicy == View.VIRTUAL_EYE) {
+ // Yes, left for both left and right rear.
+ B = leftEyeInImagePlate.z +
+ scale * -backClipDistance;
+ }
+ else {
+ B = scale * -backClipDistance;
+ }
+
+ // TODO: Can optimize for HMD case.
+ if (true /*viewCache.viewPolicy != View.HMD_VIEW*/) {
+
+ // Call buildProjView to build the projection and view matrices.
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("Left projection and view matrices");
+ System.out.println("Fl " + Fl + " B " + B);
+ System.out.println("leftEyeInImagePlate\n" + leftEyeInImagePlate);
+ System.out.println("Before : leftProjection\n" + leftProjection);
+ System.out.println("Before leftVpcToEc\n" + leftVpcToEc);
+ }
+
+ buildProjView(leftEyeInImagePlate, coexistenceToLeftPlate,
+ vpcToLeftPlate, Fl, B, leftProjection, leftVpcToEc, false);
+
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("After : leftProjection\n" + leftProjection);
+ System.out.println("After leftVpcToEc\n" + leftVpcToEc);
+ }
+
+ computeFrustumPlanes(leftProjection, leftVpcToEc,
+ leftFrustumPlanes, leftFrustumPoints,
+ leftCcToVworld);
+
+ if(useStereo) {
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2))
+ System.out.println("Right projection and view matrices");
+
+ buildProjView(rightEyeInImagePlate, coexistenceToRightPlate,
+ vpcToRightPlate, Fr, B, rightProjection,
+ rightVpcToEc, false);
+
+ computeFrustumPlanes(rightProjection, rightVpcToEc,
+ rightFrustumPlanes, rightFrustumPoints,
+ rightCcToVworld);
+ }
+
+ //
+ // Now to compute the left (& right) eye (and infinite)
+ // viewing matrices.
+ if(doInfinite) {
+ // Call buildProjView separately for infinite view
+ buildProjView(leftEyeInImagePlate, coexistenceToLeftPlate,
+ vpcToLeftPlate, leftEyeInImagePlate.z - 0.05,
+ leftEyeInImagePlate.z - 1.5,
+ infLeftProjection, infLeftVpcToEc, true);
+
+ if(useStereo) {
+ buildProjView(rightEyeInImagePlate, coexistenceToRightPlate,
+ vpcToRightPlate, rightEyeInImagePlate.z - 0.05,
+ rightEyeInImagePlate.z - 1.5,
+ infRightProjection, infRightVpcToEc, true);
+
+ }
+ }
+ }
+ // TODO: The following code has never been ported
+// else {
+// Point3d cen_eye;
+//
+// // HMD case. Just concatenate the approprate matrices together.
+// // Additional work just for now
+//
+// compute_lr_plate_to_cc( &cen_eye, Fl, B, 0, &vb, 0);
+//
+// if(useStereo) {
+// mat_mul_dpt(&right_eye_pos_in_head,
+// head_to_right_plate, &cen_eye);
+// compute_lr_plate_to_cc( &cen_eye, Fr, B,
+// 1, &vb, 0);
+// }
+//
+// // Make sure that coexistence_to_plate is current.
+// // (It is usually constant for fixed plates, always varies for HMDs.)
+// // For HMD case, computes finial matrices that will be used.
+// //
+// computeCoexistenceToPlate();
+// }
+
+ }
+
+ /**
+ * Debugging routine to analyze the projection matrix.
+ */
+ private void analyzeProjection(Transform3D p, double xMax) {
+ if (viewCache.projectionPolicy == View.PARALLEL_PROJECTION)
+ System.out.println("PARALLEL_PROJECTION =");
+ else
+ System.out.println("PERSPECTIVE_PROJECTION =");
+
+ System.out.println(p);
+
+ double projectionPlaneZ = ((p.mat[0] * xMax + p.mat[3] - p.mat[15]) /
+ (p.mat[14] - p.mat[2]));
+
+ System.out.println("projection plane at z = " + projectionPlaneZ);
+ }
+
+ /**
+ * buildProjView creates a projection and viewing matrix.
+ *
+ * Inputs:
+ * ep : eye point, in plate coordinates
+ * coe2Plate : matrix from coexistence to image plate.
+ * F, B : front, back clipping planes, in plate coordinates
+ * doInfinite : flag to indicate ``at infinity'' view desired
+ *
+ * Output:
+ * vpc2Plate : matric from vpc to image plate.
+ * ecToCc : projection matrix from Eye Coordinates (EC)
+ * to Clipping Coordinates (CC)
+ * vpcToEc : view matrix from ViewPlatform Coordinates (VPC)
+ * to Eye Coordinates (EC)
+ */
+ private void buildProjView(Point3d ep,
+ Transform3D coe2Plate,
+ Transform3D vpc2Plate,
+ double F,
+ double B,
+ Transform3D ecToCc,
+ Transform3D vpcToEc,
+ boolean doInfinite) {
+
+ // Lx,Ly Hx,Hy will be adjusted window boundaries
+ double Lx, Hx, Ly, Hy;
+ Lx = physicalWindowXLeft; Hx = physicalWindowXRight;
+ Ly = physicalWindowYBottom; Hy = physicalWindowYTop;
+
+ ecToCc.setIdentity();
+
+
+ // TODO: we have no concept of glass correction in the Java 3D API
+ //
+ // Correction in apparent 3D position of window due to glass/CRT
+ // and spherical/cylinderical curvarure of CRT.
+ // This boils down to producing modified values of Lx Ly Hx Hy
+ // and is different for hot spot vs. window center corrections.
+ //
+ /* TODO:
+ double cx, cy;
+ if(viewPolicy != HMD_VIEW && enable_crt_glass_correction) {
+ if (correction_point == CORRECTION_POINT_WINDOW_CENTER) {
+ correct_crt( ep, Lx, Ly, &cx, &cy); Lx = cx; Ly = cy;
+ correct_crt( ep, Hx, Hy, &cx, &cy); Hx = cx; Hy = cy;
+ }
+ else { // must be hot spot correction
+ // Not real code yet, for now just do same as above.
+ correct_crt( ep, Lx, Ly, &cx, &cy); Lx = cx; Ly = cy;
+ correct_crt( ep, Hx, Hy, &cx, &cy); Hx = cx; Hy = cy;
+ }
+ }
+ */
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("ep = " + ep);
+ System.out.println("Lx = " + Lx + ", Hx = " + Hx);
+ System.out.println("Ly = " + Ly + ", Hy = " + Hy);
+ System.out.println("F = " + F + ", B = " + B);
+ }
+
+ // Compute the proper projection equation. Note that we
+ // do this in two steps: first we generate ImagePlateToCc,
+ // then we translate this by EcToPlate, resulting in a
+ // projection from EctoCc.
+ //
+ // A more efficient (and more accurate) approach would be to
+ // modify the equations below to directly project from EcToCc.
+
+ if (viewCache.projectionPolicy == View.PARALLEL_PROJECTION) {
+ double inv_dx, inv_dy, inv_dz;
+ inv_dx = 1.0 / (Hx - Lx);
+ inv_dy = 1.0 / (Hy - Ly);
+ inv_dz = 1.0 / (F - B);
+
+ ecToCc.mat[0] = 2.0 * inv_dx;
+ ecToCc.mat[3] = -(Hx + Lx) * inv_dx;
+ ecToCc.mat[5] = 2.0 * inv_dy;
+ ecToCc.mat[7] = -(Hy + Ly) * inv_dy;
+ ecToCc.mat[10] = 2.0 * inv_dz;
+ ecToCc.mat[11] = -(F + B) * inv_dz;
+ }
+ else {
+ double sxy, rzb, inv_dx, inv_dy;
+
+ inv_dx = 1.0 / (Hx - Lx);
+ inv_dy = 1.0 / (Hy - Ly);
+ rzb = 1.0/(ep.z - B);
+ sxy = ep.z*rzb;
+
+ ecToCc.mat[0] = sxy*2.0*inv_dx;
+ ecToCc.mat[5] = sxy*2.0*inv_dy;
+
+ ecToCc.mat[2] = rzb*(Hx+Lx - 2.0*ep.x)*inv_dx;
+ ecToCc.mat[6] = rzb*(Hy+Ly - 2.0*ep.y)*inv_dy;
+ ecToCc.mat[10] = rzb*(B+F-2*ep.z)/(B-F);
+ ecToCc.mat[14] = -rzb;
+
+ ecToCc.mat[3] = sxy*(-Hx-Lx)*inv_dx;
+ ecToCc.mat[7] = sxy*(-Hy-Ly)*inv_dy;
+ ecToCc.mat[11] = rzb*(B - ep.z - B*(B+F - 2*ep.z)/(B-F));
+ ecToCc.mat[15] = sxy;
+ }
+
+ // Since we set the matrix elements ourselves, we need to set the
+ // type field. A value of 0 means a non-affine matrix.
+ ecToCc.setOrthoDirtyBit();
+
+ // EC to ImagePlate matrix is a simple translation.
+ tVec1.set(ep.x, ep.y, ep.z);
+ tMat1.set(tVec1);
+ ecToCc.mul(tMat1);
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("ecToCc:");
+ analyzeProjection(ecToCc, Hx);
+ }
+
+ if(!doInfinite) {
+ // View matrix is:
+ // [plateToEc] [coexistence_to_plate] [vpc_to_coexistence]
+ // where vpc_to_coexistence includes the viewPlatformScale
+
+ // First compute ViewPlatform to Plate
+ vpc2Plate.mul(coe2Plate, vpcToCoexistence);
+
+ // ImagePlate to EC matrix is a simple translation.
+ tVec1.set(-ep.x, -ep.y, -ep.z);
+ tMat1.set(tVec1);
+ vpcToEc.mul(tMat1, vpc2Plate);
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ System.out.println("vpcToEc:");
+ System.out.println(vpcToEc);
+ }
+ }
+ else {
+ // Final infinite composite is:
+ // [coexistence_to_eye] [vpc_to_coexistence (vom)]
+ // (does vworld_to_coe_scale_factor get used here??? )
+ //
+ // The method is to relocate the coexistence org centered on
+ // the eye rather than the window center (via coexistence_to_eye).
+ // Computationaly simpler simplifed equation form may exist.
+
+ // coexistence to eye is a simple translation.
+/*
+ tVec1.set(ep.x, ep.y, ep.z);
+ tMat1.set(tVec1);
+ vpcToEc.mul(tMat1, vpcToCoexistence);
+ // First compute ViewPlatform to Plate
+ vpcToPlate.mul(coexistenceToPlatevpcToPlate, vpcToCoexistence);
+*/
+
+ // ImagePlate to EC matrix is a simple translation.
+ tVec1.set(-ep.x, -ep.y, -ep.z);
+ tMat1.set(tVec1);
+ tMat1.mul(tMat1, vpc2Plate);
+ tMat1.getRotation(vpcToEc); // use only rotation component of transform
+
+ }
+
+ }
+
+ /**
+ * Compute the plane equations for the frustum in ViewPlatform
+ * coordinates, plus its viewing frustum points. ccToVworld will
+ * be cached - used by Canavs3D.getInverseVworldProjection().
+ */
+ private void computeFrustumPlanes(Transform3D ecToCc,
+ Transform3D vpcToEc,
+ Vector4d [] frustumPlanes,
+ Point4d [] frustumPoints,
+ Transform3D ccToVworld) {
+
+ // Compute the inverse of the Vworld to Cc transform. This
+ // gives us the Cc to Vworld transform.
+ tMat2.mul(ecToCc, vpcToEc);
+ ccToVworld.mul(tMat2, vworldToVpc);
+ // System.out.println("ccToVworld = " + ccToVworld);
+ try {
+ ccToVworld.invert();
+ }
+ catch (SingularMatrixException e) {
+ ccToVworld.setIdentity();
+ // System.out.println("SingularMatrixException encountered when doing invert in computeFrustumPlanes");
+ }
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+ Transform3D t = new Transform3D();
+ t.mul(ecToCc, vpcToEc);
+ t.mul(vworldToVpc);
+ System.out.println("\nvworldToCc = " + t);
+ System.out.println("ccToVworld = " + ccToVworld);
+ t.mul(ccToVworld);
+ System.out.println("vworldToCc * ccToVworld = " + t);
+ }
+
+ // Transform the 8 corners of the viewing frustum into Vpc
+ frustumPoints[0].set(-1.0, -1.0, 1.0, 1.0); // lower-left-front
+ frustumPoints[1].set(-1.0, 1.0, 1.0, 1.0); // upper-left-front
+ frustumPoints[2].set( 1.0, 1.0, 1.0, 1.0); // upper-right-front
+ frustumPoints[3].set( 1.0, -1.0, 1.0, 1.0); // lower-right-front
+ frustumPoints[4].set(-1.0, -1.0, -1.0, 1.0); // lower-left-back
+ frustumPoints[5].set(-1.0, 1.0, -1.0, 1.0); // upper-left-back
+ frustumPoints[6].set( 1.0, 1.0, -1.0, 1.0); // upper-right-back
+ frustumPoints[7].set( 1.0, -1.0, -1.0, 1.0); // lower-right-back
+
+ ccToVworld.get(tMatrix);
+ int i;
+ for (i = 0; i < frustumPoints.length; i++) {
+ tMatrix.transform(frustumPoints[i]);
+ double w_inv = 1.0 / frustumPoints[i].w;
+ frustumPoints[i].x *= w_inv;
+ frustumPoints[i].y *= w_inv;
+ frustumPoints[i].z *= w_inv;
+ }
+
+ // Now compute the 6 plane equations
+ // left
+ computePlaneEq(frustumPoints[0], frustumPoints[4],
+ frustumPoints[5], frustumPoints[1],
+ frustumPlanes[0]);
+
+ // right
+ computePlaneEq(frustumPoints[3], frustumPoints[2],
+ frustumPoints[6], frustumPoints[7],
+ frustumPlanes[1]);
+
+ // top
+ computePlaneEq(frustumPoints[1], frustumPoints[5],
+ frustumPoints[6], frustumPoints[2],
+ frustumPlanes[2]);
+
+ // bottom
+ computePlaneEq(frustumPoints[0], frustumPoints[3],
+ frustumPoints[7], frustumPoints[4],
+ frustumPlanes[3]);
+
+ // front
+ computePlaneEq(frustumPoints[0], frustumPoints[1],
+ frustumPoints[2], frustumPoints[3],
+ frustumPlanes[4]);
+
+ // back
+ computePlaneEq(frustumPoints[4], frustumPoints[7],
+ frustumPoints[6], frustumPoints[5],
+ frustumPlanes[5]);
+
+ //System.out.println("left plane = " + frustumPlanes[0]);
+ //System.out.println("right plane = " + frustumPlanes[1]);
+ //System.out.println("top plane = " + frustumPlanes[2]);
+ //System.out.println("bottom plane = " + frustumPlanes[3]);
+ //System.out.println("front plane = " + frustumPlanes[4]);
+ //System.out.println("back plane = " + frustumPlanes[5]);
+ }
+
+ private void computePlaneEq(Point4d p1, Point4d p2, Point4d p3, Point4d p4,
+ Vector4d planeEq) {
+ tVec1.x = p3.x - p1.x;
+ tVec1.y = p3.y - p1.y;
+ tVec1.z = p3.z - p1.z;
+
+ tVec2.x = p2.x - p1.x;
+ tVec2.y = p2.y - p1.y;
+ tVec2.z = p2.z - p1.z;
+
+ tVec3.cross(tVec2, tVec1);
+ tVec3.normalize();
+ planeEq.x = tVec3.x;
+ planeEq.y = tVec3.y;
+ planeEq.z = tVec3.z;
+ planeEq.w = -(planeEq.x * p1.x + planeEq.y * p1.y + planeEq.z * p1.z);
+ }
+
+ // Get methods for returning derived data values.
+ // Eventually, these get functions will cause some of the parameters
+ // to be lazily evaluated.
+ //
+ // NOTE: in the case of Transform3D, and Tuple objects, a reference
+ // to the actual derived data is returned. In these cases, the caller
+ // must ensure that the returned data is not modified.
+ //
+ // NOTE: the snapshot and computeDerivedData methods are synchronized.
+ // Callers of the following methods that can run asynchronously with
+ // the renderer must call these methods and copy the data from within
+ // a synchronized block on the canvas view cache object.
+
+ int getCanvasX() {
+ return canvasX;
+ }
+
+ int getCanvasY() {
+ return canvasY;
+ }
+
+ int getCanvasWidth() {
+ return canvasWidth;
+ }
+
+ int getCanvasHeight() {
+ return canvasHeight;
+ }
+
+ double getPhysicalWindowWidth() {
+ return physicalWindowWidth;
+ }
+
+ double getPhysicalWindowHeight() {
+ return physicalWindowHeight;
+ }
+
+ boolean getUseStereo() {
+ return useStereo;
+ }
+
+ Transform3D getLeftProjection() {
+ return leftProjection;
+ }
+
+ Transform3D getRightProjection() {
+ return rightProjection;
+ }
+
+ Transform3D getLeftVpcToEc() {
+ return leftVpcToEc;
+ }
+
+ Transform3D getRightVpcToEc() {
+ return rightVpcToEc;
+ }
+
+ Transform3D getLeftEcToVpc() {
+ return leftEcToVpc;
+ }
+
+ Transform3D getRightEcToVpc() {
+ return rightEcToVpc;
+ }
+
+ Transform3D getInfLeftProjection() {
+ return infLeftProjection;
+ }
+
+ Transform3D getInfRightProjection() {
+ return infLeftProjection;
+ }
+
+ Transform3D getInfLeftVpcToEc() {
+ return infLeftVpcToEc;
+ }
+
+ Transform3D getInfRightVpcToEc() {
+ return infRightVpcToEc;
+ }
+
+ Transform3D getInfLeftEcToVpc() {
+ return infLeftEcToVpc;
+ }
+
+ Transform3D getInfgRightEcToVpc() {
+ return infRightEcToVpc;
+ }
+
+ Transform3D getInfVworldToVpc() {
+ return infVworldToVpc;
+ }
+
+ Transform3D getLeftCcToVworld() {
+ return leftCcToVworld;
+ }
+
+ Transform3D getRightCcToVworld() {
+ return rightCcToVworld;
+ }
+
+ Transform3D getImagePlateToVworld() {
+ // TODO: Document -- This will return the transform of left plate.
+ return leftPlateToVworld;
+ }
+
+
+
+ Transform3D getLastVworldToImagePlate() {
+ // TODO: Document -- This will return the transform of left plate.
+ return lastVworldToLeftPlate;
+
+ }
+
+ Transform3D getVworldToImagePlate() {
+ // TODO: Document -- This will return the transform of left plate.
+ return vworldToLeftPlate;
+ }
+
+ Transform3D getVworldToTrackerBase() {
+ return vworldToTrackerBase;
+ }
+
+ double getVworldToCoexistenceScale() {
+ return vworldToCoexistenceScale;
+ }
+
+ double getInfVworldToCoexistenceScale() {
+ return infVworldToCoexistenceScale;
+ }
+
+ Point3d getLeftEyeInImagePlate() {
+ return leftEyeInImagePlate;
+ }
+
+ Point3d getRightEyeInImagePlate() {
+ return rightEyeInImagePlate;
+ }
+
+ Point3d getCenterEyeInImagePlate() {
+ return centerEyeInImagePlate;
+ }
+
+ Transform3D getHeadToVworld() {
+ return headToVworld;
+ }
+
+ Transform3D getVpcToVworld() {
+ return vpcToVworld;
+ }
+
+ Transform3D getVworldToVpc() {
+ return vworldToVpc;
+ }
+
+
+ // Transform the specified X point in AWT window-relative coordinates
+ // to image plate coordinates
+ double getWindowXInImagePlate(double x) {
+ double xScreen = x + (double)canvasX;
+ return metersPerPixelX * xScreen;
+ }
+
+ // Transform the specified Y point in AWT window-relative coordinates
+ // to image plate coordinates
+ double getWindowYInImagePlate(double y) {
+ double yScreen = y + (double)canvasY;
+ return metersPerPixelY * ((double)(screenHeight - 1) - yScreen);
+ }
+
+ Vector4d[] getLeftFrustumPlanesInVworld() {
+ return leftFrustumPlanes;
+ }
+
+ Vector4d[] getRightFrustumPlanesInVworld() {
+ return rightFrustumPlanes;
+ }
+
+
+ void getPixelLocationInImagePlate(double x, double y, double z,
+ Point3d imagePlatePoint) {
+
+ double screenx = (x + canvasX)*metersPerPixelX;
+ double screeny = (screenHeight - 1 - canvasY - y)*metersPerPixelY;
+
+ if ((viewCache.projectionPolicy == View.PERSPECTIVE_PROJECTION) &&
+ (centerEyeInImagePlate.z != 0)) {
+ double zScale = 1.0 - z/centerEyeInImagePlate.z;
+ imagePlatePoint.x = (screenx - centerEyeInImagePlate.x)*zScale
+ + centerEyeInImagePlate.x;
+ imagePlatePoint.y = (screeny - centerEyeInImagePlate.y)*zScale
+ + centerEyeInImagePlate.y;
+ } else {
+ imagePlatePoint.x = screenx;
+ imagePlatePoint.y = screeny;
+ }
+ imagePlatePoint.z = z;
+ }
+
+ /**
+ * Projects the specified point from image plate coordinates
+ * into AWT pixel coordinates.
+ */
+ void getPixelLocationFromImagePlate(Point3d imagePlatePoint,
+ Point2d pixelLocation) {
+
+ double screenX, screenY;
+
+ if(viewCache.projectionPolicy == View.PERSPECTIVE_PROJECTION) {
+ // get the vector from centerEyeInImagePlate to imagePlatePoint
+ tVec1.sub(imagePlatePoint, centerEyeInImagePlate);
+
+ // Scale this vector to make it end at the projection plane.
+ // Scale is ratio :
+ // eye->imagePlate Plane dist / eye->imagePlatePt dist
+ // eye dist to plane is eyePos.z (eye is in +z space)
+ // image->eye dist is -tVec1.z (image->eye is in -z dir)
+ //System.out.println("eye dist = " + (centerEyeInImagePlate.z));
+ //System.out.println("image dist = " + (-tVec1.z));
+ if (tVec1.z != 0) {
+ double zScale = centerEyeInImagePlate.z / (-tVec1.z);
+ screenX = centerEyeInImagePlate.x + tVec1.x * zScale;
+ screenY = centerEyeInImagePlate.y + tVec1.y * zScale;
+
+ } else {
+ screenX = imagePlatePoint.x;
+ screenY = imagePlatePoint.y;
+ }
+
+ } else {
+ screenX = imagePlatePoint.x;
+ screenY = imagePlatePoint.y;
+ }
+
+ //System.out.println("screenX = " + screenX + " screenY = " + screenY);
+ // Note: screenPt is in image plate coords, at z=0
+
+ // Transform from image plate coords to screen coords
+ pixelLocation.x = (screenX / screenViewCache.metersPerPixelX) - canvasX;
+ pixelLocation.y = screenViewCache.screenHeight - 1 -
+ (screenY / screenViewCache.metersPerPixelY) - canvasY;
+ //System.out.println("pixelLocation = " + pixelLocation);
+ }
+
+ /**
+ * Constructs and initializes a CanvasViewCache object.
+ * Note that the canvas, screen, screenCache, view, and
+ * viewCache parameters are all fixed at construction time
+ * and must be non-null.
+ */
+ CanvasViewCache(Canvas3D canvas,
+ ScreenViewCache screenViewCache,
+ ViewCache viewCache) {
+
+ this.canvas = canvas;
+ this.screenViewCache = screenViewCache;
+ this.viewCache = viewCache;
+
+ // Set up the initial plane equations
+ int i;
+ for (i = 0; i < leftFrustumPlanes.length; i++) {
+ leftFrustumPlanes[i] = new Vector4d();
+ rightFrustumPlanes[i] = new Vector4d();
+ }
+
+ for (i = 0; i < leftFrustumPoints.length; i++) {
+ leftFrustumPoints[i] = new Point4d();
+ rightFrustumPoints[i] = new Point4d();
+ }
+
+ // canvas is null in Renderer copyOfCvCache
+ if (canvas != null) {
+ leftEyeInImagePlate.set(canvas.leftManualEyeInImagePlate);
+ rightEyeInImagePlate.set(canvas.rightManualEyeInImagePlate);
+ centerEyeInImagePlate.add(leftEyeInImagePlate,
+ rightEyeInImagePlate);
+ centerEyeInImagePlate.scale(0.5);
+ }
+
+ if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1))
+ System.out.println("Constructed a CanvasViewCache");
+ }
+
+ synchronized void setCanvas(Canvas3D c) {
+ canvas = c;
+ }
+
+ synchronized void setScreenViewCache(ScreenViewCache svc) {
+ screenViewCache = svc;
+ }
+
+ synchronized void setViewCache(ViewCache vc) {
+ viewCache = vc;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java b/src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java
new file mode 100644
index 0000000..b84af92
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java
@@ -0,0 +1,78 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * The CanvasViewEventCatcher class is used to track events on a Canvas3D that
+ * may cause view matries to change.
+ *
+ */
+class CanvasViewEventCatcher extends ComponentAdapter {
+
+ // The canvas associated with this event catcher
+ Canvas3D canvas;
+ ArrayList parentList = new ArrayList();
+ static final boolean DEBUG = false;
+
+ CanvasViewEventCatcher(Canvas3D c) {
+ canvas = c;
+ }
+
+ public void componentResized(ComponentEvent e) {
+ if (DEBUG) {
+ System.out.println("Component resized " + e);
+ }
+
+ if(e.getComponent() == canvas ) {
+ if (DEBUG) {
+ System.out.println("It is canvas!");
+ }
+ synchronized(canvas) {
+ canvas.cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+ canvas.resizeGraphics2D = true;
+ }
+
+ // see comment below
+ try {
+ canvas.newSize = canvas.getSize();
+ canvas.newPosition = canvas.getLocationOnScreen();
+ } catch (IllegalComponentStateException ex) {}
+
+ }
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ if (DEBUG) {
+ System.out.println("Component moved " + e);
+ }
+
+ synchronized(canvas) {
+ canvas.cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+ }
+ // Can't sync. with canvas lock since canvas.getLocationOnScreen()
+ // required Component lock. The order is reverse of
+ // removeNotify() lock sequence which required Component lock
+ // first, then canvas lock in removeComponentListener()
+
+ try {
+ canvas.newSize = canvas.getSize();
+ canvas.newPosition = canvas.getLocationOnScreen();
+ } catch (IllegalComponentStateException ex) {}
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/CapabilityBits.java b/src/classes/share/javax/media/j3d/CapabilityBits.java
new file mode 100644
index 0000000..db42d48
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CapabilityBits.java
@@ -0,0 +1,492 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * This CapabilityBits class provides a global namespace for all
+ * capability bits
+ */
+class CapabilityBits extends Object {
+
+ // SceneGraphObject
+
+ // Node extends SceneGraphObject
+ static final int NODE_ENABLE_COLLISION_REPORTING = 0;
+ static final int NODE_ENABLE_PICK_REPORTING = 1;
+ static final int NODE_ALLOW_PICK = 2;
+ static final int NODE_ALLOW_BOUNDS_READ = 3;
+ static final int NODE_ALLOW_BOUNDS_WRITE = 4;
+ static final int NODE_ALLOW_PICKABLE_READ = 5;
+ static final int NODE_ALLOW_PICKABLE_WRITE = 6;
+ static final int NODE_ALLOW_COLLIDABLE_READ = 7;
+ static final int NODE_ALLOW_COLLIDABLE_WRITE = 8;
+ static final int NODE_ALLOW_AUTO_COMPUTE_BOUNDS_READ = 9;
+ static final int NODE_ALLOW_AUTO_COMPUTE_BOUNDS_WRITE = 10;
+ static final int NODE_ALLOW_LOCAL_TO_VWORLD_READ = 11;
+
+
+ // Group extends Node
+ static final int GROUP_ALLOW_CHILDREN_READ = 12;
+ static final int GROUP_ALLOW_CHILDREN_WRITE = 13;
+ static final int GROUP_ALLOW_CHILDREN_EXTEND = 14;
+ static final int GROUP_ALLOW_COLLISION_BOUNDS_READ = 15;
+ static final int GROUP_ALLOW_COLLISION_BOUNDS_WRITE = 16;
+
+ // BranchGroup extends Group
+ static final int BRANCH_GROUP_ALLOW_DETACH = 17;
+
+ // SharedGroup extends Group
+ static final int SHARED_GROUP_ALLOW_LINK_READ = 17;
+
+ // TransformGroup extends Group
+ static final int TRANSFORM_GROUP_ALLOW_TRANSFORM_READ = 17;
+ static final int TRANSFORM_GROUP_ALLOW_TRANSFORM_WRITE = 18;
+
+ // Switch extends Group
+ static final int SWITCH_ALLOW_SWITCH_READ = 17;
+ static final int SWITCH_ALLOW_SWITCH_WRITE = 18;
+
+ // ViewSpecificGroup extends Group
+ static final int VIEW_SPECIFIC_GROUP_ALLOW_VIEW_READ = 17;
+ static final int VIEW_SPECIFIC_GROUP_ALLOW_VIEW_WRITE = 18;
+
+ // OrderedGroup extends Group
+ static final int ORDERED_GROUP_ALLOW_CHILD_INDEX_ORDER_READ = 17;
+ static final int ORDERED_GROUP_ALLOW_CHILD_INDEX_ORDER_WRITE = 18;
+
+
+ // Leaf extends Node
+
+ // Background extends Leaf
+ static final int BACKGROUND_ALLOW_APPLICATION_BOUNDS_READ = 12;
+ static final int BACKGROUND_ALLOW_APPLICATION_BOUNDS_WRITE = 13;
+ static final int BACKGROUND_ALLOW_IMAGE_READ = 14;
+ static final int BACKGROUND_ALLOW_IMAGE_WRITE = 15;
+ static final int BACKGROUND_ALLOW_COLOR_READ = 16;
+ static final int BACKGROUND_ALLOW_COLOR_WRITE = 17;
+ static final int BACKGROUND_ALLOW_GEOMETRY_READ = 18;
+ static final int BACKGROUND_ALLOW_GEOMETRY_WRITE = 19;
+ static final int BACKGROUND_ALLOW_IMAGE_SCALE_MODE_READ = 20;
+ static final int BACKGROUND_ALLOW_IMAGE_SCALE_MODE_WRITE = 21;
+
+ // BoundingLeaf extends Leaf
+ static final int BOUNDING_LEAF_ALLOW_REGION_READ = 12;
+ static final int BOUNDING_LEAF_ALLOW_REGION_WRITE = 13;
+
+ // Clip extends Leaf
+ static final int CLIP_ALLOW_APPLICATION_BOUNDS_READ = 12;
+ static final int CLIP_ALLOW_APPLICATION_BOUNDS_WRITE = 13;
+ static final int CLIP_ALLOW_BACK_DISTANCE_READ = 14;
+ static final int CLIP_ALLOW_BACK_DISTANCE_WRITE = 15;
+
+ // Morph extends Leaf
+ static final int MORPH_ALLOW_GEOMETRY_ARRAY_READ = 12;
+ static final int MORPH_ALLOW_GEOMETRY_ARRAY_WRITE = 13;
+ static final int MORPH_ALLOW_APPEARANCE_READ = 14;
+ static final int MORPH_ALLOW_APPEARANCE_WRITE = 15;
+ static final int MORPH_ALLOW_WEIGHTS_READ = 16;
+ static final int MORPH_ALLOW_WEIGHTS_WRITE = 17;
+ static final int MORPH_ALLOW_COLLISION_BOUNDS_READ = 18;
+ static final int MORPH_ALLOW_COLLISION_BOUNDS_WRITE = 19;
+ static final int MORPH_ALLOW_APPEARANCE_OVERRIDE_READ = 20;
+ static final int MORPH_ALLOW_APPEARANCE_OVERRIDE_WRITE = 21;
+
+ // Link extends Leaf
+ static final int LINK_ALLOW_SHARED_GROUP_READ = 12;
+ static final int LINK_ALLOW_SHARED_GROUP_WRITE = 13;
+
+ // Shape3D extends Leaf
+ static final int SHAPE3D_ALLOW_GEOMETRY_READ = 12;
+ static final int SHAPE3D_ALLOW_GEOMETRY_WRITE = 13;
+ static final int SHAPE3D_ALLOW_APPEARANCE_READ = 14;
+ static final int SHAPE3D_ALLOW_APPEARANCE_WRITE = 15;
+ static final int SHAPE3D_ALLOW_COLLISION_BOUNDS_READ = 16;
+ static final int SHAPE3D_ALLOW_COLLISION_BOUNDS_WRITE = 17;
+ static final int SHAPE3D_ALLOW_APPEARANCE_OVERRIDE_READ = 18;
+ static final int SHAPE3D_ALLOW_APPEARANCE_OVERRIDE_WRITE = 19;
+
+ // OrientedShape3D extends Shape3D
+ static final int ORIENTED_SHAPE3D_ALLOW_MODE_READ = 20;
+ static final int ORIENTED_SHAPE3D_ALLOW_MODE_WRITE = 21;
+ static final int ORIENTED_SHAPE3D_ALLOW_AXIS_READ = 22;
+ static final int ORIENTED_SHAPE3D_ALLOW_AXIS_WRITE = 23;
+ static final int ORIENTED_SHAPE3D_ALLOW_POINT_READ = 24;
+ static final int ORIENTED_SHAPE3D_ALLOW_POINT_WRITE = 25;
+ static final int ORIENTED_SHAPE3D_ALLOW_SCALE_READ = 26;
+ static final int ORIENTED_SHAPE3D_ALLOW_SCALE_WRITE = 27;
+
+ // Soundscape extends Leaf
+ static final int SOUNDSCAPE_ALLOW_APPLICATION_BOUNDS_READ = 12;
+ static final int SOUNDSCAPE_ALLOW_APPLICATION_BOUNDS_WRITE = 13;
+ static final int SOUNDSCAPE_ALLOW_ATTRIBUTES_READ = 14;
+ static final int SOUNDSCAPE_ALLOW_ATTRIBUTES_WRITE = 15;
+
+ // ViewPlatform extends Leaf
+ static final int VIEW_PLATFORM_ALLOW_POLICY_READ = 12;
+ static final int VIEW_PLATFORM_ALLOW_POLICY_WRITE = 13;
+
+ // Fog extends Leaf
+ static final int FOG_ALLOW_INFLUENCING_BOUNDS_READ = 12;
+ static final int FOG_ALLOW_INFLUENCING_BOUNDS_WRITE = 13;
+ static final int FOG_ALLOW_COLOR_READ = 14;
+ static final int FOG_ALLOW_COLOR_WRITE = 15;
+
+ // ExponentialFog extends Fog
+ static final int EXPONENTIAL_FOG_ALLOW_DENSITY_READ = 16;
+ static final int EXPONENTIAL_FOG_ALLOW_DENSITY_WRITE = 17;
+
+ // LinearFog extends Fog
+ static final int LINEAR_FOG_ALLOW_DISTANCE_READ = 16;
+ static final int LINEAR_FOG_ALLOW_DISTANCE_WRITE = 17;
+
+ // Additional Fog bits (must go after LinearFog bits)
+ static final int FOG_ALLOW_SCOPE_READ = 18;
+ static final int FOG_ALLOW_SCOPE_WRITE = 19;
+
+ // Light extends Leaf
+ static final int LIGHT_ALLOW_STATE_READ = 12;
+ static final int LIGHT_ALLOW_STATE_WRITE = 13;
+ static final int LIGHT_ALLOW_COLOR_READ = 14;
+ static final int LIGHT_ALLOW_COLOR_WRITE = 15;
+ static final int LIGHT_ALLOW_INFLUENCING_BOUNDS_READ = 16;
+ static final int LIGHT_ALLOW_INFLUENCING_BOUNDS_WRITE = 17;
+
+ // DirectionalLight extends Light
+ static final int DIRECTIONAL_LIGHT_ALLOW_DIRECTION_READ = 18;
+ static final int DIRECTIONAL_LIGHT_ALLOW_DIRECTION_WRITE = 19;
+
+ // PointLight extends Light
+ static final int POINT_LIGHT_ALLOW_POSITION_READ = 18;
+ static final int POINT_LIGHT_ALLOW_POSITION_WRITE = 19;
+ static final int POINT_LIGHT_ALLOW_ATTENUATION_READ = 20;
+ static final int POINT_LIGHT_ALLOW_ATTENUATION_WRITE = 21;
+
+ // SpotLight extends PointLight
+ static final int SPOT_LIGHT_ALLOW_SPREAD_ANGLE_WRITE = 22;
+ static final int SPOT_LIGHT_ALLOW_SPREAD_ANGLE_READ = 23;
+ static final int SPOT_LIGHT_ALLOW_CONCENTRATION_WRITE = 24;
+ static final int SPOT_LIGHT_ALLOW_CONCENTRATION_READ = 25;
+ static final int SPOT_LIGHT_ALLOW_DIRECTION_WRITE = 26;
+ static final int SPOT_LIGHT_ALLOW_DIRECTION_READ = 27;
+
+ // Additional Light bits (must go after SpotLight bits)
+ static final int LIGHT_ALLOW_SCOPE_READ = 28;
+ static final int LIGHT_ALLOW_SCOPE_WRITE = 29;
+
+ // Sound extends Leaf
+ static final int SOUND_ALLOW_SOUND_DATA_READ = 12;
+ static final int SOUND_ALLOW_SOUND_DATA_WRITE = 13;
+ static final int SOUND_ALLOW_INITIAL_GAIN_READ = 14;
+ static final int SOUND_ALLOW_INITIAL_GAIN_WRITE = 15;
+ static final int SOUND_ALLOW_LOOP_READ = 16;
+ static final int SOUND_ALLOW_LOOP_WRITE = 17;
+ static final int SOUND_ALLOW_RELEASE_READ = 18;
+ static final int SOUND_ALLOW_RELEASE_WRITE = 19;
+ static final int SOUND_ALLOW_CONT_PLAY_READ = 20;
+ static final int SOUND_ALLOW_CONT_PLAY_WRITE = 21;
+ static final int SOUND_ALLOW_ENABLE_READ = 22;
+ static final int SOUND_ALLOW_ENABLE_WRITE = 23;
+ static final int SOUND_ALLOW_SCHEDULING_BOUNDS_READ = 24;
+ static final int SOUND_ALLOW_SCHEDULING_BOUNDS_WRITE = 25;
+ static final int SOUND_ALLOW_PRIORITY_READ = 26;
+ static final int SOUND_ALLOW_PRIORITY_WRITE = 27;
+ static final int SOUND_ALLOW_DURATION_READ = 28;
+ static final int SOUND_ALLOW_IS_READY_READ = 29;
+ static final int SOUND_ALLOW_IS_PLAYING_READ = 30;
+ static final int SOUND_ALLOW_CHANNELS_USED_READ = 31;
+ static final int SOUND_ALLOW_MUTE_READ = 40;
+ static final int SOUND_ALLOW_MUTE_WRITE = 41;
+ static final int SOUND_ALLOW_PAUSE_READ = 42;
+ static final int SOUND_ALLOW_PAUSE_WRITE = 43;
+ static final int SOUND_ALLOW_RATE_SCALE_FACTOR_READ = 44;
+ static final int SOUND_ALLOW_RATE_SCALE_FACTOR_WRITE = 45;
+
+ // PointSound extends Sound
+ static final int POINT_SOUND_ALLOW_POSITION_READ = 32;
+ static final int POINT_SOUND_ALLOW_POSITION_WRITE = 33;
+ static final int POINT_SOUND_ALLOW_DISTANCE_GAIN_READ = 34;
+ static final int POINT_SOUND_ALLOW_DISTANCE_GAIN_WRITE = 35;
+
+ // ConeSound extends PointSound
+ static final int CONE_SOUND_ALLOW_DIRECTION_READ = 36;
+ static final int CONE_SOUND_ALLOW_DIRECTION_WRITE = 37;
+ static final int CONE_SOUND_ALLOW_ANGULAR_ATTENUATION_READ = 38;
+ static final int CONE_SOUND_ALLOW_ANGULAR_ATTENUATION_WRITE = 39;
+
+ // ModelClip extends Leaf
+ static final int MODEL_CLIP_ALLOW_INFLUENCING_BOUNDS_READ = 12;
+ static final int MODEL_CLIP_ALLOW_INFLUENCING_BOUNDS_WRITE = 13;
+ static final int MODEL_CLIP_ALLOW_PLANE_READ = 14;
+ static final int MODEL_CLIP_ALLOW_PLANE_WRITE = 15;
+ static final int MODEL_CLIP_ALLOW_ENABLE_READ = 16;
+ static final int MODEL_CLIP_ALLOW_ENABLE_WRITE = 17;
+ static final int MODEL_CLIP_ALLOW_SCOPE_READ = 18;
+ static final int MODEL_CLIP_ALLOW_SCOPE_WRITE = 19;
+
+ // AlternateAppearance extends Leaf
+ static final int ALTERNATE_APPEARANCE_ALLOW_INFLUENCING_BOUNDS_READ = 12;
+ static final int ALTERNATE_APPEARANCE_ALLOW_INFLUENCING_BOUNDS_WRITE = 13;
+ static final int ALTERNATE_APPEARANCE_ALLOW_APPEARANCE_READ = 14;
+ static final int ALTERNATE_APPEARANCE_ALLOW_APPEARANCE_WRITE = 15;
+ static final int ALTERNATE_APPEARANCE_ALLOW_SCOPE_READ = 16;
+ static final int ALTERNATE_APPEARANCE_ALLOW_SCOPE_WRITE = 17;
+
+ // NodeComponent extends SceneGraphObject
+
+ // Appearance extends NodeComponent
+ static final int APPEARANCE_ALLOW_MATERIAL_READ = 0;
+ static final int APPEARANCE_ALLOW_MATERIAL_WRITE = 1;
+ static final int APPEARANCE_ALLOW_TEXTURE_READ = 2;
+ static final int APPEARANCE_ALLOW_TEXTURE_WRITE = 3;
+ static final int APPEARANCE_ALLOW_TEXGEN_READ = 4;
+ static final int APPEARANCE_ALLOW_TEXGEN_WRITE = 5;
+ static final int APPEARANCE_ALLOW_TEXTURE_ATTRIBUTES_READ = 6;
+ static final int APPEARANCE_ALLOW_TEXTURE_ATTRIBUTES_WRITE = 7;
+ static final int APPEARANCE_ALLOW_COLORING_ATTRIBUTES_READ = 8;
+ static final int APPEARANCE_ALLOW_COLORING_ATTRIBUTES_WRITE = 9;
+ static final int APPEARANCE_ALLOW_TRANSPARENCY_ATTRIBUTES_READ = 10;
+ static final int APPEARANCE_ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE = 11;
+ static final int APPEARANCE_ALLOW_RENDERING_ATTRIBUTES_READ = 12;
+ static final int APPEARANCE_ALLOW_RENDERING_ATTRIBUTES_WRITE = 13;
+ static final int APPEARANCE_ALLOW_POLYGON_ATTRIBUTES_READ = 14;
+ static final int APPEARANCE_ALLOW_POLYGON_ATTRIBUTES_WRITE = 15;
+ static final int APPEARANCE_ALLOW_LINE_ATTRIBUTES_READ = 16;
+ static final int APPEARANCE_ALLOW_LINE_ATTRIBUTES_WRITE = 17;
+ static final int APPEARANCE_ALLOW_POINT_ATTRIBUTES_READ = 18;
+ static final int APPEARANCE_ALLOW_POINT_ATTRIBUTES_WRITE = 19;
+ static final int APPEARANCE_ALLOW_TEXTURE_UNIT_STATE_READ = 20;
+ static final int APPEARANCE_ALLOW_TEXTURE_UNIT_STATE_WRITE = 21;
+
+ // AuralAttributes extends NodeComponent
+ static final int AURAL_ATTRIBUTES_ALLOW_ATTRIBUTE_GAIN_READ = 0;
+ static final int AURAL_ATTRIBUTES_ALLOW_ATTRIBUTE_GAIN_WRITE = 1;
+ static final int AURAL_ATTRIBUTES_ALLOW_ROLLOFF_READ = 2;
+ static final int AURAL_ATTRIBUTES_ALLOW_ROLLOFF_WRITE = 3;
+ static final int AURAL_ATTRIBUTES_ALLOW_REFLECTION_COEFFICIENT_READ = 4;
+ static final int AURAL_ATTRIBUTES_ALLOW_REFLECTION_COEFFICIENT_WRITE = 5;
+ static final int AURAL_ATTRIBUTES_ALLOW_REVERB_DELAY_READ = 6;
+ static final int AURAL_ATTRIBUTES_ALLOW_REVERB_DELAY_WRITE = 7;
+ static final int AURAL_ATTRIBUTES_ALLOW_REVERB_ORDER_READ = 8;
+ static final int AURAL_ATTRIBUTES_ALLOW_REVERB_ORDER_WRITE = 9;
+ static final int AURAL_ATTRIBUTES_ALLOW_DISTANCE_FILTER_READ = 10;
+ static final int AURAL_ATTRIBUTES_ALLOW_DISTANCE_FILTER_WRITE = 11;
+ static final int AURAL_ATTRIBUTES_ALLOW_FREQUENCY_SCALE_FACTOR_READ = 12;
+ static final int AURAL_ATTRIBUTES_ALLOW_FREQUENCY_SCALE_FACTOR_WRITE = 13;
+ static final int AURAL_ATTRIBUTES_ALLOW_VELOCITY_SCALE_FACTOR_READ = 14;
+ static final int AURAL_ATTRIBUTES_ALLOW_VELOCITY_SCALE_FACTOR_WRITE = 15;
+ static final int AURAL_ATTRIBUTES_ALLOW_REFLECTION_DELAY_READ = 16;
+ static final int AURAL_ATTRIBUTES_ALLOW_REFLECTION_DELAY_WRITE = 17;
+ static final int AURAL_ATTRIBUTES_ALLOW_REVERB_COEFFICIENT_READ = 18;
+ static final int AURAL_ATTRIBUTES_ALLOW_REVERB_COEFFICIENT_WRITE = 19;
+ static final int AURAL_ATTRIBUTES_ALLOW_DECAY_TIME_READ = 20;
+ static final int AURAL_ATTRIBUTES_ALLOW_DECAY_TIME_WRITE = 21;
+ static final int AURAL_ATTRIBUTES_ALLOW_DECAY_FILTER_READ = 22;
+ static final int AURAL_ATTRIBUTES_ALLOW_DECAY_FILTER_WRITE = 23;
+ static final int AURAL_ATTRIBUTES_ALLOW_DIFFUSION_READ = 24;
+ static final int AURAL_ATTRIBUTES_ALLOW_DIFFUSION_WRITE = 25;
+ static final int AURAL_ATTRIBUTES_ALLOW_DENSITY_READ = 26;
+ static final int AURAL_ATTRIBUTES_ALLOW_DENSITY_WRITE = 27;
+
+ // ColoringAttributes extends NodeComponent
+ static final int COLORING_ATTRIBUTES_ALLOW_COLOR_READ = 0;
+ static final int COLORING_ATTRIBUTES_ALLOW_COLOR_WRITE = 1;
+ static final int COLORING_ATTRIBUTES_ALLOW_SHADE_MODEL_READ = 2;
+ static final int COLORING_ATTRIBUTES_ALLOW_SHADE_MODEL_WRITE = 3;
+
+ // DepthComponent extends NodeComponent
+ static final int DEPTH_COMPONENT_ALLOW_SIZE_READ = 0;
+ static final int DEPTH_COMPONENT_ALLOW_DATA_READ = 1;
+
+ // ImageComponent extends NodeComponent
+ static final int IMAGE_COMPONENT_ALLOW_SIZE_READ = 0;
+ static final int IMAGE_COMPONENT_ALLOW_FORMAT_READ = 1;
+ static final int IMAGE_COMPONENT_ALLOW_IMAGE_READ = 2;
+ static final int IMAGE_COMPONENT_ALLOW_IMAGE_WRITE = 3;
+
+ // LineAttributes extends NodeComponent
+ static final int LINE_ATTRIBUTES_ALLOW_WIDTH_READ = 0;
+ static final int LINE_ATTRIBUTES_ALLOW_WIDTH_WRITE = 1;
+ static final int LINE_ATTRIBUTES_ALLOW_PATTERN_READ = 2;
+ static final int LINE_ATTRIBUTES_ALLOW_PATTERN_WRITE = 3;
+ static final int LINE_ATTRIBUTES_ALLOW_ANTIALIASING_READ = 4;
+ static final int LINE_ATTRIBUTES_ALLOW_ANTIALIASING_WRITE = 5;
+
+ // Material extends NodeComponent
+ static final int MATERIAL_ALLOW_COMPONENT_READ = 0;
+ static final int MATERIAL_ALLOW_COMPONENT_WRITE = 1;
+
+ // MediaContainer extends NodeComponent
+ static final int MEDIA_CONTAINER_ALLOW_CACHE_READ = 0;
+ static final int MEDIA_CONTAINER_ALLOW_CACHE_WRITE = 1;
+ static final int MEDIA_CONTAINER_ALLOW_URL_READ = 2;
+ static final int MEDIA_CONTAINER_ALLOW_URL_WRITE = 3;
+
+ // PointAttributes extends NodeComponent
+ static final int POINT_ATTRIBUTES_ALLOW_SIZE_READ = 0;
+ static final int POINT_ATTRIBUTES_ALLOW_SIZE_WRITE = 1;
+ static final int POINT_ATTRIBUTES_ALLOW_ANTIALIASING_READ = 2;
+ static final int POINT_ATTRIBUTES_ALLOW_ANTIALIASING_WRITE = 3;
+
+ // PolygonAttributes extends NodeComponent
+ static final int POLYGON_ATTRIBUTES_ALLOW_CULL_FACE_READ = 0;
+ static final int POLYGON_ATTRIBUTES_ALLOW_CULL_FACE_WRITE = 1;
+ static final int POLYGON_ATTRIBUTES_ALLOW_MODE_READ = 2;
+ static final int POLYGON_ATTRIBUTES_ALLOW_MODE_WRITE = 3;
+ static final int POLYGON_ATTRIBUTES_ALLOW_OFFSET_READ = 4;
+ static final int POLYGON_ATTRIBUTES_ALLOW_OFFSET_WRITE = 5;
+ static final int POLYGON_ATTRIBUTES_ALLOW_NORMAL_FLIP_READ = 6;
+ static final int POLYGON_ATTRIBUTES_ALLOW_NORMAL_FLIP_WRITE = 7;
+
+ // RenderingAttributes extends NodeComponent
+ static final int RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_VALUE_READ = 0;
+ static final int RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_VALUE_WRITE = 1;
+ static final int RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_FUNCTION_READ = 2;
+ static final int RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_FUNCTION_WRITE = 3;
+ static final int RENDERING_ATTRIBUTES_ALLOW_DEPTH_ENABLE_READ = 4;
+ static final int RENDERING_ATTRIBUTES_ALLOW_VISIBLE_READ = 5;
+ static final int RENDERING_ATTRIBUTES_ALLOW_VISIBLE_WRITE = 6;
+ static final int RENDERING_ATTRIBUTES_ALLOW_RASTER_OP_READ = 7;
+ static final int RENDERING_ATTRIBUTES_ALLOW_RASTER_OP_WRITE = 8;
+ static final int
+ RENDERING_ATTRIBUTES_ALLOW_IGNORE_VERTEX_COLORS_READ = 9;
+ static final int
+ RENDERING_ATTRIBUTES_ALLOW_IGNORE_VERTEX_COLORS_WRITE = 10;
+ static final int RENDERING_ATTRIBUTES_ALLOW_DEPTH_ENABLE_WRITE = 11;
+
+ // TexCoordGeneration extends NodeComponent
+ static final int TEX_COORD_GENERATION_ALLOW_ENABLE_READ = 0;
+ static final int TEX_COORD_GENERATION_ALLOW_ENABLE_WRITE = 1;
+ static final int TEX_COORD_GENERATION_ALLOW_FORMAT_READ = 2;
+ static final int TEX_COORD_GENERATION_ALLOW_MODE_READ = 3;
+ static final int TEX_COORD_GENERATION_ALLOW_PLANE_READ = 4;
+ static final int TEX_COORD_GENERATION_ALLOW_PLANE_WRITE = 5;
+
+ // Texture extends NodeComponent
+ static final int TEXTURE_ALLOW_ENABLE_READ = 0;
+ static final int TEXTURE_ALLOW_ENABLE_WRITE = 1;
+ static final int TEXTURE_ALLOW_BOUNDARY_MODE_READ = 2;
+ static final int TEXTURE_ALLOW_FILTER_READ = 3;
+ static final int TEXTURE_ALLOW_IMAGE_READ = 4;
+ static final int TEXTURE_ALLOW_MIPMAP_MODE_READ = 5;
+ static final int TEXTURE_ALLOW_BOUNDARY_COLOR_READ = 6;
+ static final int TEXTURE_ALLOW_IMAGE_WRITE = 7;
+ static final int TEXTURE_ALLOW_SIZE_READ = 8;
+ static final int TEXTURE_ALLOW_FORMAT_READ = 9;
+ static final int TEXTURE_ALLOW_LOD_RANGE_READ = 10;
+ static final int TEXTURE_ALLOW_LOD_RANGE_WRITE = 11;
+ static final int TEXTURE_ALLOW_ANISOTROPIC_FILTER_READ = 12;
+ static final int TEXTURE_ALLOW_SHARPEN_TEXTURE_READ = 13;
+ static final int TEXTURE_ALLOW_FILTER4_READ = 14;
+
+ // Texture2D extends Texture
+ static final int TEXTURE2D_ALLOW_DETAIL_TEXTURE_READ = 15;
+
+ // TextureAttributes extends NodeComponent
+ static final int TEXTURE_ATTRIBUTES_ALLOW_MODE_READ = 0;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_MODE_WRITE = 1;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_BLEND_COLOR_READ = 2;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_BLEND_COLOR_WRITE = 3;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_TRANSFORM_READ = 4;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_TRANSFORM_WRITE = 5;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_COLOR_TABLE_READ = 6;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_COLOR_TABLE_WRITE = 7;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_COMBINE_READ = 8;
+ static final int TEXTURE_ATTRIBUTES_ALLOW_COMBINE_WRITE = 9;
+
+ // TransparencyAttributes extends NodeComponent
+ static final int TRANSPARENCY_ATTRIBUTES_ALLOW_MODE_READ = 0;
+ static final int TRANSPARENCY_ATTRIBUTES_ALLOW_MODE_WRITE = 1;
+ static final int TRANSPARENCY_ATTRIBUTES_ALLOW_VALUE_READ = 2;
+ static final int TRANSPARENCY_ATTRIBUTES_ALLOW_VALUE_WRITE = 3;
+ static final int TRANSPARENCY_ATTRIBUTES_ALLOW_BLEND_FUNCTION_READ = 4;
+ static final int TRANSPARENCY_ATTRIBUTES_ALLOW_BLEND_FUNCTION_WRITE = 5;
+
+ // TextureUnitState extends NodeComponent
+ static final int TEXTURE_UNIT_STATE_ALLOW_STATE_READ = 0;
+ static final int TEXTURE_UNIT_STATE_ALLOW_STATE_WRITE = 1;
+
+ // Geometry extends NodeComponent
+ // NOTE: additional bits are below the subclasses
+
+ // GeometryArray extends Geometry
+ static final int GEOMETRY_ARRAY_ALLOW_COORDINATE_READ = 0;
+ static final int GEOMETRY_ARRAY_ALLOW_COORDINATE_WRITE = 1;
+ static final int GEOMETRY_ARRAY_ALLOW_COLOR_READ = 2;
+ static final int GEOMETRY_ARRAY_ALLOW_COLOR_WRITE = 3;
+ static final int GEOMETRY_ARRAY_ALLOW_NORMAL_READ = 4;
+ static final int GEOMETRY_ARRAY_ALLOW_NORMAL_WRITE = 5;
+ static final int GEOMETRY_ARRAY_ALLOW_TEXCOORD_READ = 6;
+ static final int GEOMETRY_ARRAY_ALLOW_TEXCOORD_WRITE = 7;
+ static final int GEOMETRY_ARRAY_ALLOW_COUNT_READ = 8;
+
+ // IndexedGeometryArray extends GeometryArray
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_COORDINATE_INDEX_READ = 9;
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_COORDINATE_INDEX_WRITE= 10;
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_COLOR_INDEX_READ = 11;
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_COLOR_INDEX_WRITE = 12;
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_NORMAL_INDEX_READ = 13;
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_NORMAL_INDEX_WRITE = 14;
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_TEXCOORD_INDEX_READ = 15;
+ static final int INDEXED_GEOMETRY_ARRAY_ALLOW_TEXCOORD_INDEX_WRITE = 16;
+
+ // Additional GeometryArray bits (must go after IndexedGeometryArray bits)
+ static final int GEOMETRY_ARRAY_ALLOW_FORMAT_READ = 17;
+ static final int J3D_1_2_GEOMETRY_ARRAY_ALLOW_REF_DATA_READ = 18;
+ static final int GEOMETRY_ARRAY_ALLOW_REF_DATA_WRITE = 19;
+ static final int GEOMETRY_ARRAY_ALLOW_COUNT_WRITE = 20;
+ static final int GEOMETRY_ARRAY_ALLOW_REF_DATA_READ = 21;
+
+ // CompressedGeometry extends Geometry
+ static final int COMPRESSED_GEOMETRY_ALLOW_COUNT_READ = 0;
+ static final int COMPRESSED_GEOMETRY_ALLOW_HEADER_READ = 1;
+ static final int COMPRESSED_GEOMETRY_ALLOW_GEOMETRY_READ = 2;
+ static final int COMPRESSED_GEOMETRY_ALLOW_REF_DATA_READ = 3;
+
+ // Raster extends Geometry
+ static final int RASTER_ALLOW_POSITION_READ = 0;
+ static final int RASTER_ALLOW_POSITION_WRITE = 1;
+ static final int RASTER_ALLOW_OFFSET_READ = 2;
+ static final int RASTER_ALLOW_OFFSET_WRITE = 3;
+ static final int RASTER_ALLOW_IMAGE_READ = 4;
+ static final int RASTER_ALLOW_IMAGE_WRITE = 5;
+ static final int RASTER_ALLOW_DEPTH_COMPONENT_READ = 6;
+ static final int RASTER_ALLOW_DEPTH_COMPONENT_WRITE = 7;
+ static final int RASTER_ALLOW_SIZE_READ = 8;
+ static final int RASTER_ALLOW_SIZE_WRITE = 9;
+ static final int RASTER_ALLOW_TYPE_READ = 10;
+ static final int RASTER_ALLOW_CLIP_MODE_READ = 11;
+ static final int RASTER_ALLOW_CLIP_MODE_WRITE = 12;
+
+ // Text3D extends Geometry
+ static final int TEXT3D_ALLOW_FONT3D_READ = 0;
+ static final int TEXT3D_ALLOW_FONT3D_WRITE = 1;
+ static final int TEXT3D_ALLOW_STRING_READ = 2;
+ static final int TEXT3D_ALLOW_STRING_WRITE = 3;
+ static final int TEXT3D_ALLOW_POSITION_READ = 4;
+ static final int TEXT3D_ALLOW_POSITION_WRITE = 5;
+ static final int TEXT3D_ALLOW_ALIGNMENT_READ = 6;
+ static final int TEXT3D_ALLOW_ALIGNMENT_WRITE = 7;
+ static final int TEXT3D_ALLOW_PATH_READ = 8;
+ static final int TEXT3D_ALLOW_PATH_WRITE = 9;
+ static final int TEXT3D_ALLOW_CHARACTER_SPACING_READ = 10;
+ static final int TEXT3D_ALLOW_CHARACTER_SPACING_WRITE = 11;
+ static final int TEXT3D_ALLOW_BOUNDING_BOX_READ = 12;
+
+ // Additional geometry bits (must go after GeometryArray bits)
+ // NOTE: ALLOW_INTERSECT was duplicated by the old value of
+ // ALLOW_REF_DATA_READ in Java 3D 1.2.
+ static final int GEOMETRY_ALLOW_INTERSECT = 18;
+
+ // NOTE: any further additional Geometry bits must come after the
+ // last GeometryArray bit
+}
diff --git a/src/classes/share/javax/media/j3d/CapabilityNotSetException.java b/src/classes/share/javax/media/j3d/CapabilityNotSetException.java
new file mode 100644
index 0000000..6872657
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CapabilityNotSetException.java
@@ -0,0 +1,37 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates an access to a live or
+ * compiled Scene Graph object without the required capability
+ * set.
+ */
+public class CapabilityNotSetException extends RestrictedAccessException {
+
+/**
+ * Create the exception object with default values.
+ */
+ public CapabilityNotSetException(){
+ }
+
+/**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public CapabilityNotSetException(String str){
+
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Clip.java b/src/classes/share/javax/media/j3d/Clip.java
new file mode 100644
index 0000000..143a200
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Clip.java
@@ -0,0 +1,284 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The Clip leaf node defines the back, or far, clip distance in
+ * the virtual universe.
+ * The distance is specified in the local coordinate system of this node.
+ * This node also specifies an application
+ * region in which this clip node is active.
+ * A Clip node is active when its application region intersects
+ * the ViewPlatform's activation volume. If multiple Clip nodes
+ * are active, the Clip node that is "closest" to the eye will be
+ * used.
+ * If no clip node is in scope of the view platform
+ * associated with the current view, then the back clip distance is
+ * defined by the View object.
+ * The front clip distance is always defined by the
+ * View object.
+ *
+ * @see View
+ */
+public class Clip extends Leaf {
+
+ /**
+ * Specifies that the Clip allows read access to its application
+ * bounds and bounding leaf at runtime.
+ */
+ public static final int
+ ALLOW_APPLICATION_BOUNDS_READ = CapabilityBits.CLIP_ALLOW_APPLICATION_BOUNDS_READ;
+
+ /**
+ * Specifies that the Clip allows write access to its application
+ * bounds and bounding leaf at runtime.
+ */
+ public static final int
+ ALLOW_APPLICATION_BOUNDS_WRITE = CapabilityBits.CLIP_ALLOW_APPLICATION_BOUNDS_WRITE;
+
+ /**
+ * Specifies that the Clip allows read access to its back distance
+ * at runtime.
+ */
+ public static final int
+ ALLOW_BACK_DISTANCE_READ = CapabilityBits.CLIP_ALLOW_BACK_DISTANCE_READ;
+
+ /**
+ * Specifies that the Clip allows write access to its back distance
+ * at runtime.
+ */
+ public static final int
+ ALLOW_BACK_DISTANCE_WRITE = CapabilityBits.CLIP_ALLOW_BACK_DISTANCE_WRITE;
+
+ /**
+ * Constructs a Clip node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * back clip distance : 100 meters<sr>
+ * application bounds : null<br>
+ * application bounding leaf : null<br>
+ * </ul>
+ */
+ public Clip () {
+ // Just use the defaults
+ }
+
+ /**
+ * Constructs a Clip node with the specified back clip distance.
+ */
+ public Clip(double backDistance) {
+ ((ClipRetained)this.retained).initBackDistance(backDistance);
+ }
+
+ /**
+ * Sets the back clip distance to the specified value.
+ * There are several considerations that need to be taken into
+ * account when choosing values for the front and back clip
+ * distances. These are enumerated in the description of
+ * <a href=View.html#setFrontClipDistance(double)>
+ * View.setFrontClipDistance</a>.
+ * @param backDistance the new back clip distance in meters
+ * @see View#setFrontClipDistance
+ * @see View#setBackClipDistance
+ */
+ public void setBackDistance(double backDistance) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_BACK_DISTANCE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Clip0"));
+
+ if (isLive())
+ ((ClipRetained)this.retained).setBackDistance(backDistance);
+ else
+ ((ClipRetained)this.retained).initBackDistance(backDistance);
+ }
+
+ /**
+ * Retrieves the back clip distance.
+ * @return the current back clip distance, in meters
+ */
+ public double getBackDistance() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_BACK_DISTANCE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Clip1"));
+ return ((ClipRetained)this.retained).getBackDistance();
+ }
+
+ /**
+ * Set the Clip's application region to the specified bounds.
+ * This is used when the application bounding leaf is set to null.
+ * @param region the bounds that contains the Clip's new application
+ * region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setApplicationBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Clip2"));
+
+ if (isLive())
+ ((ClipRetained)this.retained).setApplicationBounds(region);
+ else
+ ((ClipRetained)this.retained).initApplicationBounds(region);
+ }
+
+ /**
+ * Retrieves the Clip node's application bounds.
+ * @return this Clip's application bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getApplicationBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Clip3"));
+
+ return ((ClipRetained)this.retained).getApplicationBounds();
+ }
+
+ /**
+ * Set the Clip's application region to the specified bounding leaf.
+ * When set to a value other than null, this overrides the application
+ * bounds object.
+ * @param region the bounding leaf node used to specify the Clip
+ * node's new application region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setApplicationBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Clip2"));
+
+ if (isLive())
+ ((ClipRetained)this.retained).setApplicationBoundingLeaf(region);
+ else
+ ((ClipRetained)this.retained).initApplicationBoundingLeaf(region);
+ }
+
+ /**
+ * Retrieves the Clip node's application bounding leaf.
+ * @return this Clip's application bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getApplicationBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Clip3"));
+
+ return ((ClipRetained)this.retained).getApplicationBoundingLeaf();
+ }
+
+ /**
+ * Creates the retained mode ClipRetained object that this
+ * Clip component object will point to.
+ */
+ void createRetained() {
+ this.retained = new ClipRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * 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) {
+ Clip c = new Clip();
+ c.duplicateNode(this, forceDuplicate);
+ return c;
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ ClipRetained rt = (ClipRetained) retained;
+ BoundingLeaf bl = rt.getApplicationBoundingLeaf();
+
+ // check for applicationBoundingLeaf
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.initApplicationBoundingLeaf((BoundingLeaf) o);
+ }
+ }
+
+
+ /**
+ * Copies all Clip 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ClipRetained attr = (ClipRetained) originalNode.retained;
+ ClipRetained rt = (ClipRetained) retained;
+
+ rt.initBackDistance(attr.getBackDistance());
+ rt.initApplicationBounds(attr.getApplicationBounds());
+
+ // correct value will set in updateNodeReferences
+ rt.initApplicationBoundingLeaf(attr.getApplicationBoundingLeaf());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ClipRetained.java b/src/classes/share/javax/media/j3d/ClipRetained.java
new file mode 100644
index 0000000..b4e7fcc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ClipRetained.java
@@ -0,0 +1,384 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+/**
+ * The Clip leaf node defines the back, or far, clipping distance in
+ * the virtual universe. The front clipping plane is defined in the
+ * View object. If no clip node is in scope of the view platform
+ * associated with the current view, then the back clipping plane is
+ * also defined by the View.
+ * @see View
+ */
+class ClipRetained extends LeafRetained {
+
+ static final int BOUNDS_CHANGED = 0x00001;
+ static final int BOUNDINGLEAF_CHANGED = 0x00002;
+ static final int BACKDISTANCE_CHANGED = 0x00004;
+
+ /**
+ * Clip's back distance
+ */
+ double backDistance = 100.0;
+
+ /**
+ * back distance scaled to vworld
+ */
+ double backDistanceInVworld;
+
+ /**
+ * The Boundary object defining the application region.
+ */
+ Bounds applicationRegion = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * The transformed value of the applicationRegion.
+ */
+ Bounds transformedRegion = null;
+
+ // This is true when this object is referenced in an immediate mode context
+ boolean inImmCtx = false;
+
+
+ // Target threads to be notified when light changes
+ // Note, the rendering env structure only get notified
+ // when there is a bounds related change
+ static final int targetThreads = J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+
+
+ // Is true, if the clip is viewScoped
+ boolean isViewScoped = false;
+
+ /**
+ * Constructs a Clip node with a default color (black).
+ */
+ ClipRetained () {
+ this.nodeType = NodeRetained.CLIP;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ /**
+ * initializes the clip's back distance to the specified value.
+ * @param backDistance the new back clipping distance
+ */
+ final void initBackDistance(double backDistance) {
+ this.backDistance = backDistance;
+ }
+
+
+ /**
+ * Sets the clip's back distance to the specified value.
+ * @param backDistance the new back clipping distance
+ */
+ final void setBackDistance(double backDistance) {
+ this.backDistance = backDistance;
+ sendMessage(BACKDISTANCE_CHANGED, new Double(backDistance), null);
+ }
+
+ /**
+ * Retrieves the clip's back distance.
+ * @return the current back clipping distance
+ */
+ final double getBackDistance() {
+ return backDistance;
+ }
+
+
+ /**
+ * Initializes the Clip's application region.
+ * @param region a region that contains the Backgound's new application bounds
+ */
+ final void initApplicationBounds(Bounds region) {
+ if (region != null) {
+ applicationRegion = (Bounds) region.clone();
+ } else {
+ applicationRegion = null;
+ }
+ }
+
+ /**
+ * Set the Clip's application region.
+ * @param region a region that contains the Clip's new application bounds
+ */
+ final void setApplicationBounds(Bounds region) {
+ initApplicationBounds(region);
+ // Don't send the message if there is a valid boundingleaf
+ if (boundingLeaf == null) {
+ sendMessage(BOUNDS_CHANGED,
+ (region != null ? region.clone(): null), null);
+ }
+ }
+
+ /**
+ * Get the Backgound's application region.
+ * @return this Clip's application bounds information
+ */
+ final Bounds getApplicationBounds() {
+ return (applicationRegion != null ?
+ (Bounds) applicationRegion.clone() : null);
+ }
+
+ /**
+ * Initializes the Clip's application region
+ * to the specified Leaf node.
+ */
+ void initApplicationBoundingLeaf(BoundingLeaf region) {
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ } else {
+ boundingLeaf = null;
+ }
+ }
+
+ /**
+ * Set the Clip's application region to the specified Leaf node.
+ */
+ void setApplicationBoundingLeaf(BoundingLeaf region) {
+ if (boundingLeaf != null)
+ boundingLeaf.mirrorBoundingLeaf.removeUser(this);
+
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ boundingLeaf.mirrorBoundingLeaf.addUser(this);
+ } else {
+ boundingLeaf = null;
+ }
+ sendMessage(BOUNDINGLEAF_CHANGED,
+ (boundingLeaf != null ?
+ boundingLeaf.mirrorBoundingLeaf : null),
+ (applicationRegion != null ? applicationRegion.clone() : null));
+ }
+
+ /**
+ * Get the Clip's application region
+ */
+ BoundingLeaf getApplicationBoundingLeaf() {
+ return (boundingLeaf != null ?
+ (BoundingLeaf)boundingLeaf.source : null);
+ }
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return inImmCtx;
+ }
+
+ /**
+ * This setLive routine first calls the superclass's method, then
+ * it adds itself to the list of lights
+ */
+ void setLive(SetLiveState s) {
+ if (inImmCtx) {
+ throw new IllegalSharingException(J3dI18N.getString("ClipRetained0"));
+ }
+
+ super.doSetLive(s);
+
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("ClipRetained1"));
+ }
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("ClipRetained2"));
+ }
+
+
+ initMirrorObject();
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(this);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(this);
+ }
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.ENV_TARGETS);
+ }
+ switchState = (SwitchState)s.switchStates.get(0);
+
+ // add this node to the transform target
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+
+ super.markAsLive();
+ }
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it removes itself to the list of lights
+ */
+ void clearLive(SetLiveState s) {
+ super.clearLive(s);
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(this);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(this);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.ENV_TARGETS);
+ }
+ }
+
+ void initMirrorObject() {
+ Transform3D lastLocalToVworld = getLastLocalToVworld();
+
+ if (boundingLeaf != null) {
+ transformedRegion = (Bounds)boundingLeaf.mirrorBoundingLeaf.transformedRegion;
+ }
+ else { // Evaluate applicationRegion if not null
+ if (applicationRegion != null) {
+ transformedRegion = (Bounds)applicationRegion.clone();
+ transformedRegion.transform(applicationRegion, lastLocalToVworld);
+ }
+ else {
+ transformedRegion = null;
+ }
+
+ }
+ backDistanceInVworld = backDistance *
+ lastLocalToVworld.getDistanceScale();
+ }
+
+
+ // The update Object function.
+ void updateImmediateMirrorObject(Object[] objs) {
+ int component = ((Integer)objs[1]).intValue();
+ Transform3D trans;
+ Transform3D currentLocalToVworld = getCurrentLocalToVworld();
+
+ // Bounds message only sent when boundingleaf is null
+ if ((component & BOUNDS_CHANGED) != 0) {
+ if (objs[2] != null) {
+ transformedRegion = ((Bounds) objs[2]).copy(transformedRegion);
+ transformedRegion.transform(transformedRegion,
+ currentLocalToVworld);
+ }
+ else {
+ transformedRegion = null;
+ }
+ }
+ else if ((component & BOUNDINGLEAF_CHANGED) != 0) {
+ if (objs[2] != null) {
+ transformedRegion = ((BoundingLeafRetained)objs[2]).transformedRegion;
+ }
+ else { // Evaluate applicationRegion if not null
+ Bounds appRegion = (Bounds)objs[3];
+ if (appRegion != null) {
+ transformedRegion = ((Bounds)appRegion).copy(transformedRegion);
+ transformedRegion.transform(appRegion,
+ currentLocalToVworld);
+ }
+ else {
+ transformedRegion = null;
+ }
+
+ }
+
+ }
+ else if ((component & BACKDISTANCE_CHANGED) != 0) {
+ backDistanceInVworld = ((Double)objs[2]).doubleValue() *
+ currentLocalToVworld.getDistanceScale();
+ }
+ }
+
+ /** Note: This routine will only be called on
+ * the mirror object - will update the object's
+ * cached region and transformed region
+ */
+
+ void updateBoundingLeaf() {
+ if (boundingLeaf != null &&
+ boundingLeaf.mirrorBoundingLeaf.switchState.currentSwitchOn) {
+ transformedRegion =
+ boundingLeaf.mirrorBoundingLeaf.transformedRegion;
+ } else { // Evaluate applicationRegion if not null
+ if (applicationRegion != null) {
+ transformedRegion = applicationRegion.copy(transformedRegion);
+ transformedRegion.transform(applicationRegion,
+ getCurrentLocalToVworld());
+ } else {
+ transformedRegion = null;
+ }
+ }
+ }
+
+ void updateImmediateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (boundingLeaf == null) {
+ if (applicationRegion != null) {
+ transformedRegion = (Bounds)applicationRegion.clone();
+ transformedRegion.transform(applicationRegion,
+ getCurrentLocalToVworld());
+ }
+ }
+ }
+
+ final void sendMessage(int attrMask, Object attr, Object attr2) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.CLIP_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = attr2;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (applicationRegion != null) {
+ applicationRegion.transform(xform.transform);
+ }
+ }
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ leafList.add(this);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ColorInterpolator.java b/src/classes/share/javax/media/j3d/ColorInterpolator.java
new file mode 100644
index 0000000..b1b5b1c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ColorInterpolator.java
@@ -0,0 +1,296 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+import java.util.Enumeration;
+
+/**
+ * Color interpolation behavior. This class defines a behavior that
+ * modifies the ambient, emissive, diffuse, or specular color of its
+ * target material object by linearly interpolating between a pair of
+ * specified colors, using the value generated by the specified Alpha
+ * object.
+ * The behavior modifies the color specified by the
+ * Material's colorTarget attribute, one of: AMBIENT, EMISSIVE,
+ * DIFFUSE, SPECULAR, or AMBIENT_AND_DIFFUSE.
+ * The ALLOW_COMPONENT_READ bit must be set in the Material object in
+ * order for the Material's colorTarget to be read.
+ * If the Material object's ALLOW_COMPONENT_READ bit is <i>not</i> set, the
+ * diffuse component will be modified.
+ *
+ * @see Material
+ */
+
+public class ColorInterpolator extends Interpolator {
+
+ Material target;
+ Color3f startColor = new Color3f();
+ Color3f endColor = new Color3f();
+ Color3f newColor = new Color3f();
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private int prevColorTarget = -1;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, no parameter constructor used by cloneNode
+ ColorInterpolator() {
+ }
+
+ /**
+ * Constructs a trivial color interpolator with a specified target,
+ * a starting color of black, and an ending color of white.
+ * @param alpha the alpha object for this interpolator
+ * @param target the material component object whose
+ * color is affected by this color interpolator
+ */
+ public ColorInterpolator(Alpha alpha,
+ Material target) {
+
+ super(alpha);
+
+ this.target = target;
+ this.startColor.set(0.0f, 0.0f, 0.0f);
+ this.endColor.set(1.0f, 1.0f, 1.0f);
+ }
+
+ /**
+ * Constructs a color interpolator with the specified target,
+ * starting color, and ending color.
+ * @param alpha the alpha object for this interpolator
+ * @param target the material component object whose
+ * color is affected by this color interpolator
+ * @param startColor the starting color
+ * @param endColor the ending color
+ */
+ public ColorInterpolator(Alpha alpha,
+ Material target,
+ Color3f startColor,
+ Color3f endColor) {
+
+ super(alpha);
+
+ this.target = target;
+ this.startColor.set(startColor);
+ this.endColor.set(endColor);
+ }
+
+ /**
+ * This method sets the startColor for this interpolator.
+ * @param color the new start color
+ */
+ public void setStartColor(Color3f color) {
+ startColor.set(color);
+ prevAlphaValue = Float.NaN;
+ }
+
+ /**
+ * This method retrieves this interpolator's startColor.
+ * @param color the vector that will receive the interpolator's start color
+ */
+ public void getStartColor(Color3f color) {
+ color.set(startColor);
+ }
+
+ /**
+ * This method sets the endColor for this interpolator.
+ * @param color the new end color
+ */
+ public void setEndColor(Color3f color) {
+ endColor.set(color);
+ prevAlphaValue = Float.NaN;
+ }
+
+ /**
+ * This method retrieves this interpolator's endColor.
+ * @param color the vector that will receive the interpolator's end color
+ */
+ public void getEndColor(Color3f color) {
+ color.set(endColor);
+ }
+
+ /**
+ * This method sets the target material component object for
+ * this interpolator.
+ * @param target the material component object whose
+ * color is affected by this color interpolator
+ */
+ public void setTarget(Material target) {
+ this.target = target;
+ prevAlphaValue = Float.NaN;
+ }
+
+ /**
+ * This method retrieves this interpolator's target material
+ * component object.
+ * @return the interpolator's target material component object
+ */
+ public Material getTarget() {
+ return target;
+ }
+
+ // The ColorInterpolator's initialize routine uses the default
+ // initialization routine.
+
+ /**
+ * This method is invoked by the behavior scheduler every frame.
+ * It maps the alpha value that corresponds to the current time
+ * into a color value and updates the ambient, emissive, diffuse,
+ * or specular color (or both the ambient and diffuse color) of
+ * the specified target Material object with this new color value.
+ *
+ * @param criteria an enumeration of the criteria that caused the
+ * stimulus
+ */
+ public void processStimulus(Enumeration criteria) {
+
+ // Handle stimulus
+ WakeupCriterion criterion = passiveWakeupCriterion;
+
+ if (alpha != null) {
+ float value = alpha.value();
+
+ int colorTarget = Material.DIFFUSE;
+ if (target.getCapability(Material.ALLOW_COMPONENT_READ))
+ colorTarget = target.getColorTarget();
+
+ if (value != prevAlphaValue || colorTarget != prevColorTarget) {
+ newColor.x = (1.0f-value)*startColor.x + value*endColor.x;
+ newColor.y = (1.0f-value)*startColor.y + value*endColor.y;
+ newColor.z = (1.0f-value)*startColor.z + value*endColor.z;
+
+ switch (colorTarget) {
+ case Material.AMBIENT:
+ target.setAmbientColor(newColor);
+ break;
+ case Material.AMBIENT_AND_DIFFUSE:
+ target.setAmbientColor(newColor);
+ // fall through
+ case Material.DIFFUSE:
+ target.setDiffuseColor(newColor);
+ break;
+ case Material.EMISSIVE:
+ target.setEmissiveColor(newColor);
+ break;
+ case Material.SPECULAR:
+ target.setSpecularColor(newColor);
+ break;
+ }
+
+ prevAlphaValue = value;
+ prevColorTarget = colorTarget;
+ }
+
+ if (!alpha.finished() && !alpha.isPaused()) {
+ criterion = defaultWakeupCriterion;
+ }
+ }
+ wakeupOn(criterion);
+ }
+
+ /**
+ * 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) {
+ ColorInterpolator ci = new ColorInterpolator();
+ ci.duplicateNode(this, forceDuplicate);
+ return ci;
+ }
+
+
+ /**
+ * Copies all ColorInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ColorInterpolator ci = (ColorInterpolator) originalNode;
+
+ ci.getStartColor(startColor);
+ ci.getEndColor(endColor);
+
+ // this reference will be updated in updateNodeReferences()
+ setTarget(ci.getTarget());
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ // check Material
+ NodeComponent nc = getTarget();
+
+ if (nc != null) {
+ setTarget((Material) referenceTable.getNewObjectReference(nc));
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ColoringAttributes.java b/src/classes/share/javax/media/j3d/ColoringAttributes.java
new file mode 100644
index 0000000..5a25fd1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ColoringAttributes.java
@@ -0,0 +1,337 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+
+/**
+ * The ColoringAttributes object defines attributes used in
+ * color selection and shading model.
+ *
+ * <p>
+ * <b>Color</b>
+ * <p>
+ * The <code>setColor</code> methods set the current intrinsic red, green, and
+ * blue color values of this ColoringAttributes component object.
+ * This color is only used for unlit geometry. If lighting is enabled,
+ * the material colors are used in the lighting equation to produce
+ * the final color. When vertex colors are present in unlit
+ * geometry, those vertex colors are used in place of this
+ * ColoringAttributes color, unless the vertex colors are ignored.
+ * <p>
+ * There are two variations on the <code>setColor</code> methods, one
+ * that takes a Color3f and one that takes three floats. No alpha
+ * value is allowed (it's automatically set to 1.0). The float values
+ * range between 0.0 and 1.0, with 1.0 being full intensity of the
+ * color. A color value of (1.0, 1.0, 1.0) is white.
+ * <p>
+ * <b>Shading Model</b>
+ * <p>
+ * The <code>setShadeModel</code> method sets the shade model for this
+ * ColoringAttributes component object. The shade model may be one of
+ * the following:<p>
+ * <ul>
+ * <li>FASTEST - use the fastest available method for shading. This
+ * shading mode maps to whatever shading model the Java 3D implementor
+ * defines as the "fastest," which may be hardware-dependent.</li>
+ * <p>
+ * <li>NICEST - use the nicest (highest quality) available method
+ * for shading. This shading mode maps to whatever shading model
+ * the Java 3D implementor defines as the "nicest," shading
+ * model, which may be hardware-dependent.</li>
+ * <p>
+ * <li>SHADE_FLAT - use the flat shading model. This shading model
+ * does not interpolate color across the primitive.
+ * The primitive is drawn with a single color
+ * and the color of one vertex of the primitive is duplicated
+ * across all the vertices of the primitive.</li>
+ * <p>
+ * <li>SHADE_GOURAUD - use the Gouraud (smooth) shading model.
+ * This shading model smoothly interpolates the color at each vertex
+ * across the primitive.
+ * The primitive is drawn with many different colors
+ * and the color at each vertex is treated individually. For lines,
+ * the colors along the line segment are interpolated between
+ * the vertex colors. This is the default shade model if no other
+ * is specified.</li>
+ * <p></ul>
+ *
+ * @see Appearance
+ */
+public class ColoringAttributes extends NodeComponent {
+ /**
+ * Specifies that this ColoringAttributes object allows
+ * reading its color component information.
+ */
+ public static final int
+ ALLOW_COLOR_READ = CapabilityBits.COLORING_ATTRIBUTES_ALLOW_COLOR_READ;
+
+ /**
+ * Specifies that this ColoringAttributes object allows
+ * writing its color component information.
+ */
+ public static final int
+ ALLOW_COLOR_WRITE = CapabilityBits.COLORING_ATTRIBUTES_ALLOW_COLOR_WRITE;
+
+ /**
+ * Specifies that this ColoringAttributes object allows
+ * reading its shade model component information.
+ */
+ public static final int
+ ALLOW_SHADE_MODEL_READ = CapabilityBits.COLORING_ATTRIBUTES_ALLOW_SHADE_MODEL_READ;
+
+ /**
+ * Specifies that this ColoringAttributes object allows
+ * writing its shade model component information.
+ */
+ public static final int
+ ALLOW_SHADE_MODEL_WRITE = CapabilityBits.COLORING_ATTRIBUTES_ALLOW_SHADE_MODEL_WRITE;
+
+ /**
+ * Use the fastest available method for shading.
+ */
+ public static final int FASTEST = 0;
+ /**
+ * Use the nicest available method for shading.
+ */
+ public static final int NICEST = 1;
+
+ /**
+ * Do not interpolate color across the primitive.
+ */
+ public static final int SHADE_FLAT = 2;
+ /**
+ * Smoothly interpolate the color at each vertex across the primitive.
+ */
+ public static final int SHADE_GOURAUD = 3;
+
+ /**
+ * Constructs a ColoringAttributes node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * color = white (1,1,1)<br>
+ * shade model = SHADE_GOURAUD<br>
+ * </ul>
+ */
+ public ColoringAttributes() {
+ // Just use default attributes
+ }
+
+ /**
+ * Construct ColoringAttributes object with specified values.
+ * @param color the intrisic color
+ * @param shadeModel the shade model used; one of FASTEST, NICEST,
+ * SHADE_FLAT, or SHADE_GOURAUD
+ */
+ public ColoringAttributes(Color3f color, int shadeModel) {
+ ((ColoringAttributesRetained)this.retained).initColor(color);
+ ((ColoringAttributesRetained)this.retained).initShadeModel(shadeModel);
+
+ }
+
+ /**
+ * Construct ColoringAttributes object with specified values.
+ * @param red red component of the intrisic color
+ * @param green green component of the intrisic color
+ * @param blue blue component of the intrisic color
+ * @param shadeModel the shade model used; one of FASTEST, NICEST,
+ * SHADE_FLAT, or SHADE_GOURAUD
+ */
+ public ColoringAttributes(float red, float green, float blue,
+ int shadeModel) {
+ ((ColoringAttributesRetained)this.retained).initColor(red, green,blue);
+ ((ColoringAttributesRetained)this.retained).initShadeModel(shadeModel);
+ }
+
+ /**
+ * Sets the intrinsic color of this ColoringAttributes
+ * component object. This color is only used for unlit geometry;
+ * if lighting is enabled, then the material colors are used in the
+ * lighting equation to produce the final color.
+ * When vertex colors are present in unlit geometry, those
+ * vertex colors are used in place of this ColoringAttributes color
+ * unless the vertex colors are ignored.
+ * @param color the color that is used when lighting is disabled
+ * or when material is null
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see Material
+ * @see RenderingAttributes#setIgnoreVertexColors
+ */
+ public void setColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ColoringAttributes0"));
+
+ if (isLive())
+ ((ColoringAttributesRetained)this.retained).setColor(color);
+ else
+ ((ColoringAttributesRetained)this.retained).initColor(color);
+
+ }
+
+ /**
+ * Sets the intrinsic color of this ColoringAttributes
+ * component object. This color is only used for unlit geometry;
+ * if lighting is enabled, then the material colors are used in the
+ * lighting equation to produce the final color.
+ * When vertex colors are present in unlit geometry, those
+ * vertex colors are used in place of this ColoringAttributes color
+ * unless the vertex colors are ignored.
+ * @param r the red component of the color
+ * @param g the green component of the color
+ * @param b the blue component of the color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see Material
+ * @see RenderingAttributes#setIgnoreVertexColors
+ */
+ public void setColor(float r, float g, float b) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ColoringAttributes0"));
+
+ if (isLive())
+ ((ColoringAttributesRetained)this.retained).setColor(r, g, b);
+ else
+ ((ColoringAttributesRetained)this.retained).initColor(r, g, b);
+ }
+
+ /**
+ * Gets the intrinsic color of this ColoringAttributes
+ * component object.
+ * @param color the vector that will receive color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ColoringAttributes2"));
+
+ ((ColoringAttributesRetained)this.retained).getColor(color);
+ }
+
+ /**
+ * Sets the shade mode for this ColoringAttributes component object.
+ * @param shadeModel the shade mode to be used; one of FASTEST,
+ * NICEST, SHADE_FLAT, or SHADE_GOURAUD
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setShadeModel(int shadeModel) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SHADE_MODEL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ColoringAttributes3"));
+
+ if (isLive())
+ ((ColoringAttributesRetained)this.retained).setShadeModel(shadeModel);
+ else
+ ((ColoringAttributesRetained)this.retained).initShadeModel(shadeModel);
+ }
+
+ /**
+ * Gets the shade mode for this ColoringAttributes component object.
+ * @return shadeModel the shade mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getShadeModel() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SHADE_MODEL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ColoringAttributes4"));
+
+ return ((ColoringAttributesRetained)this.retained).getShadeModel();
+ }
+
+ /**
+ * Creates a retained mode ColoringAttributesRetained object that this
+ * ColoringAttributes component object will point to.
+ */
+ void createRetained() {
+ this.retained = new ColoringAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+
+ public NodeComponent cloneNodeComponent() {
+ ColoringAttributes ca = new ColoringAttributes();
+ ca.duplicateNodeComponent(this);
+ return ca;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent,
+ forceDuplicate);
+
+ ColoringAttributesRetained attr =
+ (ColoringAttributesRetained) originalNodeComponent.retained;
+
+ ColoringAttributesRetained rt = (ColoringAttributesRetained) retained;
+ Color3f c = new Color3f();
+ attr.getColor(c);
+
+ rt.initColor(c);
+ rt.initShadeModel(attr.getShadeModel());
+ }
+
+ /**
+ * Returns a String representation of this ColoringAttributes object.
+ * If the scene graph is live only those values with their
+ * Capability read bit set will be displayed.
+ */
+ public String toString() {
+ StringBuffer str=new StringBuffer("ColoringAttributes:");
+ String shadingModes[] = { "FASTEST", "NICEST", "SHADE_FLAT",
+ "SHADE_GOURAUD" };
+
+ try {
+ Color3f color=new Color3f();
+ getColor( color );
+ str.append( "Color="+color );
+ }
+ catch (CapabilityNotSetException e) {str.append("Color=N/A");}
+
+ try {
+ str.append( " ShadeModel="+shadingModes[getShadeModel()] );
+ }
+ catch (CapabilityNotSetException ex) {str.append("ShadeModel=N/A");}
+
+ return new String(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ColoringAttributesRetained.java b/src/classes/share/javax/media/j3d/ColoringAttributesRetained.java
new file mode 100644
index 0000000..1f0ae14
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ColoringAttributesRetained.java
@@ -0,0 +1,253 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+import java.util.ArrayList;
+
+/**
+ * The ColoringAttributesRetained object defines attributes that apply to
+ * to coloring mapping.
+ */
+class ColoringAttributesRetained extends NodeComponentRetained {
+ // A list of pre-defined bits to indicate which component
+ // in this ColoringAttributes object changed.
+ static final int COLOR_CHANGED = 0x01;
+ static final int SHADE_MODEL_CHANGED = 0x02;
+
+ // Intrinsic color used when lighting is disabled or when
+ // material is null
+ Color3f color = new Color3f(1.0f, 1.0f, 1.0f);
+
+ // Shade model (flat, smooth)
+ int shadeModel = ColoringAttributes.SHADE_GOURAUD;
+
+ /**
+ * Sets the intrinsic color of this ColoringAttributes
+ * component object.
+ * @param color the color that is used when lighting is disabled
+ * or when material is null
+ */
+ final void initColor(Color3f color) {
+ this.color.set(color);
+ }
+
+ /**
+ * Sets the intrinsic color of this ColoringAttributes
+ * component object and sends a message notifying
+ * the interested structures of the change.
+ * @param color the color that is used when lighting is disabled
+ * or when material is null
+ */
+ final void setColor(Color3f color) {
+ initColor(color);
+ sendMessage(COLOR_CHANGED, new Color3f(color));
+ }
+
+ /**
+ * Sets the intrinsic color of this ColoringAttributes
+ * component object. This color is used when lighting is disabled
+ * or when material is null.
+ * @param r the red component of the color
+ * @param g the green component of the color
+ * @param b the blue component of the color
+ */
+ final void initColor(float r, float g, float b) {
+ this.color.set(r, g, b);
+ }
+
+ /**
+ * Sets the intrinsic color of this ColoringAttributes
+ * component object and sends a message notifying
+ * the interested structures of the change.
+ * This color is used when lighting is disabled
+ * or when material is null.
+ * @param r the red component of the color
+ * @param g the green component of the color
+ * @param b the blue component of the color
+ */
+ final void setColor(float r, float g, float b) {
+ initColor(r, g, b);
+ sendMessage(COLOR_CHANGED, new Color3f(r, g, b));
+ }
+
+ /**
+ * Gets the intrinsic color of this ColoringAttributes
+ * component object.
+ * @param color the vector that will receive color
+ */
+ final void getColor(Color3f color) {
+ color.set(this.color);
+ }
+
+ /**
+ * Sets the shade mode for this ColoringAttributes component object.
+ * @param shadeModel the shade mode to be used; one of FASTEST,
+ * NICEST, SHADE_FLAT, or SHADE_GOURAUD
+ */
+ final void initShadeModel(int shadeModel) {
+ this.shadeModel = shadeModel;
+ }
+
+ /**
+ * Sets the shade mode for this ColoringAttributes component object
+ * and sends a message notifying
+ * the interested structures of the change.
+ * @param shadeModel the shade mode to be used; one of FASTEST,
+ * NICEST, SHADE_FLAT, or SHADE_GOURAUD
+ */
+ final void setShadeModel(int shadeModel) {
+ initShadeModel(shadeModel);
+ sendMessage(SHADE_MODEL_CHANGED, new Integer(shadeModel));
+ }
+
+ /**
+ * Gets the shade mode for this ColoringAttributes component object.
+ * @return shadeModel the shade mode
+ */
+ final int getShadeModel() {
+ return shadeModel;
+ }
+
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ } else {
+ ColoringAttributesRetained mirrorCa
+ = new ColoringAttributesRetained();
+ mirrorCa.source = source;
+ mirrorCa.set(this);
+ mirror = mirrorCa;
+ }
+ } else {
+ ((ColoringAttributesRetained) mirror).set(this);
+ }
+ }
+ /**
+ * These two native methods update the native context
+ */
+ native void updateNative(long ctx,
+ float dRed, float dGreen, float dBlue,
+ float red, float green, float blue,
+ float alpha,
+ boolean lEnable,
+ int shadeModel);
+
+ void updateNative(long ctx,
+ float dRed, float dGreen, float dBlue,
+ float alpha, boolean lEnable) {
+ updateNative(ctx, dRed, dBlue, dGreen, color.x, color.y,
+ color.z, alpha,
+ lEnable, shadeModel);
+ }
+
+ /**
+ * Creates a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ ((ColoringAttributesRetained)mirror).set(this);
+ }
+
+ /** Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ ColoringAttributesRetained mirrorCa =
+ (ColoringAttributesRetained) mirror;
+
+ if ((component & COLOR_CHANGED) != 0) {
+ mirrorCa.color.set(((Color3f)value));
+ }
+ else if ((component & SHADE_MODEL_CHANGED) != 0) {
+ mirrorCa.shadeModel = ((Integer)value).intValue();
+ }
+ }
+
+ boolean equivalent(ColoringAttributesRetained cr) {
+ return ((cr != null) &&
+ color.equals(cr.color) &&
+ (shadeModel == cr.shadeModel));
+ }
+
+
+ // This functions clones the retained side only and is used
+ // internally
+ protected Object clone() {
+ ColoringAttributesRetained cr =
+ (ColoringAttributesRetained)super.clone();
+ cr.color = new Color3f(color);
+ // shadeModel is copied in super.clone()
+ return cr;
+ }
+
+ // This functions clones the retained side only and is used
+ // internally
+ protected void set(ColoringAttributesRetained cr) {
+ super.set(cr);
+ color.set(cr.color);
+ shadeModel = cr.shadeModel;
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.COLORINGATTRIBUTES_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.COLORINGATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+ void handleFrequencyChange(int bit) {
+ if (bit == ColoringAttributes.ALLOW_COLOR_WRITE ||
+ bit == ColoringAttributes.ALLOW_SHADE_MODEL_WRITE) {
+ setFrequencyChangeMask(bit, 0x1);
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/CompileState.java b/src/classes/share/javax/media/j3d/CompileState.java
new file mode 100644
index 0000000..997b224
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CompileState.java
@@ -0,0 +1,325 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import java.security.*;
+
+/**
+ * The CompileState holds information used during a compile. It is
+ * passed to each SceneGraphObject (SGO) during the compile. Each SGO
+ * modifies the CompileState as necessary and passes the CompileState
+ * to its children (if any).
+ *
+ * The CompileState currently has two functions: appearance mapping
+ * and shape merging.
+ *
+ * Appearance mapping maintains a list of the unique appearances seen
+ * during the compile. getAppearance() is used to turn multiple,
+ * equivalent and static appearances into a single shared appearance.
+ *
+ * The shape mergings collects shapes that are potentially mergable
+ * during a compile. The shapes are sorted into a Map of Lists of
+ * shapes, using the shape's appearance as the key. After a subtree
+ * is traversed, the shapes are merged and added to the Group.
+ */
+
+class CompileState {
+ // Appearance mapping stuff:
+ HashMap knownAppearances = new HashMap();
+ int numAppearances = 0;
+ int numShared = 0;
+ int numShapes = 0;
+
+ // Shape merging stuff:
+ HashMap shapeLists = null; // top entry in shapeListStack
+ int numMergeSets = 0;
+ int numMergeShapes = 0;
+ boolean compileVerbose = false;
+
+
+ static final int BOUNDS_READ = 0x00001;
+ static final int GEOMETRY_READ = 0x00002;
+
+
+ // scene graph flattening
+
+ boolean keepTG = false; // used to force the immediate transform
+ // group to stay around
+
+ boolean needNormalsTransform = false; // true if the current transform
+ // needs to push down normals
+ // transform to geometries
+
+ // the current static transform group
+ TransformGroupRetained staticTransform = null;
+
+ // parent group
+ GroupRetained parentGroup = null;
+
+ // list of transform group
+ // for the current transform group
+ ArrayList transformGroupChildrenList = null;
+
+ // list of objects that have a static
+ // transform that can be deferenced
+ // after compile
+ ArrayList staticTransformObjects = new ArrayList(1);
+
+ int numTransformGroups = 0;
+ int numStaticTransformGroups = 0;
+ int numMergedTransformGroups = 0;
+ int numGroups = 0;
+ int numMergedGroups = 0;
+ int numShapesWSharedGeom = 0;
+ int numShapesWStaticTG = 0;
+ int numLinks = 0;
+ int numSwitches = 0;
+ int numOrderedGroups = 0;
+ int numMorphs = 0;
+
+ CompileState() {
+ try {
+ compileVerbose = Boolean.getBoolean("javax.media.j3d.compileVerbose");
+ } catch (AccessControlException e) {
+ compileVerbose = false;
+ }
+ initShapeMerge();
+ }
+
+ // Appearance mapping:
+ /**
+ * Returns an unique appearance which equals app. If appearance does not
+ * equal any previously found, the appearance will be added to the known
+ * appearances and be returned. If the apperance equals a previously known
+ * appearance, then the prevously known apperance will be returned
+ */
+ AppearanceRetained getAppearance(AppearanceRetained app) {
+ AppearanceRetained retval;
+
+ // see if the appearance has allready been classified
+ if (app.map == this) {
+ if (app.mapAppearance != null) {
+ numShared++;
+ return app.mapAppearance;
+ }
+ }
+
+ // check if this appearance equals one one in the Map
+ if ((retval = (AppearanceRetained)knownAppearances.get(app)) != null) {
+ numShared++;
+ } else {
+ // not found, put this appearance in the map
+ knownAppearances.put(app, app);
+ numAppearances++;
+ numShared++; // sharing with self...
+ retval = app;
+ }
+
+ // cache this result on the appearance in case it appears again
+ app.map = this;
+ app.mapAppearance = retval;
+
+ return retval;
+ }
+
+ // Shape Merging:
+ private void initShapeMerge() {
+ shapeLists = new HashMap();
+
+ }
+
+ void addShape(Shape3DRetained shape) {
+ if (parentGroup != null) {
+ // sort the shapes into lists with equivalent appearances
+ Vector list;
+ if ((list = (Vector)shapeLists.get(shape.appearance)) == null) {
+ list = new Vector();
+ shapeLists.put(shape.appearance, list);
+ }
+ // Add the shape to the list only if at its parent level
+ // no children can be added or removed ..
+ // Look for the first non-null geometry, there should be atleast
+ // one otherwise it wouldn't come here
+ GeometryRetained geometry = null;
+ int i = 0;
+ while (geometry == null && i < shape.geometryList.size()) {
+ geometry = (GeometryRetained) shape.geometryList.get(i);
+ i++;
+ }
+ if (shape.parent instanceof GroupRetained && ((GroupRetained)shape.parent).isStaticChildren() && geometry.geoType < GeometryArrayRetained.GEO_TYPE_RASTER) {
+ list.add(shape);
+ }
+
+ }
+ }
+
+
+ void printStats() {
+ System.out.println("numTransformGroups= " + numTransformGroups);
+ System.out.println("numStaticTransformGroups= " + numStaticTransformGroups);
+ System.out.println("numMergedTransformGroups= " + numMergedTransformGroups);
+ System.out.println("numGroups= " + numGroups);
+ System.out.println("numMergedGroups= " + numMergedGroups);
+ System.out.println("numShapes= " + numShapes);
+ System.out.println("numShapesWStaticTG= " + numShapesWStaticTG);
+ System.out.println("numMergeShapes= " + numMergeShapes);
+ System.out.println("numMergeSets= " + numMergeSets);
+ System.out.println("numLinks= " + numLinks);
+ System.out.println("numSwitches= " + numSwitches);
+ System.out.println("numOrderedGroups= " + numOrderedGroups);
+ System.out.println("numMorphs= " + numMorphs);
+ }
+
+ void doShapeMerge() {
+
+ // System.out.println("doShapeMerge, shapeList = "+shapeLists);
+ if (shapeLists != null) {
+ // loop over the shapes in each list, creating a single shape
+ // for each. Add the shape to the group
+ Collection lists = shapeLists.values();
+ Iterator listIterator = lists.iterator();
+ Shape3DRetained mergeShape;
+ GeometryRetained firstGeo;
+ int num = 0;
+ int compileFlags = 0;
+
+ while (listIterator.hasNext()) {
+ Vector curList = (Vector)listIterator.next();
+ int numShapes = curList.size();
+ Shape3DRetained[] shapes = new Shape3DRetained[numShapes];
+ curList.copyInto(shapes);
+ Shape3DRetained[] toBeMergedShapes = new Shape3DRetained[numShapes];
+ for (int i = 0; i < numShapes; i++) {
+ if (shapes[i] == null) {
+ continue;
+ }
+ firstGeo = null;
+ num = 0;
+ // Get the first non-null geometry
+ while (firstGeo == null && num < shapes[i].geometryList.size()) {
+ firstGeo = (GeometryRetained) shapes[i].geometryList.get(num);
+ num++;
+ }
+
+ if (firstGeo != null && firstGeo instanceof GeometryArrayRetained) {
+ int numMerge = 0;
+ mergeShape = shapes[i];
+ GeometryArrayRetained mergeGeo = (GeometryArrayRetained)firstGeo;
+
+ toBeMergedShapes[numMerge++] = mergeShape;
+ // Determine if all mergeable shapes have the same boundsCompute
+ // and collisionBounds set the same way
+ compileFlags = getCompileFlags(mergeShape);
+ for (int j = i+1; j < numShapes; j++) {
+ if (shapes[j] == null) {
+ continue;
+ }
+ firstGeo = null;
+ num = 0;
+ // Get the first non-null geometry
+ while (firstGeo == null && num < shapes[j].geometryList.size()) {
+ firstGeo = (GeometryRetained) shapes[j].geometryList.get(num);
+ num++;
+ }
+
+ // There is a non-null geometry for this shape ..
+ if (firstGeo != null &&
+ shapes[j].isEquivalent(mergeShape) &&
+ firstGeo.isEquivalenceClass(mergeGeo) &&
+ ((GeometryArrayRetained)firstGeo).vertexFormat == mergeGeo.vertexFormat) {
+ // got one to merge, add shapes to merge,
+ toBeMergedShapes[numMerge++] = shapes[j];
+
+ compileFlags |= getCompileFlags(shapes[j]);
+
+ // remove from shapes
+ shapes[j] = null;
+ }
+ }
+ if (numMerge > 1) {
+
+ // remove the shapes from its parent before merge
+ // They all should
+ GroupRetained group = (GroupRetained)toBeMergedShapes[0].parent;
+ Shape3DRetained s;
+ for (int n = 0; n < numMerge; n++) {
+ s = toBeMergedShapes[n];
+ boolean found = false;
+ int numChilds = group.numChildren();
+ for (int k = 0; (k < numChilds && !found); k++) {
+ if (group.getChild(k).retained == s) {
+ found = true;
+ group.removeChild(k);
+ }
+ }
+ if (!found) {
+ System.out.println("ShapeSet.add(): Can't remove " +
+ "shape from parent, can't find shape!");
+ }
+
+ }
+
+ mergeShape = new Shape3DCompileRetained(toBeMergedShapes, numMerge, compileFlags);
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ if (J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_3)) {
+ System.out.println("Dest is "+ parentGroup);
+ System.out.println("Compile Shape "+mergeShape);
+ System.out.println(mergeShape.geometryList.size()+" geoemtryList");
+ for (int j = 0; j < mergeShape.geometryList.size(); j++) {
+ GeometryRetained geo = ((GeometryRetained)mergeShape.geometryList.get(j));
+ if (geo != null)
+ System.out.println("\t Geo_type = "+geo.geoType);
+ }
+
+ System.out.println(numMerge+" Shapes were merged ");
+ for (int j = 0; j < numMerge; j++) {
+ System.out.println("\t" + toBeMergedShapes[j]);
+ }
+ }
+ }
+
+ // Set the source to one of the merged shape's source
+ mergeShape.setSource(toBeMergedShapes[0].source);
+ numMergeSets++;
+ numMergeShapes += numMerge ;
+ parentGroup.addChild((Node)mergeShape.source);
+ }
+ }
+ // add the shape to the dest
+ }
+ }
+ }
+
+ // Clear the shapelists for the next merge
+ shapeLists.clear();
+
+ }
+
+
+ int getCompileFlags(Shape3DRetained shape) {
+ int cflag = 0;
+
+ // If allow intersect is turned on , then geometry is readable
+ if (shape.allowIntersect() ||
+ shape.source.getCapability(Shape3D.ALLOW_GEOMETRY_READ)||
+ (shape.boundsAutoCompute &&
+ shape.source.getCapability(Shape3D.ALLOW_BOUNDS_READ)))
+ cflag |= GEOMETRY_READ;
+
+ return cflag;
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/CompressedGeometry.java b/src/classes/share/javax/media/j3d/CompressedGeometry.java
new file mode 100644
index 0000000..40e6657
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CompressedGeometry.java
@@ -0,0 +1,431 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The compressed geometry object is used to store geometry in a
+ * compressed format. Using compressed geometry reduces the amount
+ * of memory needed by a Java 3D application and increases the speed
+ * objects can be sent over the network. Once geometry decompression
+ * hardware support becomes available, increased rendering performance
+ * will also result from the use of compressed geometry.
+ * <p>
+ * Compressed geometry may be passed to this CompressedGeometry object
+ * in one of two ways: by copying the data into this object using the
+ * existing constructor, or by passing a reference to the data.
+ * <p>
+ * <ul>
+ * <li>
+ * <b>By Copying:</b>
+ * The existing CompressedGeometry constructor copies the buffer of
+ * compressed geometry data into this CompressedGeometry object. This
+ * is appropriate for many applications, and allows Java 3D to verify
+ * the data once and then not worry about it again.
+ * </li>
+ * <li><b>By Reference:</b>
+ * A new constructor and set of methods in Java 3D version 1.2 allows
+ * compressed geometry data to be accessed by reference, directly from
+ * the user's array. To use this feature, you need to construct a
+ * CompressedGeometry object with the <code>byReference</code> flag
+ * set to <code>true</code>. In this mode, a reference to the input
+ * data is saved, but the data itself is not necessarily copied. Note
+ * that the compressed geometry header is still copied into this
+ * compressed geometry object. Data referenced by a
+ * CompressedGeometry object must not be modified after the
+ * CompressedGeometry object is constructed.
+ * Applications
+ * must exercise care not to violate this rule. If any referenced
+ * compressed geometry data is modified after construction,
+ * the results are undefined.
+ * </li>
+ * </ul>
+ */
+public class CompressedGeometry extends Geometry {
+
+ CompressedGeometryHeader cgHeader ;
+
+ /**
+ * Specifies that this CompressedGeometry object allows reading its
+ * byte count information.
+ */
+ public static final int
+ ALLOW_COUNT_READ = CapabilityBits.COMPRESSED_GEOMETRY_ALLOW_COUNT_READ ;
+
+ /**
+ * Specifies that this CompressedGeometry object allows reading its
+ * header information.
+ */
+ public static final int
+ ALLOW_HEADER_READ = CapabilityBits.COMPRESSED_GEOMETRY_ALLOW_HEADER_READ ;
+
+ /**
+ * Specifies that this CompressedGeometry object allows reading its
+ * geometry data component information.
+ */
+ public static final int
+ ALLOW_GEOMETRY_READ =
+ CapabilityBits.COMPRESSED_GEOMETRY_ALLOW_GEOMETRY_READ ;
+
+ /**
+ * Specifies that this CompressedGeometry allows reading the geometry
+ * data reference information for this object. This is only used in
+ * by-reference geometry mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int
+ ALLOW_REF_DATA_READ =
+ CapabilityBits.COMPRESSED_GEOMETRY_ALLOW_REF_DATA_READ;
+
+ /**
+ * Package scoped default constructor for use by cloneNodeComponent.
+ */
+ CompressedGeometry() {
+ }
+
+ /**
+ * Creates a new CompressedGeometry NodeComponent by copying
+ * the specified compressed geometry data into this object.
+ * If the version number of compressed geometry, as specified by
+ * the CompressedGeometryHeader, is incompatible with the
+ * supported version of compressed geometry in the current version
+ * of Java 3D, then the compressed geometry object will not be
+ * rendered.
+ *
+ * @param hdr the compressed geometry header. This is copied
+ * into the CompressedGeometry NodeComponent.
+ *
+ * @param compressedGeometry the compressed geometry data. The
+ * geometry must conform to the format described in Appendix B of
+ * the <i>Java 3D API Specification</i>.
+ *
+ * @exception IllegalArgumentException if a problem is detected with the
+ * header
+ *
+ * @see CompressedGeometryHeader
+ * @see Canvas3D#queryProperties
+ */
+ public CompressedGeometry(CompressedGeometryHeader hdr,
+ byte[] compressedGeometry) {
+ this(hdr, compressedGeometry, false) ;
+ }
+
+ /**
+ * Creates a new CompressedGeometry NodeComponent. The
+ * specified compressed geometry data is either copied into this
+ * object or is accessed by reference.
+ * If the version number of compressed geometry, as specified by
+ * the CompressedGeometryHeader, is incompatible with the
+ * supported version of compressed geometry in the current version
+ * of Java 3D, the compressed geometry object will not be
+ * rendered.
+ *
+ * @param hdr the compressed geometry header. This is copied
+ * into the CompressedGeometry NodeComponent.
+ *
+ * @param compressedGeometry the compressed geometry data. The
+ * geometry must conform to the format described in Appendix B of
+ * the <i>Java 3D API Specification</i>.
+ *
+ * @param byReference a flag that indicates whether the data is copied
+ * into this compressed geometry object or is accessed by reference.
+ *
+ * @exception IllegalArgumentException if a problem is detected with the
+ * header
+ *
+ * @see CompressedGeometryHeader
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.2
+ */
+ public CompressedGeometry(CompressedGeometryHeader hdr,
+ byte[] compressedGeometry,
+ boolean byReference) {
+
+ if ((hdr.size + hdr.start) > compressedGeometry.length)
+ throw new IllegalArgumentException
+ (J3dI18N.getString("CompressedGeometry0")) ;
+
+ // Create a separate copy of the given header.
+ cgHeader = new CompressedGeometryHeader() ;
+ hdr.copy(cgHeader) ;
+
+ // Create the retained object.
+ ((CompressedGeometryRetained)this.retained).createCompressedGeometry
+ (cgHeader, compressedGeometry, byReference) ;
+
+ // This constructor is designed to accept byte arrays that may contain
+ // possibly many large compressed geometry blocks interspersed with
+ // non-J3D-specific metadata. Only one of these blocks is used per
+ // CompressedGeometry object, so set the geometry offset to zero in
+ // the header if the data itself is copied.
+ if (!byReference)
+ cgHeader.start = 0 ;
+ }
+
+ /**
+ * Creates a new CompressedGeometry NodeComponent. The
+ * specified compressed geometry data is accessed by reference
+ * from the specified buffer.
+ * If the version number of compressed geometry, as specified by
+ * the CompressedGeometryHeader, is incompatible with the
+ * supported version of compressed geometry in the current version
+ * of Java 3D, the compressed geometry object will not be
+ * rendered.
+ *
+ * @param hdr the compressed geometry header. This is copied
+ * into the CompressedGeometry NodeComponent.
+ *
+ * @param compressedGeometry a buffer containing an NIO byte buffer
+ * of compressed geometry data. The
+ * geometry must conform to the format described in Appendix B of
+ * the <i>Java 3D API Specification</i>.
+ *
+ * @exception UnsupportedOperationException this method is not
+ * yet implemented
+ *
+ * @exception IllegalArgumentException if a problem is detected with the
+ * header,
+ * or if the java.nio.Buffer contained in the specified J3DBuffer
+ * is not a java.nio.ByteBuffer object.
+ *
+ * @see CompressedGeometryHeader
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public CompressedGeometry(CompressedGeometryHeader hdr,
+ J3DBuffer compressedGeometry) {
+ throw new UnsupportedOperationException(J3dI18N.getString("CompressedGeometry9")) ;
+ }
+
+
+ /**
+ * Returns the size, in bytes, of the compressed geometry buffer.
+ * The size of the compressed geometry header is not included.
+ *
+ * @return the size, in bytes, of the compressed geometry buffer.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getByteCount() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException
+ (J3dI18N.getString("CompressedGeometry1")) ;
+
+ return cgHeader.size ;
+ }
+
+ /**
+ * Copies the compressed geometry header from the CompressedGeometry
+ * NodeComponent into the passed in parameter.
+ *
+ * @param hdr the CompressedGeometryHeader object into which to copy the
+ * CompressedGeometry NodeComponent's header; the offset field may differ
+ * from that which was originally specified if a copy of the original
+ * compressed geometry byte array was created.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see CompressedGeometryHeader
+ */
+ public void getCompressedGeometryHeader(CompressedGeometryHeader hdr) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_HEADER_READ))
+ throw new CapabilityNotSetException
+ (J3dI18N.getString("CompressedGeometry2")) ;
+
+ cgHeader.copy(hdr) ;
+ }
+
+ /**
+ * Retrieves the compressed geometry associated with the
+ * CompressedGeometry NodeComponent object. Copies the compressed
+ * geometry from the CompressedGeometry node into the given array.
+ * The array must be large enough to hold all of the bytes.
+ * The individual array elements must be allocated by the caller.
+ *
+ * @param compressedGeometry the array into which to copy the compressed
+ * geometry.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data access mode for this
+ * object is by-reference.
+ *
+ * @exception ArrayIndexOutOfBoundsException if compressedGeometry byte
+ * array is not large enough to receive the compressed geometry
+ */
+ public void getCompressedGeometry(byte[] compressedGeometry) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException
+ (J3dI18N.getString("CompressedGeometry3")) ;
+
+ if (isByReference())
+ throw new IllegalStateException
+ (J3dI18N.getString("CompressedGeometry7")) ;
+
+ if (cgHeader.size > compressedGeometry.length)
+ throw new ArrayIndexOutOfBoundsException
+ (J3dI18N.getString("CompressedGeometry4")) ;
+
+ ((CompressedGeometryRetained)this.retained).copy(compressedGeometry) ;
+ }
+
+ /**
+ * Decompresses the compressed geometry. Returns an array of Shape nodes
+ * containing the decompressed geometry objects, or null if the version
+ * number of the compressed geometry is incompatible with the decompressor
+ * in the current version of Java 3D.
+ *
+ * @return an array of Shape nodes containing the
+ * geometry decompressed from this CompressedGeometry NodeComponent
+ * object, or null if its version is incompatible
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Shape3D[] decompress() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException
+ (J3dI18N.getString("CompressedGeometry5")) ;
+
+ CompressedGeometryRetained cgr =
+ (CompressedGeometryRetained)this.retained ;
+
+ GeometryDecompressorShape3D decompressor =
+ new GeometryDecompressorShape3D() ;
+
+ // Decompress the geometry as TriangleStripArrays. A combination of
+ // TriangleStripArrays and TrianglesFanArrays is more compact but
+ // requires twice as many Shape3D objects, resulting in slower
+ // rendering performance.
+ //
+ // Using TriangleArray output is currently the fastest, given the
+ // strip sizes observed from various compressed geometry objects, but
+ // produces about twice as many vertices. TriangleStripArray produces
+ // the same number of Shape3D objects as TriangleArray using 1/2
+ // to 2/3 of the vertices, with only a marginal performance penalty.
+ //
+ // TODO revisit this
+ return decompressor.toTriangleStripArrays(cgr) ;
+ }
+
+
+ /**
+ * Retrieves the data access mode for this CompressedGeometry object.
+ *
+ * @return <code>true</code> if the data access mode for this
+ * CompressedGeometry object is by-reference;
+ * <code>false</code> if the data access mode is by-copying.
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean isByReference() {
+ return ((CompressedGeometryRetained)this.retained).isByReference() ;
+ }
+
+
+ /**
+ * Gets the compressed geometry data reference.
+ *
+ * @return the current compressed geometry data reference;
+ * null is returned if this compressed geometry object was created
+ * with a J3DBuffer reference rather than a byte array.
+ *
+ * @exception IllegalStateException if the data access mode for this
+ * object is not by-reference.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public byte[] getCompressedGeometryRef() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ))
+ throw new CapabilityNotSetException
+ (J3dI18N.getString("CompressedGeometry6")) ;
+
+ if (!isByReference())
+ throw new IllegalStateException
+ (J3dI18N.getString("CompressedGeometry8")) ;
+
+ return ((CompressedGeometryRetained)this.retained).getReference() ;
+ }
+
+
+ /**
+ * Gets the compressed geometry data buffer reference.
+ *
+ * @return the current compressed geometry data buffer reference;
+ * null is returned if this compressed geometry object was created
+ * with a byte array reference rather than a J3DBuffer.
+ *
+ * @exception IllegalStateException if the data access mode for this
+ * object is not by-reference.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public J3DBuffer getCompressedGeometryBuffer() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ))
+ throw new CapabilityNotSetException
+ (J3dI18N.getString("CompressedGeometry6")) ;
+
+ if (!isByReference())
+ throw new IllegalStateException
+ (J3dI18N.getString("CompressedGeometry8")) ;
+
+ // TODO: implement this when NIO buffer support is added
+ return null;
+ }
+
+
+ /**
+ * Creates the retained mode CompressedGeometryRetained object that this
+ * CompressedGeometry object will point to.
+ */
+ void createRetained() {
+ this.retained = new CompressedGeometryRetained() ;
+ this.retained.setSource(this) ;
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ CompressedGeometry cg = new CompressedGeometry() ;
+
+ // Duplicate data specific to this class.
+ cg.cgHeader = new CompressedGeometryHeader() ;
+ cgHeader.copy(cg.cgHeader) ;
+
+ // Duplicate the retained side.
+ CompressedGeometryRetained cgr = (CompressedGeometryRetained)retained ;
+ cgr.duplicate((CompressedGeometryRetained)cg.retained) ;
+
+ // Duplicate superclass data and return.
+ cg.duplicateNodeComponent(this) ;
+ return cg ;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/CompressedGeometryHeader.java b/src/classes/share/javax/media/j3d/CompressedGeometryHeader.java
new file mode 100644
index 0000000..afc4a79
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CompressedGeometryHeader.java
@@ -0,0 +1,239 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The CompressedGeometrHeader object is used in conjunction with
+ * the CompressedGeometry object. The CompressedGeometrHeader object
+ * contains information specific to the compressed geometry stored in
+ * CompressedGeometry NodeComponent object. This information
+ * is used to aid the decompression of the compressed geometry.
+ * <P>
+ * All instance data is declared public and no get or set methods are
+ * provided.
+ *
+ * @see CompressedGeometry
+ */
+public class CompressedGeometryHeader extends Object {
+
+ /**
+ * bufferType: compressed geometry is made up of individual points.
+ */
+ public static final int POINT_BUFFER = 0 ;
+
+ /**
+ * bufferType: compressed geometry is made up of line segments.
+ */
+ public static final int LINE_BUFFER = 1 ;
+
+ /**
+ * bufferType: compressed geometry is made up of triangles.
+ */
+ public static final int TRIANGLE_BUFFER = 2 ;
+
+ // Valid values for the bufferDataPresent field.
+
+ /**
+ * bufferDataPresent: bit indicating that normal information is
+ * bundled with the vertices in the compressed geometry buffer.
+ */
+ public static final int NORMAL_IN_BUFFER = 1 ;
+
+ /**
+ * bufferDataPresent: bit indicating that RGB color information is
+ * bundled with the vertices in the compressed geometry buffer.
+ */
+ public static final int COLOR_IN_BUFFER = 2 ;
+
+ /**
+ * bufferDataPresent: bit indicating that alpha information is
+ * bundled with the vertices in the compressed geometry buffer.
+ */
+ public static final int ALPHA_IN_BUFFER = 4 ;
+
+ /**
+ * The major version number for the compressed geometry format that
+ * was used to compress the geometry.
+ * If the version number of compressed geometry is incompatible
+ * with the supported version of compressed geometry in the
+ * current version of Java 3D, the compressed geometry obejct will
+ * not be rendered.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public int majorVersionNumber ;
+
+ /**
+ * The minor version number for the compressed geometry format that
+ * was used to compress the geometry.
+ * If the version number of compressed geometry is incompatible
+ * with the supported version of compressed geometry in the
+ * current version of Java 3D, the compressed geometry obejct will
+ * not be rendered.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public int minorVersionNumber ;
+
+ /**
+ * The minor-minor version number for the compressed geometry format
+ * that was used to compress the geometry.
+ * If the version number of compressed geometry is incompatible
+ * with the supported version of compressed geometry in the
+ * current version of Java 3D, the compressed geometry obejct will
+ * not be rendered.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public int minorMinorVersionNumber ;
+
+ /**
+ * Describes the type of data in the compressed geometry buffer.
+ * Only one type may be present in any given compressed geometry
+ * buffer.
+ */
+ public int bufferType ;
+
+ /**
+ * Contains bits indicating what data is bundled with the vertices in the
+ * compressed geometry buffer. If this data is not present (e.g. color)
+ * then this info will be inherited from the Appearance node.
+ */
+ public int bufferDataPresent ;
+
+ /**
+ * Size of the compressed geometry in bytes.
+ */
+ public int size ;
+
+ /**
+ * Offset in bytes of the start of the compressed geometry from the
+ * beginning of the compressed geometry byte array passed to the
+ * CompressedGeometry constructor. <p>
+ *
+ * If the CompressedGeometry is created with reference access semantics,
+ * then this allow external compressors or file readers to embed several
+ * blocks of compressed geometry in a single large byte array, possibly
+ * interspersed with metadata that is not specific to Java 3D, without
+ * having to copy each block to a separate byte array. <p>
+ *
+ * If the CompressedGeometry is created with copy access semantics, then
+ * <code>size</code> bytes of compressed geometry data are copied from the
+ * offset indicated by <code>start</code> instead of copying the entire
+ * byte array. The getCompressedGeometry() method will return only the
+ * bytes used to construct the object, and the getCompressedGeometryHeader()
+ * method will return a header with the <code>start</code> field set to 0.
+ */
+ public int start ;
+
+ /**
+ * A point that defines the lower bound of the <i>x</i>,
+ * <i>y</i>, and <i>z</i> components for all positions in the
+ * compressed geometry buffer. If null, a lower bound of
+ * (-1,-1,-1) is assumed. Java 3D will use this information to
+ * construct a bounding box around compressed geometry objects
+ * that are used in nodes for which the auto compute bounds flag
+ * is true. The default value for this point is null.
+ *
+ * @since Java 3D 1.2
+ */
+ public Point3d lowerBound = null ;
+
+ /**
+ * A point that defines the upper bound of the <i>x</i>,
+ * <i>y</i>, and <i>z</i> components for all positions in the
+ * compressed geometry buffer. If null, an upper bound of (1,1,1)
+ * is assumed. Java 3D will use this information to construct a
+ * bounding box around compressed geometry objects that are used
+ * in nodes for which the auto compute bounds flag is true. The
+ * default value for this point is null.
+ *
+ * @since Java 3D 1.2
+ */
+ public Point3d upperBound = null ;
+
+ /**
+ * Creates a new CompressedGeometryHeader object used for the
+ * creation of a CompressedGeometry NodeComponent object.
+ * All instance data is declared public and no get or set methods are
+ * provided. All values are set to 0 by default and must be filled
+ * in by the application.
+ *
+ * @see CompressedGeometry
+ */
+ public CompressedGeometryHeader() {
+ }
+
+ /**
+ * Package-scoped method to copy current CompressedGeometryHeader object
+ * to the passed-in CompressedGeometryHeader object.
+ *
+ * @param hdr the CompressedGeometryHeader object into which to copy the
+ * current CompressedGeometryHeader.
+ */
+ void copy(CompressedGeometryHeader hdr) {
+ hdr.majorVersionNumber = this.majorVersionNumber ;
+ hdr.minorVersionNumber = this.minorVersionNumber ;
+ hdr.minorMinorVersionNumber = this.minorMinorVersionNumber ;
+ hdr.bufferType = this.bufferType ;
+ hdr.bufferDataPresent = this.bufferDataPresent ;
+ hdr.size = this.size ;
+ hdr.start = this.start ;
+ hdr.lowerBound = this.lowerBound ;
+ hdr.upperBound = this.upperBound ;
+ }
+
+ /**
+ * Returns a String describing the contents of the
+ * CompressedGeometryHeader object.
+ *
+ * @return a String describing contents of the compressed geometry header
+ */
+ public String toString() {
+ String type = "UNKNOWN" ;
+ switch (bufferType) {
+ case POINT_BUFFER: type = "POINT_BUFFER" ; break ;
+ case LINE_BUFFER: type = "LINE_BUFFER" ; break ;
+ case TRIANGLE_BUFFER: type = "TRIANGLE_BUFFER" ; break ;
+ }
+
+ String data = "" ;
+ if ((bufferDataPresent & NORMAL_IN_BUFFER) != 0)
+ data = data + "NORMALS " ;
+ if ((bufferDataPresent & COLOR_IN_BUFFER) != 0)
+ data = data + "COLORS " ;
+ if ((bufferDataPresent & ALPHA_IN_BUFFER) != 0)
+ data = data + "ALPHA " ;
+
+ String lbound = "null" ;
+ if (lowerBound != null)
+ lbound = lowerBound.toString() ;
+
+ String ubound = "null" ;
+ if (upperBound != null)
+ ubound = upperBound.toString() ;
+
+ return
+ "majorVersionNumber: " + majorVersionNumber + " " +
+ "minorVersionNumber: " + minorVersionNumber + " " +
+ "minorMinorVersionNumber: " + minorMinorVersionNumber + "\n" +
+ "bufferType: " + type + " " +
+ "bufferDataPresent: " + data + "\n" +
+ "size: " + size + " " +
+ "start: " + start + "\n" +
+ "lower bound: " + lbound + "\n" +
+ "upper bound: " + ubound + " " ;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/CompressedGeometryRenderMethod.java b/src/classes/share/javax/media/j3d/CompressedGeometryRenderMethod.java
new file mode 100644
index 0000000..0080ca1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CompressedGeometryRenderMethod.java
@@ -0,0 +1,103 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d ;
+
+/**
+ * The RenderMethod interface is used to create various ways to render
+ * different geometries.
+ */
+
+class CompressedGeometryRenderMethod implements RenderMethod {
+
+ /**
+ * The actual rendering code for this RenderMethod.
+ */
+ public boolean render(RenderMolecule rm, Canvas3D cv, int pass,
+ RenderAtomListInfo ra, int dirtyBits) {
+
+ CompressedGeometryRetained cgr ;
+
+
+ if (rm.doInfinite) {
+ cv.updateState(pass, dirtyBits);
+ while (ra != null) {
+ renderCompressedGeo(ra, rm, cv);
+ ra = ra.next;
+ }
+ return true;
+ }
+
+ boolean isVisible = false; // True if any of the RAs is visible.
+
+ while (ra != null) {
+ if (cv.ra == ra.renderAtom) {
+ if (cv.raIsVisible) {
+ cv.updateState(pass, dirtyBits);
+ renderCompressedGeo(ra, rm, cv);
+ isVisible = true;
+ }
+ }
+ else {
+ if (ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) {
+ cv.updateState(pass, dirtyBits);
+ cv.raIsVisible = true;
+ renderCompressedGeo(ra, rm, cv);
+ isVisible = true;
+ }
+ else {
+ cv.raIsVisible = false;
+ }
+ cv.ra = ra.renderAtom;
+ }
+
+ ra = ra.next;
+ }
+
+ return isVisible;
+
+ }
+
+ void renderCompressedGeo(RenderAtomListInfo ra, RenderMolecule rm, Canvas3D cv) {
+
+ boolean useAlpha ;
+ CompressedGeometryRetained cgr ;
+ useAlpha = rm.useAlpha ;
+
+ cgr = (CompressedGeometryRetained)ra.renderAtom.geometryAtom.geometryArray[ra.index];
+
+ /* force_decompression if lighting is disabled and
+ * ignoreVertexColors is TRUE, since there is no way for openGL
+ * to ignore vertexColors in this case, force decompression
+ */
+ if (rm.textureBin.attributeBin.ignoreVertexColors && rm.enableLighting == false && cgr.mirrorGeometry == null) {
+ cgr.mirrorGeometry = cgr.getGeometry(true, cv) ;
+ }
+ else if (cgr.mirrorGeometry == null) {
+ // cgr.getGeometry() will decompress in software and return a
+ // GeometryRetained if hardware decompression isn't available,
+ // otherwise it just returns cgr.
+ cgr.mirrorGeometry = cgr.getGeometry(false, cv) ;
+ if (cgr.mirrorGeometry == null)
+ // decompressor error
+ return ;
+ }
+
+ cgr.mirrorGeometry.execute
+ (cv, ra.renderAtom, rm.isNonUniformScale,
+ (useAlpha && ra.geometry().noAlpha), rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.ignoreVertexColors,
+ -1) ;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/CompressedGeometryRetained.java b/src/classes/share/javax/media/j3d/CompressedGeometryRetained.java
new file mode 100644
index 0000000..20b28fe
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CompressedGeometryRetained.java
@@ -0,0 +1,439 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d ;
+import javax.vecmath.* ;
+
+/**
+ * The compressed geometry object is used to store geometry in a
+ * compressed format. Using compressed geometry reduces the amount
+ * of memory needed by a Java 3D application and increases the speed
+ * objects can be sent over the network. Once geometry decompression
+ * hardware support becomes available, increased rendering performance
+ * will also result from the use of compressed geometry.
+ */
+class CompressedGeometryRetained extends GeometryRetained {
+
+ // If not in by-reference mode, a 48-byte header as defined by the
+ // GL_SUNX_geometry_compression OpenGL extension is always concatenated to
+ // the beginning of the compressed geometry data and copied along with the
+ // it into a contiguous array. This allows hardware decompression using
+ // the obsolete experimental GL_SUNX_geometry_compression extension if
+ // that is all that is available.
+ //
+ // This is completely distinct and not to be confused with the cgHeader
+ // field on the non-retained side, although much of the data is
+ // essentially the same.
+ private static final int HEADER_LENGTH = 48 ;
+
+ // These are the header locations examined.
+ private static final int HEADER_MAJOR_VERSION_OFFSET = 0 ;
+ private static final int HEADER_MINOR_VERSION_OFFSET = 1 ;
+ private static final int HEADER_MINOR_MINOR_VERSION_OFFSET = 2 ;
+ private static final int HEADER_BUFFER_TYPE_OFFSET = 3 ;
+ private static final int HEADER_BUFFER_DATA_OFFSET = 4 ;
+
+ // The OpenGL compressed geometry extensions use bits instead of
+ // enumerations to represent the type of compressed geometry.
+ static final byte TYPE_POINT = 1 ;
+ static final byte TYPE_LINE = 2 ;
+ static final byte TYPE_TRIANGLE = 4 ;
+
+ // Version number of this compressed geometry object.
+ int majorVersionNumber ;
+ int minorVersionNumber ;
+ int minorMinorVersionNumber ;
+
+ // These fields are used by the native execute() method.
+ int packedVersion ;
+ int bufferType ;
+ int bufferContents ;
+ int renderFlags ;
+ int offset ;
+ int size ;
+ byte[] compressedGeometry ;
+
+ // True if by-reference data access mode is in effect.
+ private boolean byReference = false ;
+
+ // A reference to the original byte array with which this object was
+ // created. If hardware decompression is available but it doesn't support
+ // by-reference semantics, then an internal copy of the original byte array
+ // is made even when by-reference semantics have been requested.
+ private byte[] originalCompressedGeometry = null ;
+
+ // True if the platform supports hardware decompression.
+ private static boolean hardwareDecompression = false ;
+
+ // This field retains a reference to the GeometryRetained object used for
+ // geometry-based picking. It is normally the same reference as the
+ // mirror geometry used for rendering unless hardware decompression is
+ // supported.
+ private GeometryRetained pickGeometry = null ;
+
+ /**
+ * Native method that returns availability of a native by-reference
+ * rendering API for compressed geometry.
+ */
+ native boolean decompressByRef(long ctx) ;
+
+ /**
+ * Native method that returns availability of hardware acceleration for
+ * compressed geometry of the given version.
+ */
+ native boolean decompressHW(long ctx, int majorVersion, int minorVersion) ;
+
+ /**
+ * Native method that does the rendering
+ */
+ native void execute(long ctx, int version, int bufferType,
+ int bufferContents, int renderFlags,
+ int offset, int size, byte[] geometry) ;
+
+ /**
+ * Method for calling native execute() method on behalf of the J3D renderer.
+ */
+ void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha,
+ boolean multiScreen, int screen,
+ boolean ignoreVertexColors, int pass) {
+
+ // TODO: alpha udpate
+ execute(cv.ctx, packedVersion, bufferType, bufferContents,
+ renderFlags, offset, size, compressedGeometry) ;
+ }
+
+ /**
+ * The package-scoped constructor.
+ */
+ CompressedGeometryRetained() {
+ this.geoType = GEO_TYPE_COMPRESSED ;
+
+ // Compressed geometry is always bounded by [-1..1] on each axis, so
+ // set that as the initial bounding box.
+ geoBounds.setUpper( 1.0, 1.0, 1.0) ;
+ geoBounds.setLower(-1.0,-1.0,-1.0) ;
+ }
+
+ /**
+ * Compressed geometry is immutable so this method does nothing.
+ */
+ void computeBoundingBox() {
+ }
+
+ /**
+ * Update this object. Compressed geometry is immutable so there's
+ * nothing to do.
+ */
+ void update() {
+ isDirty = 0 ;
+ }
+
+ /**
+ * Return true if the data access mode is by-reference.
+ */
+ boolean isByReference() {
+ return this.byReference ;
+ }
+
+ private void createByCopy(byte[] geometry) {
+ // Always copy a header along with the compressed geometry into a
+ // contiguous array in order to support hardware acceleration with the
+ // GL_SUNX_geometry_compression extension. The header is unnecessary
+ // if only the newer GL_SUN_geometry_compression API needs support.
+ compressedGeometry = new byte[HEADER_LENGTH + this.size] ;
+
+ compressedGeometry[HEADER_MAJOR_VERSION_OFFSET] =
+ (byte)this.majorVersionNumber ;
+
+ compressedGeometry[HEADER_MINOR_VERSION_OFFSET] =
+ (byte)this.minorVersionNumber ;
+
+ compressedGeometry[HEADER_MINOR_MINOR_VERSION_OFFSET] =
+ (byte)this.minorMinorVersionNumber ;
+
+ compressedGeometry[HEADER_BUFFER_TYPE_OFFSET] =
+ (byte)this.bufferType ;
+
+ compressedGeometry[HEADER_BUFFER_DATA_OFFSET] =
+ (byte)this.bufferContents ;
+
+ System.arraycopy(geometry, this.offset,
+ compressedGeometry, HEADER_LENGTH, this.size) ;
+
+ this.offset = HEADER_LENGTH ;
+ }
+
+ /**
+ * Creates the retained compressed geometry data. Data from the header is
+ * always copied; the compressed geometry is copied as well if the data
+ * access mode is not by-reference.
+ *
+ * @param hdr the compressed geometry header
+ * @param geometry the compressed geometry
+ * @param byReference if true then by-reference semantics requested
+ */
+ void createCompressedGeometry(CompressedGeometryHeader hdr,
+ byte[] geometry, boolean byReference) {
+
+ this.byReference = byReference ;
+
+ if (hdr.lowerBound != null)
+ this.geoBounds.setLower(hdr.lowerBound) ;
+
+ if (hdr.upperBound != null)
+ this.geoBounds.setUpper(hdr.upperBound) ;
+
+ this.centroid.set(geoBounds.getCenter());
+ recompCentroid = false;
+ this.majorVersionNumber = hdr.majorVersionNumber ;
+ this.minorVersionNumber = hdr.minorVersionNumber ;
+ this.minorMinorVersionNumber = hdr.minorMinorVersionNumber ;
+
+ this.packedVersion =
+ (hdr.majorVersionNumber << 24) |
+ (hdr.minorVersionNumber << 16) |
+ (hdr.minorMinorVersionNumber << 8) ;
+
+ switch(hdr.bufferType) {
+ case CompressedGeometryHeader.POINT_BUFFER:
+ this.bufferType = TYPE_POINT ;
+ break ;
+ case CompressedGeometryHeader.LINE_BUFFER:
+ this.bufferType = TYPE_LINE ;
+ break ;
+ case CompressedGeometryHeader.TRIANGLE_BUFFER:
+ this.bufferType = TYPE_TRIANGLE ;
+ break ;
+ }
+
+ this.bufferContents = hdr.bufferDataPresent ;
+ this.renderFlags = 0 ;
+
+ this.size = hdr.size ;
+ this.offset = hdr.start ;
+
+ if (byReference) {
+ // Assume we can use the given reference, but maintain a second
+ // reference in case a copy is later needed.
+ this.compressedGeometry = geometry ;
+ this.originalCompressedGeometry = geometry ;
+ } else {
+ // Copy the original data into a format that can be used by both
+ // the software and native hardware decompressors.
+ createByCopy(geometry) ;
+ this.originalCompressedGeometry = null ;
+ }
+ }
+
+ /**
+ * Decompress this object into a GeometryArrayRetained if hardware
+ * decompression is not available. Once decompressed the resulting
+ * geometry replaces the geometry reference in the associated RenderAtom
+ * as well as the mirror geometry reference in this object.
+ */
+ GeometryRetained getGeometry(boolean forceDecompression, Canvas3D cv ) {
+
+ if (forceDecompression) {
+ // forceDecompression is set to true if lighting is disabled and
+ // ignoreVertexColors is true, since there is no way for openGL to
+ // ignore vertexColors in this case.
+ GeometryDecompressorRetained gdr =
+ new GeometryDecompressorRetained() ;
+
+ mirrorGeometry = gdr.decompress(this) ;
+ gdr.getBoundingBox(geoBounds) ;
+ pickGeometry = mirrorGeometry ;
+ }
+ else {
+ // Return this object if hardware decompression is available.
+ if (hardwareDecompression)
+ return (GeometryRetained)this ;
+
+ // Check to see if hardware decompression is available.
+ if (decompressHW(cv.ctx, majorVersionNumber, minorVersionNumber)) {
+ hardwareDecompression = true ;
+
+ // If hardware can't handle by-reference, punt to by-copy.
+ if (isByReference() && !decompressByRef(cv.ctx)) {
+ createByCopy(compressedGeometry) ;
+ }
+
+ return (GeometryRetained)this ;
+ }
+
+ // Decompress the data into a GeometryArrayRetained representation
+ // for the mirror geometry reference.
+ GeometryDecompressorRetained gdr =
+ new GeometryDecompressorRetained() ;
+
+ mirrorGeometry = gdr.decompress(this) ;
+ gdr.getBoundingBox(geoBounds) ;
+
+ // The mirror geometry contains a superset of the pick geometry
+ // data. Since hardware decompression isn't available, there's no
+ // need to retain separate pick geometry.
+ pickGeometry = mirrorGeometry ;
+ }
+
+ return mirrorGeometry ;
+ }
+
+ /**
+ * This method always decompresses the geometry and retains the result in
+ * order to support geometry-based picking and collision detection. The
+ * returned GeometryRetained object will contain only positions and
+ * connections.
+ */
+ GeometryRetained getPickGeometry() {
+ // Return the pick geometry if available.
+ if (pickGeometry != null)
+ return pickGeometry ;
+
+ // Decompress the data into a GeometryArrayRetained representation for
+ // the pick geometry reference. Retain it and its bounding box.
+ GeometryDecompressorRetained gdr = new GeometryDecompressorRetained() ;
+ gdr.setDecompressPositionsOnly(true) ;
+
+ pickGeometry = gdr.decompress(this) ;
+ gdr.getBoundingBox(geoBounds) ;
+ return pickGeometry ;
+ }
+
+ //
+ // The following intersect() methods are used to implement geometry-based
+ // picking and collision.
+ //
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ GeometryRetained geom = getPickGeometry() ;
+ return (geom != null ?
+ geom.intersect(pickShape, dist, iPnt) : false);
+ }
+
+ boolean intersect(Bounds targetBound) {
+ GeometryRetained geom = getPickGeometry() ;
+ return (geom != null ? geom.intersect(targetBound) : false);
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained g) {
+ GeometryRetained geom = getPickGeometry() ;
+ return (geom != null ?
+ geom.intersect(thisToOtherVworld, g) : false);
+ }
+
+ boolean intersect(Point3d[] pnts) {
+ GeometryRetained geom = getPickGeometry() ;
+ return (geom != null ? geom.intersect(pnts) : false);
+ }
+
+ /**
+ * Return a vertex format mask that's compatible with GeometryArray
+ * objects.
+ */
+ int getVertexFormat() {
+ int vertexFormat = GeometryArray.COORDINATES ;
+
+ if ((this.bufferContents &
+ CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
+ vertexFormat |= GeometryArray.NORMALS ;
+
+ if ((this.bufferContents &
+ CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
+ vertexFormat |= GeometryArray.COLOR ;
+
+ if ((this.bufferContents &
+ CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0)
+ vertexFormat |= GeometryArray.WITH_ALPHA ;
+
+ return vertexFormat ;
+ }
+
+ /**
+ * Return a buffer type that's compatible with CompressedGeometryHeader.
+ */
+ int getBufferType() {
+ switch(this.bufferType) {
+ case TYPE_POINT:
+ return CompressedGeometryHeader.POINT_BUFFER ;
+ case TYPE_LINE:
+ return CompressedGeometryHeader.LINE_BUFFER ;
+ default:
+ case TYPE_TRIANGLE:
+ return CompressedGeometryHeader.TRIANGLE_BUFFER ;
+ }
+ }
+
+ /**
+ * Copies compressed geometry data into the given array of bytes.
+ * The internal header information is not copied.
+ *
+ * @param buff array of bytes into which to copy compressed geometry
+ */
+ void copy(byte[] buff) {
+ System.arraycopy(compressedGeometry, offset, buff, 0, size) ;
+ }
+
+ /**
+ * Returns a reference to the original compressed geometry byte array,
+ * which may have been copied even if by-reference semantics have been
+ * requested. It will be null if byCopy is in effect.
+ *
+ * @return reference to array of bytes containing the compressed geometry.
+ */
+ byte[] getReference() {
+ return originalCompressedGeometry ;
+ }
+
+ /**
+ * Copies all retained data for cloneNodeComponent() on the non-retained
+ * side. This is unlike GeometryArray subclasses which just call the
+ * public API constructors and then duplicateNodeComponent() to invoke the
+ * GeometryArray implementation of duplicateAttributes(), since the
+ * CompressedGeometry class directly subclasses Geometry and calling the
+ * public constructors would cause a lot of redundant data copying.
+ */
+ void duplicate(CompressedGeometryRetained cgr) {
+ cgr.majorVersionNumber = this.majorVersionNumber ;
+ cgr.minorVersionNumber = this.minorVersionNumber ;
+ cgr.minorMinorVersionNumber = this.minorMinorVersionNumber ;
+
+ cgr.packedVersion = this.packedVersion ;
+ cgr.bufferType = this.bufferType ;
+ cgr.bufferContents = this.bufferContents ;
+ cgr.renderFlags = this.renderFlags ;
+
+ cgr.offset = this.offset ;
+ cgr.size= this.size ;
+
+ cgr.geoBounds.setLower(this.geoBounds.lower) ;
+ cgr.geoBounds.setUpper(this.geoBounds.upper) ;
+ cgr.pickGeometry = this.pickGeometry ;
+ cgr.byReference = this.byReference ;
+
+ if (this.byReference) {
+ // Copy references only.
+ cgr.compressedGeometry = this.compressedGeometry ;
+ cgr.originalCompressedGeometry = this.originalCompressedGeometry ;
+ } else {
+ // Copy entire byte array including 48-byte native OpenGL header.
+ cgr.compressedGeometry = new byte[this.compressedGeometry.length] ;
+ System.arraycopy(this.compressedGeometry, 0,
+ cgr.compressedGeometry, 0,
+ this.compressedGeometry.length) ;
+ cgr.originalCompressedGeometry = null ;
+ }
+ }
+
+ int getClassType() {
+ return COMPRESS_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ConeSound.java b/src/classes/share/javax/media/j3d/ConeSound.java
new file mode 100644
index 0000000..d4fe4a5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ConeSound.java
@@ -0,0 +1,903 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.lang.Math;
+import javax.vecmath.Point2f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+
+/**
+ * The ConeSound node object defines a PointSound node whose sound source is
+ * directed along a specific vector in space. A ConeSound source is attenuated
+ * by gain scale factors and filters based on the angle between the vector from
+ * the source to the listener, and the ConeSound's direction vector. This
+ * attenuation is either a single spherical distance gain attenuation (as for
+ * a general PointSound source) or dual front and back distance gain
+ * attenuations defining elliptical attenuation areas. The angular filter and the
+ * active AuralAttribute component filter define what filtering is applied to
+ * the sound source. (See AuralAtttribute class for more details on filtering.)
+ * This node has the same attributes as a PointSound node with the addition of a
+ * direction vector and an array of points each containing: angular distance (in
+ * radians), gain scale factor, and filter (which for now consists of a lowpass
+ * filter cutoff frequency). Similar to the definition of the back distance gain
+ * array for PointSounds, a piece-wise linear curve (defined in terms of
+ * radians from the axis) specifies the slope of these additional attenuation
+ * values.
+ * <P>
+ * Distance Gain attuation
+ * <P><UL>
+ * A cone sound node can have one or two distance attenuation arrays.
+ * If none are set, no distance gain attenuation is performed (equivalent
+ * to using a distance gain of 1.0 for all distances). If only one distance
+ * attenuation array is set, sphere attenuation is assumed. If both a front
+ * and back distance attenuation are set, elliptical attenuation regions
+ * are defined.
+ *<P>
+ * Use PointSound setDistanceGain() method to set the front distance
+ * attenuation array separate from the back distance attenuation array.
+ * A front distance attenuation array defines monotonically-increasing
+ * distances from the sound source origin along the position direction
+ * vector. A back distance attenuation array (if given) defines
+ * monotonically-increasing distances from the sound source origin along the
+ * negative direction vector. The two arrays must be of the same length.
+ * The backDistance[i] gain values must be less than or equal to
+ * the frontDistance[i] gain values.
+ * <P>
+ * Gain scale factors are associated with distances from the listener to
+ * the sound source via an array of (distance, gain-scale-factor) pairs.
+ * The gain scale factor applied to the sound source is the linear
+ * interpolated gain value between the distance value range that includes
+ * the current distance from the listener to the sound source.
+ *<P>
+ * The getDistanceGainLength method defined for PointSound returns the length
+ * of the all distance gain attenuation arrays, including the back distance
+ * gain arrays. Arrays passed into getDistanceGain methods should all
+ * be at least this size.
+ * </UL> <P>
+ * Direction Methods
+ * <P><UL>
+ * This value is the sound source's direction vector. It is the axis from
+ * which angular distance is measured.
+ * </UL><P>
+ * Angular Attenuation
+ * <P><UL>
+ * Besides sound (linear) distance attenuation a ConeSound can optionally
+ * define angular gain and filter attenuation.
+ * <P>
+ * This attenuation is defined
+ * as a triple of (angular distance, gain-scale-factor, filter). The
+ * distance is measured as the angle in radians between the ConeSound's
+ * direction vector and the vector from the sound source position to the
+ * listener. Both the gain scale factor and filter applied to the sound
+ * source is the linear interpolation of values between the distance value
+ * range that includes the angular distance from the sound source axis.
+ *<P>
+ * If this is not set, no angular gain attenuation or filtering is performed
+ * (equivalent to using an angular gain scale factor of 1.0 and an angular
+ * filter of Sound.NO_FILTER for all distances).
+ * <P>
+ * If angular distance from the listener-sound-position vector and a sound's
+ * direction vector is less than the first distance in the array, only the first
+ * gain scale factor and first filter are applied to the sound source.
+ * This creates a conical region around the listener within which the sound
+ * is uniformly attenuated by first gain and first filter in the array.
+ * <P>
+ * If the distance from the listener-sound-position vector and the sound's
+ * direction vector is greater than the last distance in the array, the last gain
+ * scale factor and last filter are applied to the sound source.
+ * <P>
+ * Distance elements in this array of points is a monotonically-increasing
+ * set of floating point numbers measured from 0 to p radians. Gain scale
+ * factors elements in this list of points can be any positive floating
+ * point numbers. While for most applications this list of gain scale
+ * factors will usually be monotonically-decreasing, they do not have to be.
+ * The filter (for now) is a single simple frequency cutoff value.
+ * <P>
+ * The getAngularAttenuationArrayLength method returns the length of the angular
+ * attenuation arrays. Arrays passed into getAngularAttenuation methods
+ * should all be at least this size.
+ *</UL>
+ */
+
+public class ConeSound extends PointSound {
+ // Constants
+ //
+ // These flags, when enabled using the setCapability method, allow an
+ // application to invoke methods that respectively read and write direction
+ // and angular attenuation array. These capability flags are enforced only
+ // when the node is part of a live or compiled scene graph.
+
+ /**
+ * Specifies that this ConeSound allows access to its object's direction
+ * information.
+ */
+ public static final int
+ ALLOW_DIRECTION_READ = CapabilityBits.CONE_SOUND_ALLOW_DIRECTION_READ;
+
+ /**
+ * Specifies that this ConeSound allows writing to its object's direction
+ * information.
+ */
+ public static final int
+ ALLOW_DIRECTION_WRITE = CapabilityBits.CONE_SOUND_ALLOW_DIRECTION_WRITE;
+
+ /**
+ * Specifies that this ConeSound allows access to its object's cone params
+ * information.
+ */
+ public static final int
+ ALLOW_ANGULAR_ATTENUATION_READ = CapabilityBits.CONE_SOUND_ALLOW_ANGULAR_ATTENUATION_READ;
+
+ /**
+ * Specifies that this ConeSound allows writing to its object's cone params
+ * information.
+ */
+ public static final int
+ ALLOW_ANGULAR_ATTENUATION_WRITE = CapabilityBits.CONE_SOUND_ALLOW_ANGULAR_ATTENUATION_WRITE;
+
+ /**
+ * Constructs and initializes a new ConeSound node using default
+ * parameters. The following default values are used:
+ * <ul>
+ * Direction vector: (0.0, 0.0, 1.0) <br>
+ * Angular attenuation:
+ * ((0.0, 1.0, Sound.NO_FILTER),(p/2, 0.0, Sound.NO_FILTER)) <br>
+ * </ul>
+ */
+ public ConeSound() {
+ // Uses default values defined in ConeSoundRetained.java
+ super();
+ }
+
+ /**
+ * Constructs a ConeSound node object using only the provided parameter
+ * values for sound, overall initial gain, position, and direction. The
+ * remaining fields are set to the default values above. This form uses
+ * Point3f as input for its position and Vector3f for direction.
+ * @param soundData sound source data associated with this node
+ * @param initialGain amplitude scale factor applied to sound
+ * @param position 3D location of source
+ * @param direction 3D vector defining cone's axis
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ Point3f position,
+ Vector3f direction) {
+
+ super(soundData, initialGain, position );
+ ((ConeSoundRetained)this.retained).setDirection(direction);
+ }
+
+ /**
+ * Constructs a ConeSound node object using only the provided parameter
+ * values for sound, overall initial gain, position, and direction. The
+ * remaining fields are set to the default values above. This form uses
+ * individual float parameters for the elements of the position and
+ * direction vectors.
+ * @param soundData sound source data
+ * @param initialGain amplitude scale factor applied to sound
+ * @param posX x coordinate of location of source
+ * @param posY y coordinate of location of source
+ * @param posZ z coordinate of location of source
+ * @param dirX x coordinate cones' axii vector
+ * @param dirY y coordinate cones' axii vector
+ * @param dirZ z coordinate cones' axii vector
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ float posX, float posY, float posZ,
+ float dirX, float dirY, float dirZ) {
+
+ super(soundData, initialGain, posX, posY, posZ );
+ ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
+ }
+
+ /**
+ * Constructs a ConeSound node object using all the provided PointSound
+ * parameter values. This form uses points or vectors as input for its
+ * position, direction, and front/back distance attenuation arrays.
+ *<P>
+ * Unlike the single distance gain attenuation array for PointSounds which
+ * define spherical areas about the sound source between which gains are
+ * linearly interpolated, this directed ConeSound can have two distance gain
+ * attenuation arrays that define ellipsoidal attenuation areas. See the
+ * setDistanceGain PointSound method for details on how the separate distance
+ * and distanceGain arrays are interpreted.
+ *<P>
+ * The ConeSound's direction vector and angular measurements are defined in
+ * the local coordinate system of the node.
+ * @param soundData sound source data associated with this node
+ * @param initialGain amplitude scale factor applied to sound
+ * @param loopCount number of times sound is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param position 3D location of source
+ * @param frontDistanceAttenuation array of (distance,gain) pairs controlling
+ * attenuation values along the positive direction axis
+ * @param backDistanceAttenuation array of (distance,gain) pairs controlling
+ * attenuation values along the negative direction axis
+ * @param direction vector defining cones' axii
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ Point3f position,
+ Point2f[] frontDistanceAttenuation,
+ Point2f[] backDistanceAttenuation,
+ Vector3f direction) {
+
+ super(soundData, initialGain, loopCount, release, continuous, enable,
+ region, priority, position, frontDistanceAttenuation );
+ ((ConeSoundRetained)this.retained).setBackDistanceGain(
+ backDistanceAttenuation);
+ ((ConeSoundRetained)this.retained).setDirection(direction);
+ }
+
+ /**
+ * Constructs a ConeSound node object using the provided parameter values.
+ * This form uses individual float parameters for the elements of the
+ * position, direction, and two distance attenuation arrays.
+ * Unlike the single distance gain attenuation array for PointSounds, which
+ * define spherical areas about the sound source between which gains are
+ * linearly interpolated, this directed ConeSound can have two distance
+ * gain attenuation arrays that define ellipsoidal attenuation areas.
+ * See the setDistanceGain PointSound method for details on how the
+ * separate distance and distanceGain arrays are interpreted.
+ * The ConeSound's direction vector and angular measurements are defined
+ * in the local coordinate system of the node.
+ * @param soundData sound source data associated with this node
+ * @param initialGain amplitude scale factor applied to sound
+ * @param loopCount number of times sound is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param posX x coordinate of location of source
+ * @param posY y coordinate of location of source
+ * @param posZ z coordinate of location of source
+ * @param frontDistance array of front distance values used for attenuation
+ * @param frontDistanceGain array of front gain scale factors used for attenuation
+ * @param backDistance array of back distance values used for attenuation
+ * @param backDistanceGain array of back gain scale factors used for attenuation
+ * @param dirX x coordinate cones' axii vector
+ * @param dirY y coordinate cones' axii vector
+ * @param dirZ z coordinate cones' axii vector
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ float posX, float posY, float posZ,
+ float[] frontDistance,
+ float[] frontDistanceGain,
+ float[] backDistance,
+ float[] backDistanceGain,
+ float dirX, float dirY, float dirZ ) {
+ super(soundData, initialGain, loopCount, release, continuous, enable,
+ region, priority, posX, posY, posZ,
+ frontDistance, frontDistanceGain );
+ ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
+ ((ConeSoundRetained)this.retained).setBackDistanceGain(
+ backDistance, backDistanceGain );
+ }
+
+ /**
+ * Constructs a ConeSound node object using all the provided PointSound
+ * parameter values, which include a single spherical distance attenuation
+ * array, but includes an angular attenuation array.
+ * This form uses points and vectors as input for its position, direction,
+ * single spherical distanceAttenuation array, and angularAttenuation array.
+ * It also accepts arrays of points for the distance attenuation and angular
+ * values. Each Point2f in the distanceAttenuation array contains a distance
+ * and a gain scale factor. Each Point3f in the angularAttenuation array
+ * contains an angular distance, a gain scale factor, and a filtering value
+ * (which is currently defined as a simple cutoff frequency).
+ * @param soundData sound source data associated with this node
+ * @param initialGain amplitude scale factor applied to sound
+ * @param loopCount number of times sound is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param position 3D location of source
+ * @param distanceAttenuation array of (distance,gain) pairs controlling
+ * attenuation values along the positive direction axis
+ * @param direction vector defining cones' axii
+ * @param angularAttenuation array of tuples defining angular gain/filtering
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ Point3f position,
+ Point2f[] distanceAttenuation,
+ Vector3f direction,
+ Point3f[] angularAttenuation ) {
+
+ super(soundData, initialGain, loopCount, release, continuous, enable,
+ region, priority, position, distanceAttenuation );
+ ((ConeSoundRetained)this.retained).setDirection(direction);
+ ((ConeSoundRetained)this.retained).setAngularAttenuation(
+ angularAttenuation);
+ }
+
+ /**
+ * Constructs a ConeSound node object using all the provided PointSound
+ * parameter values, which include a single spherical distance attenuation
+ * array, but includes an angular attenuation array.
+ * This form uses individual float parameters for elements of position,
+ * direction, distanceAttenuation array, and angularAttenuation array.
+ * It also accepts separate arrays for the distance and gain scale factors
+ * components of distance attenuation, and separate arrays for the angular
+ * distance, angular gain, and filtering components of angular attenuation.
+ * See the setDistanceGain ConeSound method for details on how the separate
+ * distance and distanceGain arrays are interpreted. See the
+ * setAngularAttenuation ConeSound method for details on how the separate
+ * angularDistance, angularGain, and filter arrays are interpreted.
+ * @param soundData sound source data associated with this node
+ * @param initialGain amplitude scale factor applied to sound
+ * @param loopCount number of times sound is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param posX x coordinate of location of source
+ * @param posY y coordinate of location of source
+ * @param posZ z coordinate of location of source
+ * @param distance array of front distance values used for attenuation
+ * @param distanceGain array of front gain scale factors used for attenuation
+ * @param dirX x coordinate cones' axii vector
+ * @param dirY y coordinate cones' axii vector
+ * @param dirZ z coordinate cones' axii vector
+ * @param angle array of angle radians for angularAttenuation
+ * @param angularGain array of gain scale factors for angularAttenuation
+ * @param frequencyCutoff array of lowpass filter values in Hertz
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ float posX, float posY, float posZ,
+ float[] distance,
+ float[] distanceGain,
+ float dirX, float dirY, float dirZ,
+ float[] angle,
+ float[] angularGain,
+ float[] frequencyCutoff) {
+ super(soundData, initialGain, loopCount, release, continuous, enable,
+ region, priority, posX, posY, posZ,
+ distance, distanceGain );
+ ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
+ ((ConeSoundRetained)this.retained).setAngularAttenuation(angle,
+ angularGain, frequencyCutoff);
+ }
+
+ /**
+ * Constructs and initializes a new Cone Sound node explicitly setting all
+ * PointSound and ConeSound fields as arguments: the PointSound position,
+ * front and back distance attenuation Point2f array, and ConeSound
+ * direction vector and Point3f angular attenuation.
+ * @param soundData sound source data associated with this node
+ * @param initialGain amplitude scale factor applied to sound
+ * @param loopCount number of times sound is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param position 3D location of source
+ * @param frontDistanceAttenuation array of (distance,gain) pairs controlling
+ * attenuation values along the positive direction axis
+ * @param backDistanceAttenuation array of (distance,gain) pairs controlling
+ * attenuation values along the negative direction axis
+ * @param direction vector defining cones' axii
+ * @param angularAttenuation array of tuples defining angular gain/filtering
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ Point3f position,
+ Point2f[] frontDistanceAttenuation,
+ Point2f[] backDistanceAttenuation,
+ Vector3f direction,
+ Point3f[] angularAttenuation ) {
+
+ super(soundData, initialGain, loopCount, release, continuous, enable,
+ region, priority, position, frontDistanceAttenuation );
+ ((ConeSoundRetained)this.retained).setBackDistanceGain(
+ backDistanceAttenuation);
+ ((ConeSoundRetained)this.retained).setDirection(direction);
+ ((ConeSoundRetained)this.retained).setAngularAttenuation(
+ angularAttenuation);
+ }
+
+ /**
+ * Constructs and initializes a new Cone Sound node explicitly setting all
+ * PointSound and ConeSound fields as arguments but all the vector and point
+ * arguments are broken into individual float array components.
+ * @param soundData sound source data associated with this node
+ * @param initialGain amplitude scale factor applied to sound
+ * @param loopCount number of times sound is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param posX x coordinate of location of source
+ * @param posY y coordinate of location of source
+ * @param posZ z coordinate of location of source
+ * @param frontDistance array of front distance values used for attenuation
+ * @param frontDistanceGain array of front gain scale factors used for attenuation
+ * @param backDistance array of back distance values used for attenuation
+ * @param backDistanceGain array of back gain scale factors used for attenuation
+ * @param dirX x coordinate cones' axii vector
+ * @param dirY y coordinate cones' axii vector
+ * @param dirZ z coordinate cones' axii vector
+ * @param angle array of angle radians for angularAttenuation
+ * @param angularGain array of gain scale factors for angularAttenuation
+ * @param frequencyCutoff array of lowpass filter values in Hertz
+ */
+ public ConeSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ float posX, float posY, float posZ,
+ float[] frontDistance,
+ float[] frontDistanceGain,
+ float[] backDistance,
+ float[] backDistanceGain,
+ float dirX, float dirY, float dirZ,
+ float[] angle,
+ float[] angularGain,
+ float[] frequencyCutoff) {
+ super(soundData, initialGain, loopCount, release, continuous, enable,
+ region, priority, posX, posY, posZ,
+ frontDistance, frontDistanceGain );
+ ((ConeSoundRetained)this.retained).setBackDistanceGain(
+ backDistance, backDistanceGain );
+ ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
+ ((ConeSoundRetained)this.retained).setAngularAttenuation(angle,
+ angularGain, frequencyCutoff);
+ }
+
+ /**
+ * Creates the retained mode ConeSoundRetained object that this
+ * ConeSound object will point to.
+ */
+ void createRetained() {
+ this.retained = new ConeSoundRetained();
+ this.retained.setSource(this);
+ }
+
+ //
+ // OVERLOADED Sound methods
+ //
+ /**
+ * Sets this sound's distance gain elliptical attenuation -
+ * where gain scale factor is applied to sound based on distance listener
+ * is from sound source.
+ * @param frontAttenuation defined by pairs of (distance,gain-scale-factor)
+ * @param backAttenuation defined by pairs of (distance,gain-scale-factor)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDistanceGain(Point2f[] frontAttenuation,
+ Point2f[] backAttenuation ) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound0"));
+
+ ((ConeSoundRetained)this.retained).setDistanceGain(frontAttenuation,
+ backAttenuation);
+ }
+
+ /**
+ * Sets this sound's distance gain attenuation as an array of Point2fs.
+ * @param frontDistance array of monotonically-increasing floats
+ * @param frontGain array of non-negative scale factors
+ * @param backDistance array of monotonically-increasing floats
+ * @param backGain array of non-negative scale factors
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDistanceGain(float[] frontDistance, float[] frontGain,
+ float[] backDistance, float[] backGain) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound0"));
+
+ ((ConeSoundRetained)this.retained).setDistanceGain(
+ frontDistance, frontGain, backDistance, backGain);
+ }
+
+ /**
+ * Sets this sound's back distance gain attenuation - where gain scale
+ * factor is applied to sound based on distance listener along the negative
+ * sound direction axis from sound source.
+ * @param attenuation defined by pairs of (distance,gain-scale-factor)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setBackDistanceGain(Point2f[] attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound0"));
+
+ ((ConeSoundRetained)this.retained).setBackDistanceGain(attenuation);
+ }
+
+ /**
+ * Sets this sound's back distance gain attenuation as separate arrays.
+ * @param distance array of monotonically-increasing floats
+ * @param gain array of non-negative scale factors
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setBackDistanceGain(float[] distance, float[] gain) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound0"));
+
+ ((ConeSoundRetained)this.retained).setBackDistanceGain(distance, gain);
+ }
+
+ /**
+ * Gets this sound's elliptical distance attenuation. The
+ * attenuation values are copied into the specified arrays.
+ * The arrays must be large enough to hold all of the
+ * forward distances and backward distances attenuation values.
+ * The individual array elements must be allocated by the
+ * caller. The Point2f x,y values are defined as follows:
+ * x is the distance, y is the gain.
+ * @param frontAttenuation arrays containing forward distances
+ * attenuation pairs
+ * @param backAttenuation arrays containing backward distances
+ * attenuation pairs
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDistanceGain(Point2f[] frontAttenuation,
+ Point2f[] backAttenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound2"));
+
+ ((ConeSoundRetained)this.retained).getDistanceGain(
+ frontAttenuation, backAttenuation);
+ }
+
+ /**
+ * Gets this sound's elliptical distance gain attenuation values in
+ * separate arrays. The arrays must be large enough to hold all
+ * of the values.
+ * @param frontDistance array of float distances along the sound axis
+ * @param frontGain array of non-negative scale factors associated with
+ * front distances
+ * @param backDistance array of float negative distances along the sound
+ * axis
+ * @param backGain array of non-negative scale factors associated with
+ * back distances
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDistanceGain(float[] frontDistance, float[] frontGain,
+ float[] backDistance, float[] backGain) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound10"));
+
+ ((ConeSoundRetained)this.retained).getDistanceGain(
+ frontDistance, frontGain, backDistance, backGain);
+ }
+
+ /**
+ * Sets this sound's direction from the vector provided.
+ * @param direction the new direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDirection(Vector3f direction) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound3"));
+
+ ((ConeSoundRetained)this.retained).setDirection(direction);
+ }
+
+ /**
+ * Sets this sound's direction from the three values provided.
+ * @param x the new x direction
+ * @param y the new y direction
+ * @param z the new z direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDirection(float x, float y, float z) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound3"));
+
+ ((ConeSoundRetained)this.retained).setDirection(x,y,z);
+ }
+
+ /**
+ * Retrieves this sound's direction and places it in the
+ * vector provided.
+ * @param direction axis of cones; 'direction' of sound
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDirection(Vector3f direction) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound5"));
+
+ ((ConeSoundRetained)this.retained).getDirection(direction);
+ }
+
+ /**
+ * Sets this sound's angular gain attenuation (not including filter).
+ * In this form of setAngularAttenuation, only the angular distances
+ * and angular gain scale factors pairs are given. The filter values for
+ * these tuples are implicitly set to Sound.NO_FILTER.
+ * @param attenuation array containing angular distance and gain
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAngularAttenuation(Point2f[] attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANGULAR_ATTENUATION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound6"));
+
+ ((ConeSoundRetained)this.retained).setAngularAttenuation(attenuation);
+ }
+
+ /**
+ * In the second form of setAngularAttenuation, an array of all three values
+ * is supplied.
+ * @param attenuation array containing angular distance, gain, and filter
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAngularAttenuation(Point3f[] attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANGULAR_ATTENUATION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound6"));
+
+ ((ConeSoundRetained)this.retained).setAngularAttenuation(attenuation);
+ }
+
+ /**
+ * Sets angular attenuation including gain and filter using separate arrays.
+ * The third form of setAngularAttenuation accepts three separate arrays for
+ * these angular attenuation values. These arrays should be of the same
+ * length. If the angularGain or filtering array length is greater than
+ * angularDistance array length, the array elements beyond the length of
+ * the angularDistance array are ignored. If the angularGain or filtering
+ * array is shorter than the angularDistance array, the last value of the
+ * short array is repeated to fill an array of length equal to
+ * angularDistance array.
+ * @param distance array containing angular distance
+ * @param gain array containing angular gain attenuation
+ * @param filter array containing angular low-pass frequency cutoff values
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAngularAttenuation(float[] distance, float[] gain,
+ float[] filter) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANGULAR_ATTENUATION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound6"));
+
+ ((ConeSoundRetained)this.retained).setAngularAttenuation(distance,
+ gain, filter);
+ }
+
+ /**
+ * Retrieves angular attenuation array length.
+ * All arrays are forced to same size.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getAngularAttenuationLength() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANGULAR_ATTENUATION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound9"));
+
+ return (((ConeSoundRetained)this.retained).getAngularAttenuationLength());
+ }
+
+ /**
+ * Copies the array of attenuation values from this sound, including
+ * gain and filter, into the specified array. The array must be
+ * large enough to hold all of the points. The individual array
+ * elements must be allocated by the caller. The Point3f x,y,z values
+ * are defined as follows: x is the angular distance, y is
+ * the angular gain attenuation, and z is the frequency
+ * cutoff.
+ * @param attenuation the array to receive the attenuation values
+ * applied to gain when listener is between cones
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getAngularAttenuation(Point3f[] attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANGULAR_ATTENUATION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound9"));
+
+ ((ConeSoundRetained)this.retained).getAngularAttenuation(attenuation);
+ }
+
+ /**
+ * Copies the array of attenuation values from this sound,
+ * including gain and filter, into the separate arrays.
+ * The arrays must be large enough to hold all of the values.
+ * @param distance array containing angular distance
+ * @param gain array containing angular gain attenuation
+ * @param filter array containing angular low-pass frequency cutoff values
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getAngularAttenuation(float[] distance, float[] gain,
+ float[] filter) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANGULAR_ATTENUATION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ConeSound9"));
+
+ ((ConeSoundRetained)this.retained).getAngularAttenuation(distance,
+ gain, filter);
+ }
+
+ /**
+ * Creates 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) {
+ ConeSound c = new ConeSound();
+ 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ *
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>ConeSound</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+
+
+ /**
+ * Copies all ConeSound 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ConeSoundRetained orgRetained = (ConeSoundRetained)originalNode.retained;
+ ConeSoundRetained thisRetained = (ConeSoundRetained)this.retained;
+
+ // front distance gain & attenuation is set in super
+ // set back distance gain only
+ int len = orgRetained.getDistanceGainLength();
+ float distance[] = new float[len];
+ float gain[] = new float[len];
+ orgRetained.getDistanceGain(null, null,distance, gain);
+ thisRetained.setBackDistanceGain(distance, gain);
+
+ Vector3f v = new Vector3f();
+ orgRetained.getDirection(v);
+ thisRetained.setDirection(v);
+
+ len = orgRetained.getAngularAttenuationLength();
+ distance = gain = null;
+ float angle[] = new float[len];
+ float angularGain[] = new float[len];
+ float frequencyCutoff[] = new float[len];
+
+ orgRetained.getAngularAttenuation(angle, angularGain,
+ frequencyCutoff);
+
+ thisRetained.setAngularAttenuation(angle, angularGain, frequencyCutoff);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ConeSoundRetained.java b/src/classes/share/javax/media/j3d/ConeSoundRetained.java
new file mode 100644
index 0000000..4b450ef
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ConeSoundRetained.java
@@ -0,0 +1,641 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Vector3f;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Point2f;
+import javax.vecmath.Point3f;
+
+
+
+/**
+ * A ConeSoundRetained node defines a point sound source located at some
+ * location
+ * in space whose amplitude is constrained not only by maximum and minimum
+ * amplitude
+ * spheres but by two concentric cone volumes directed down an vector radiating
+ * from the sound's location.
+ */
+
+class ConeSoundRetained extends PointSoundRetained {
+ /**
+ * The Cone Sound's direction vector. This is the cone axis.
+ */
+ Vector3f direction = new Vector3f(0.0f, 0.0f, 1.0f);
+
+ // The transformed direction of this sound
+ Vector3f xformDirection = new Vector3f(0.0f, 0.0f, 1.0f);
+
+ // Sound's gain is attenuated for listener locations off-angle from
+ // the source source direction.
+ // This can be set of three numbers:
+ // angular distance in radians
+ // gain scale factor
+ // filtering (currently the only filtering supported is lowpass)
+
+ // For now the only supported filterType will be LOW_PASS frequency cutoff.
+ // At some time full FIR filtering will be supported.
+ static final int NO_FILTERING = -1;
+ static final int LOW_PASS = 1;
+
+ // 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.
+ float[] backAttenuationDistance = null;
+ float[] backAttenuationGain = null;
+
+ float[] angularDistance = {0.0f, ((float)(Math.PI) * 0.5f)};
+ float[] angularGain = {1.0f, 0.0f};
+ int filterType = NO_FILTERING;
+ float[] frequencyCutoff = {Sound.NO_FILTER, Sound.NO_FILTER};
+
+ ConeSoundRetained() {
+ this.nodeType = NodeRetained.CONESOUND;
+ }
+
+ // *********************
+ //
+ // Distance Gain methods
+ //
+ // *********************
+
+ /**
+ * Sets this sound's distance gain elliptical attenuation -
+ * where gain scale factor is applied to sound based on distance listener
+ * is from sound source.
+ * @param frontAttenuation defined by pairs of (distance,gain-scale-factor)
+ * @param backAttenuation defined by pairs of (distance,gain-scale-factor)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ void setDistanceGain(Point2f[] frontAttenuation,
+ Point2f[] backAttenuation ) {
+
+ this.setDistanceGain(frontAttenuation);
+ this.setBackDistanceGain(backAttenuation);
+ }
+
+ /**
+ * Sets this sound's distance gain attenuation as an array of Point2fs.
+ * @param frontDistance array of monotonically-increasing floats
+ * @param frontGain array of non-negative scale factors
+ * @param backDistance array of monotonically-increasing floats
+ * @param backGain array of non-negative scale factors
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ void setDistanceGain(float[] frontDistance, float[] frontGain,
+ float[] backDistance, float[] backGain) {
+ this.setDistanceGain(frontDistance, frontGain);
+ this.setBackDistanceGain(backDistance, backGain);
+ }
+
+ /**
+ * Sets this sound's back distance gain attenuation - where gain scale
+ * factor is applied to sound based on distance listener along the negative
+ * sound direction axis from sound source.
+ * @param attenuation defined by pairs of (distance,gain-scale-factor)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ void setBackDistanceGain(Point2f[] attenuation)
+ {
+ // if attenuation array null set both attenuation components to null
+ if (attenuation == null) {
+ this.backAttenuationDistance = null;
+ this.backAttenuationGain = null;
+ }
+ else {
+ int attenuationLength = attenuation.length;
+ if (attenuationLength == 0) {
+ this.backAttenuationDistance = null;
+ this.backAttenuationGain = null;
+ }
+ else {
+ this.backAttenuationDistance = new float[attenuationLength];
+ this.backAttenuationGain = new float[attenuationLength];
+ for (int i = 0; i < attenuationLength; i++) {
+ this.backAttenuationDistance[i] = attenuation[i].x;
+ this.backAttenuationGain[i] = attenuation[i].y;
+ }
+ }
+ }
+ dispatchAttribChange(BACK_DISTANCE_GAIN_DIRTY_BIT, attenuation);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Sets this sound's back distance gain attenuation as an array of Point2fs.
+ * @param distance array of monotonically-increasing floats
+ * @param gain array of non-negative scale factors
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ void setBackDistanceGain(float[] distance, float[] gain)
+ {
+ int distanceLength = 0;
+ // if distance or gain arrays are null then treat both as null
+ if (distance == null || gain == null) {
+ this.backAttenuationDistance = null;
+ this.backAttenuationGain = null;
+ }
+ else {
+ // now process the back attenuation values
+ int gainLength = gain.length;
+ distanceLength = distance.length;
+ if (distanceLength == 0 || gainLength == 0) {
+ this.backAttenuationDistance = null;
+ this.backAttenuationGain = null;
+ }
+ else {
+ this.backAttenuationDistance = new float[distanceLength];
+ this.backAttenuationGain = new float[distanceLength];
+ // Copy the distance array into nodes field
+ System.arraycopy(distance, 0, this.backAttenuationDistance,
+ 0, distanceLength);
+ // Copy the gain array an array of same length as the distance array
+ if (distanceLength <= gainLength) {
+ System.arraycopy(gain, 0, this.backAttenuationGain,
+ 0, distanceLength);
+ }
+ else {
+ System.arraycopy(gain, 0, this.backAttenuationGain, 0, gainLength);
+ // Extend gain array to length of distance array
+ // replicate last gain values.
+ for (int i=gainLength; i< distanceLength; i++) {
+ this.backAttenuationGain[i] = gain[gainLength - 1];
+ }
+ }
+ }
+ }
+
+ Point2f [] attenuation = new Point2f[distanceLength];
+ for (int i=0; i<distanceLength; i++) {
+ attenuation[i] = new Point2f(this.backAttenuationDistance[i],
+ this.backAttenuationGain[i]);
+ }
+ dispatchAttribChange(BACK_DISTANCE_GAIN_DIRTY_BIT, attenuation);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Gets this sound's elliptical distance attenuation
+ * @param frontAttenuation arrays containing forward distances attenuation pairs
+ * @param backAttenuation arrays containing backward distances attenuation pairs
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ void getDistanceGain(Point2f[] frontAttenuation,
+ Point2f[] backAttenuation) {
+ this.getDistanceGain(frontAttenuation);
+ this.getBackDistanceGain(backAttenuation);
+ }
+
+ /**
+ * Gets this sound's elliptical distance gain attenuation values in separate arrays
+ * @param frontDistance array of float distances along the sound axis
+ * @param fronGain array of non-negative scale factors associated with front distances
+ * @param backDistance array of float negative distances along the sound axis
+ * @param backGain array of non-negative scale factors associated with back distances
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ void getDistanceGain(float[] frontDistance, float[] frontGain,
+ float[] backDistance, float[] backGain) {
+ this.getDistanceGain( frontDistance, frontGain);
+ this.getBackDistanceGain( backDistance, backGain);
+ }
+
+ /**
+ * Retieves sound's back distance attenuation
+ * Put the contents of the two separate distance and gain arrays into
+ * an array of Point2f.
+ * @param attenuation containing distance attenuation pairs
+ */
+ void getBackDistanceGain(Point2f[] attenuation) {
+ // Write into arrays passed in, don't do a new
+ if (attenuation == null)
+ return;
+ if (this.backAttenuationDistance == null ||
+ this.backAttenuationGain == null)
+ return;
+ // These two array length should be the same
+ // can assume lengths are non-zero
+ int distanceLength = this.backAttenuationDistance.length;
+ int attenuationLength = attenuation.length;
+ if (distanceLength < attenuationLength)
+ distanceLength = attenuationLength;
+ for (int i=0; i< distanceLength; i++) {
+ attenuation[i].x = this.backAttenuationDistance[i];
+ attenuation[i].y = this.backAttenuationGain[i];
+ }
+ }
+
+ /**
+ * Retieves this sound's back attenuation distance and gain arrays,
+ * returned in separate arrays.
+ * @param distance array of monotonically-increasing floats.
+ * @param gain array of amplitude scale factors associated with distances.
+ */
+ void getBackDistanceGain(float[] distance, float[] gain) {
+ // write into arrays passed in, don't do a new
+ if (distance == null || gain == null)
+ return;
+ if (this.backAttenuationDistance == null ||
+ this.backAttenuationGain == null)
+ return;
+ // backAttenuationDistance and backAttenuationGain array length should
+ // be the same
+ // can assume length is non-zero
+ int attenuationLength = this.backAttenuationDistance.length;
+ int distanceLength = distance.length;
+ if (attenuationLength > distanceLength)
+ attenuationLength = distanceLength;
+ System.arraycopy(this.backAttenuationDistance, 0, distance, 0, attenuationLength);
+ attenuationLength = this.backAttenuationGain.length;
+ int gainLength = gain.length;
+ if (attenuationLength > gainLength)
+ attenuationLength = gainLength;
+ System.arraycopy(this.backAttenuationGain, 0, gain, 0, attenuationLength);
+ }
+
+
+ // *********************
+ //
+ // Direction Methods
+ //
+ // *********************
+
+ /**
+ * Sets this sound's direction from the vector provided.
+ * @param direction the new direction
+ */
+ void setDirection(Vector3f direction) {
+ if (staticTransform != null) {
+ staticTransform.transform.transform(direction, this.direction);
+ } else {
+ this.direction.set(direction);
+ }
+ dispatchAttribChange(DIRECTION_DIRTY_BIT,
+ (new Vector3f(this.direction)));
+
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Sets this sound's direction from the three values provided.
+ * @param x the new x direction
+ * @param y the new y direction
+ * @param z the new z direction
+ */
+ void setDirection(float x, float y, float z) {
+ direction.x = x;
+ direction.y = y;
+ direction.z = z;
+ if (staticTransform != null) {
+ staticTransform.transform.transform(direction);
+ }
+ dispatchAttribChange(DIRECTION_DIRTY_BIT, (new Vector3f(direction)));
+
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+
+ /**
+ * Retrieves this sound's direction and places it in the
+ * vector provided.
+ * @return direction vector (axis of cones)
+ */
+ void getDirection(Vector3f direction)
+ {
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ invTransform.transform(this.direction, direction);
+ } else {
+ direction.set(this.direction);
+ }
+ }
+
+ void getXformDirection(Vector3f direction)
+ {
+ direction.set(this.xformDirection);
+ }
+
+
+ // ***************************
+ //
+ // Angular Attenuation
+ //
+ // ***************************
+
+ /**
+ * Sets this sound's angular gain attenuation (not including filter)
+ * @param attenuation array containing angular distance and gain
+ */
+ void setAngularAttenuation(Point2f[] attenuation) {
+ int attenuationLength = 0;
+ this.filterType = NO_FILTERING;
+ if (attenuation == null) {
+ this.angularDistance = null;
+ this.angularGain = null;
+ }
+ else {
+ attenuationLength = attenuation.length;
+ if (attenuationLength == 0) {
+ this.angularDistance = null;
+ this.angularGain = null;
+ }
+ else {
+ this.angularDistance = new float[attenuationLength];
+ this.angularGain = new float[attenuationLength];
+ for (int i = 0; i < attenuationLength; i++) {
+ this.angularDistance[i] = attenuation[i].x;
+ this.angularGain[i] = attenuation[i].y;
+ }
+ } // lengths not zero
+ } // arrays not null
+ Point3f [] attenuation3f = new Point3f[attenuationLength];
+ for (int i=0; i<attenuationLength; i++) {
+ attenuation3f[i] = new Point3f(this.angularDistance[i],
+ this.angularGain[i],
+ Sound.NO_FILTER);
+ }
+ dispatchAttribChange(ANGULAR_ATTENUATION_DIRTY_BIT, attenuation3f);
+
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Sets this sound's angular attenuation including both gain and filter.
+ * @param attenuation array containing angular distance, gain and filter
+ */
+ void setAngularAttenuation(Point3f[] attenuation) {
+ if (attenuation == null) {
+ this.angularDistance = null;
+ this.angularGain = null;
+ this.frequencyCutoff = null;
+ this.filterType = NO_FILTERING;
+ }
+ else {
+ int attenuationLength = attenuation.length;
+ if (attenuationLength == 0) {
+ this.angularDistance = null;
+ this.angularGain = null;
+ this.frequencyCutoff = null;
+ this.filterType = NO_FILTERING;
+ }
+ else {
+ this.angularDistance = new float[attenuationLength];
+ this.angularGain = new float[attenuationLength];
+ this.frequencyCutoff = new float[attenuationLength];
+ this.filterType = LOW_PASS;
+ for (int i = 0; i < attenuationLength; i++) {
+ this.angularDistance[i] = attenuation[i].x;
+ this.angularGain[i] = attenuation[i].y;
+ this.frequencyCutoff[i] = attenuation[i].z;
+ }
+ } // lengths not zero
+ } // arrays not null
+ dispatchAttribChange(ANGULAR_ATTENUATION_DIRTY_BIT, attenuation);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Sets angular attenuation including gain and filter using separate arrays
+ * @param distance array containing angular distance
+ * @param filter array containing angular low-pass frequency cutoff values
+ */
+ void setAngularAttenuation(float[] distance, float[] gain, float[] filter) {
+ int distanceLength = 0;
+ if (distance == null || gain == null || filter == null) {
+ this.angularDistance = null;
+ this.angularGain = null;
+ this.frequencyCutoff = null;
+ this.filterType = NO_FILTERING;
+ }
+ else {
+ distanceLength = distance.length;
+ int gainLength = gain.length;
+ if (distanceLength == 0 || gainLength == 0) {
+ this.angularDistance = null;
+ this.angularGain = null;
+ this.frequencyCutoff = null;
+ this.filterType = NO_FILTERING;
+ }
+ else {
+ int filterLength = filter.length;
+ this.angularDistance = new float[distanceLength];
+ this.angularGain = new float[distanceLength];
+ this.frequencyCutoff = new float[distanceLength];
+ // Copy the distance array into nodes field
+ System.arraycopy(distance, 0, this.angularDistance, 0, distanceLength);
+ // Copy the gain array an array of same length as the distance array
+ if (distanceLength <= gainLength) {
+ System.arraycopy(gain, 0, this.angularGain, 0, distanceLength);
+ }
+ else {
+ System.arraycopy(gain, 0, this.angularGain, 0, gainLength);
+ /**
+ * Extend gain array to length of distance array by
+ * replicate last gain values.
+ */
+ for (int i=gainLength; i< distanceLength; i++) {
+ this.angularGain[i] = gain[gainLength - 1];
+ }
+ }
+ // Copy the filter array an array of same length as the distance array
+ if (filterLength == 0)
+ this.filterType = NO_FILTERING;
+ else {
+ this.filterType = LOW_PASS;
+ if (distanceLength <= filterLength) {
+ System.arraycopy(filter, 0, this.frequencyCutoff,0, distanceLength);
+ }
+ else {
+ System.arraycopy(filter, 0, this.frequencyCutoff, 0, filterLength);
+ // Extend filter array to length of distance array by
+ // replicate last filter values.
+ for (int i=filterLength; i< distanceLength; i++) {
+ this.frequencyCutoff[i] = filter[filterLength - 1];
+ }
+ }
+ }
+ } // length not zero
+ } // arrays not null
+ Point3f [] attenuation = new Point3f[distanceLength];
+ for (int i=0; i<distanceLength; i++) {
+ if (this.filterType != NO_FILTERING) {
+ attenuation[i] = new Point3f(this.angularDistance[i],
+ this.angularGain[i],
+ this.frequencyCutoff[i]);
+ }
+ else {
+ attenuation[i] = new Point3f(this.angularDistance[i],
+ this.angularGain[i],
+ Sound.NO_FILTER);
+ }
+ }
+ dispatchAttribChange(ANGULAR_ATTENUATION_DIRTY_BIT, attenuation);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves angular attenuation array length.
+ * All arrays are forced to same size
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ int getAngularAttenuationLength() {
+
+ if (angularDistance == null)
+ return 0;
+ else
+ return (this.angularDistance.length);
+ }
+
+ /**
+ * Retrieves angular attenuation including gain and filter in a single array
+ * @param attenuation applied to gain when listener is between cones
+ */
+ void getAngularAttenuation(Point3f[] attenuation) {
+ /// use attenuation array allocated by user - don't new it
+ // The three angular attenuation arrays length should be the same
+ if (this.angularDistance == null || this.angularGain == null)
+ return;
+ if (attenuation == null)
+ return;
+ int distanceLength = this.angularDistance.length;
+ if (attenuation.length < distanceLength)
+ distanceLength = attenuation.length;
+ for (int i=0; i< distanceLength; i++) {
+ attenuation[i].x = this.angularDistance[i];
+ attenuation[i].y = this.angularGain[i];
+ if (filterType == NO_FILTERING || this.frequencyCutoff == null)
+ attenuation[i].z = Sound.NO_FILTER;
+ else if (filterType == LOW_PASS)
+ attenuation[i].z = this.frequencyCutoff[i];
+ }
+ }
+
+ /**
+ * Retrieves angular attenuation including gain and filter
+ * returned as separate arrays
+ * @param distance array containing angular distance
+ * @param gain array containing angular gain attenuation
+ * @param filter array containing angular low-pass frequency cutoff values
+ */
+ void getAngularAttenuation(float[] distance, float[] gain, float[] filter) {
+ // use attenuation array allocated by user - don't new it
+ if (distance == null || gain == null || filter == null)
+ return;
+ if (this.angularDistance == null || this.angularGain == null)
+ return;
+ int distanceLength = this.angularDistance.length;
+ if (distance.length < distanceLength)
+ distanceLength = distance.length;
+ System.arraycopy(this.angularDistance, 0, distance, 0, distanceLength);
+
+ int gainLength = this.angularGain.length;
+ if (gain.length < gainLength)
+ gainLength = gain.length;
+ System.arraycopy(this.angularGain, 0, gain, 0, gainLength);
+
+ int filterLength = 0;
+ if (this.frequencyCutoff == null || filterType == NO_FILTERING)
+ filterLength = filter.length;
+ else {
+ filterLength = this.frequencyCutoff.length;
+ if (filter.length < filterLength)
+ filterLength = filter.length;
+ }
+ if (filterType == NO_FILTERING || this.frequencyCutoff == null) {
+ for (int i=0; i< filterLength; i++)
+ filter[i] = Sound.NO_FILTER;
+ }
+ if (filterType == LOW_PASS) {
+ System.arraycopy(this.frequencyCutoff, 0, filter,0, filterLength);
+ }
+ }
+
+
+ /**
+ * This updates the Direction fields of cone sound.
+ *
+ * Neither Angular gain Attenuation and Filtering fields, nor
+ * back distance gain not maintained in mirror object
+ */
+ void updateMirrorObject(Object[] objs) {
+ if (debugFlag)
+ debugPrint("PointSoundRetained:updateMirrorObj()");
+ Transform3D trans = null;
+ int component = ((Integer)objs[1]).intValue();
+ int numSnds = ((Integer)objs[2]).intValue();
+ SoundRetained[] mSnds = (SoundRetained[]) objs[3];
+ if (component == -1) {
+ // update every field
+ initMirrorObject(((ConeSoundRetained)objs[2]));
+ return;
+ }
+ if ((component & DIRECTION_DIRTY_BIT) != 0) {
+ for (int i = 0; i < numSnds; i++) {
+ ConeSoundRetained cone = (ConeSoundRetained)mSnds[i];
+ cone.direction = (Vector3f)objs[4];
+ cone.getLastLocalToVworld().transform(cone.direction,
+ cone.xformDirection);
+ cone.xformDirection.normalize();
+ }
+ }
+ // call the parent's mirror object update routine
+ super.updateMirrorObject(objs);
+ }
+
+ synchronized void initMirrorObject(ConeSoundRetained ms) {
+ super.initMirrorObject(ms);
+ ms.direction.set(this.direction);
+ ms.xformDirection.set(this.xformDirection);
+ }
+
+ // Called on the mirror object
+ void updateTransformChange() {
+ Transform3D lastLocalToVworld = getLastLocalToVworld();
+
+ super.updateTransformChange();
+ lastLocalToVworld.transform(direction, xformDirection);
+ xformDirection.normalize();
+ // set flag looked at by Scheduler to denote Transform change
+ // this flag will force resneding transformed direction to AudioDevice
+ if (debugFlag)
+ debugPrint("ConeSound xformDirection is (" + xformDirection.x + ", "
+ + xformDirection.y + ", "+ xformDirection.z + ")");
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ xform.transform.transform(direction);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DanglingReferenceException.java b/src/classes/share/javax/media/j3d/DanglingReferenceException.java
new file mode 100644
index 0000000..3f24868
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DanglingReferenceException.java
@@ -0,0 +1,44 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * During a <code>cloneTree</code> call an updated reference was requested
+ * for a node that did not get cloned. This happens when a sub-graph is
+ * duplicated via <code>cloneTree</code> and has at least one Leaf node
+ * that contains a reference to a Node that has no corresponding node in
+ * the cloned sub-graph. This results in two Leaf nodes wanting to share
+ * access to the same Node.
+ * <P>
+ * If dangling references are to be allowed during the cloneTree call,
+ * <code>cloneTree</code> should be called with the
+ * <code>allowDanglingReferences</code> parameter set to <code>true</code>.
+ * @see Node#cloneTree
+ */
+public class DanglingReferenceException extends RuntimeException {
+
+ /**
+ * Create the exception object with default values.
+ */
+ public DanglingReferenceException() {
+ }
+
+ /**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public DanglingReferenceException(String str) {
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/DecalGroup.java b/src/classes/share/javax/media/j3d/DecalGroup.java
new file mode 100644
index 0000000..2bd6150
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DecalGroup.java
@@ -0,0 +1,78 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The DecalGroup node is an ordered group node used for defining decal
+ * geometry on top of other geometry. The DecalGroup node specifies that
+ * its children should be rendered in index order and that they generate
+ * coplanar objects. Examples of this include: painted decals or text on
+ * surfaces, a checkerboard layered on top of a table, etc.
+ * <p>
+ * The first child, at index 0, defines the surface on top of which all
+ * other children are rendered. The geometry of this child must encompass
+ * all other children, otherwise incorrect rendering may result. The
+ * polygons contained within each of the children must be facing the same
+ * way. If the polygons defined by the first child are front facing, then
+ * all other surfaces should be front facing. In this case, the polygons
+ * are rendered in order. The renderer can use knowledge of the coplanar
+ * nature of the surfaces to avoid
+ * Z-buffer collisions. If the main surface is back facing then all other
+ * surfaces should be back facing, and need not be rendered (even if back
+ * face culling is disabled).
+ * <p>
+ * Note that using the DecalGroup node does not guarantee that Z-buffer
+ * collisions are avoided. An implementation of Java 3D may fall back to
+ * treating DecalGroup node as an OrderedGroup node.
+ */
+public class DecalGroup extends OrderedGroup {
+
+ /**
+ * Constructs and initializes a new DecalGroup node object.
+ */
+ public DecalGroup() {
+ }
+
+
+ /**
+ * Creates the retained mode DecalGroupRetained object that this
+ * DecalGroup component object will point to.
+ */
+ void createRetained() {
+ this.retained = new DecalGroupRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * 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) {
+ DecalGroup dg = new DecalGroup();
+ dg.duplicateNode(this, forceDuplicate);
+ return dg;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/DecalGroupRetained.java b/src/classes/share/javax/media/j3d/DecalGroupRetained.java
new file mode 100644
index 0000000..3b899d4
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DecalGroupRetained.java
@@ -0,0 +1,20 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class DecalGroupRetained extends OrderedGroupRetained {
+
+ DecalGroupRetained() {
+ this.nodeType = NodeRetained.DECALGROUP;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DefaultRenderMethod.java b/src/classes/share/javax/media/j3d/DefaultRenderMethod.java
new file mode 100644
index 0000000..6a6f95b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DefaultRenderMethod.java
@@ -0,0 +1,74 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The RenderMethod interface is used to create various ways to render
+ * different geometries.
+ */
+
+class DefaultRenderMethod implements RenderMethod {
+
+ boolean geometryIsLocked = false;
+
+ /**
+ * The actual rendering code for this RenderMethod
+ */
+ public boolean render(RenderMolecule rm, Canvas3D cv, int pass,
+ RenderAtomListInfo ra, int dirtyBits) {
+
+ boolean isVisible = false; // True if any of the RAs is visible.
+
+ while (ra != null) {
+ if (cv.ra == ra.renderAtom) {
+ if (cv.raIsVisible) {
+ cv.updateState(pass, dirtyBits);
+ ra.geometry().execute(cv, ra.renderAtom,
+ rm.isNonUniformScale,
+ rm.useAlpha, rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.
+ ignoreVertexColors,
+ pass);
+ isVisible = true;
+ }
+ }
+ else {
+ if (ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) {
+ cv.raIsVisible = true;
+ cv.updateState(pass, dirtyBits);
+ ra.geometry().execute(cv, ra.renderAtom, rm.isNonUniformScale,
+ rm.useAlpha, rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.
+ ignoreVertexColors,
+ pass);
+ isVisible = true;
+ }
+ else {
+ cv.raIsVisible = false;
+ }
+ cv.ra = ra.renderAtom;
+ }
+ ra = ra.next;
+ }
+
+ return isVisible;
+
+ }
+
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponent.java b/src/classes/share/javax/media/j3d/DepthComponent.java
new file mode 100644
index 0000000..cc68c47
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponent.java
@@ -0,0 +1,67 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Abstract base class that defines a 2D array of depth (Z) values.
+ */
+
+public abstract class DepthComponent extends NodeComponent {
+ /**
+ * Specifies that this DepthComponent object allows reading its
+ * size component information (width and height).
+ */
+ public static final int
+ ALLOW_SIZE_READ = CapabilityBits.DEPTH_COMPONENT_ALLOW_SIZE_READ;
+
+ /**
+ * Specifies that this DepthComponent object allows reading its
+ * depth data component information.
+ */
+ public static final int
+ ALLOW_DATA_READ = CapabilityBits.DEPTH_COMPONENT_ALLOW_DATA_READ;
+
+ /**
+ * default constructor
+ */
+ DepthComponent() {
+ }
+
+ /**
+ * Retrieves the width of this depth component object.
+ * @return the width of the array of depth values
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getWidth() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("DepthComponent0"));
+ return ((DepthComponentRetained)this.retained).getWidth();
+ }
+
+ /**
+ * Retrieves the height of this depth component object.
+ * @return the height of the array of depth values
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getHeight() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("DepthComponent0"));
+ return ((DepthComponentRetained)this.retained).getHeight();
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponentFloat.java b/src/classes/share/javax/media/j3d/DepthComponentFloat.java
new file mode 100644
index 0000000..3e79efc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponentFloat.java
@@ -0,0 +1,117 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A 2D array of depth (Z) values in floating point format in the range [0,1].
+ * A value of 0.0 indicates the closest Z value to the user while a value of
+ * 1.0 indicates the farthest Z value.
+ */
+
+public class DepthComponentFloat extends DepthComponent {
+
+ /**
+ * Package scope defualt constructor used by cloneNodeComponent
+ */
+ DepthComponentFloat() {
+ }
+
+ /**
+ * Constructs a new floating-point depth (z-buffer) component object with
+ * the specified width and height.
+ * @param width the width of the array of depth values
+ * @param height the height of the array of depth values
+ */
+ public DepthComponentFloat(int width, int height) {
+ ((DepthComponentFloatRetained)this.retained).initialize(width, height);
+ }
+
+ /**
+ * Copies the specified depth data to this object.
+ * @param depthData array of floats containing the depth data
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void setDepthData(float[] depthData) {
+ checkForLiveOrCompiled();
+ ((DepthComponentFloatRetained)this.retained).setDepthData(depthData);
+ }
+
+ /**
+ * Copies the depth data from this object to the specified array.
+ * The array must be large enough to hold all of the floats.
+ * @param depthData array of floats that will receive a copy of
+ * the depth data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDepthData(float[] depthData) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(DepthComponent.ALLOW_DATA_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("DepthComponentFloat0"));
+ ((DepthComponentFloatRetained)this.retained).getDepthData(depthData);
+ }
+
+ /**
+ * Creates a retained mode DepthComponentFloatRetained object that this
+ * DepthComponentFloat component object will point to.
+ */
+ void createRetained() {
+ this.retained = new DepthComponentFloatRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ DepthComponentFloatRetained rt = (DepthComponentFloatRetained) retained;
+ DepthComponentFloat d = new DepthComponentFloat(rt.width,
+ rt.height);
+ d.duplicateNodeComponent(this);
+ return d;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent,
+ forceDuplicate);
+ // width, height is copied in cloneNode before
+ int len = getWidth()*getHeight();
+ float f[] = new float[len];
+
+ ((DepthComponentFloatRetained) originalNodeComponent.retained).getDepthData(f);
+ ((DepthComponentFloatRetained) retained).setDepthData(f);
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponentFloatRetained.java b/src/classes/share/javax/media/j3d/DepthComponentFloatRetained.java
new file mode 100644
index 0000000..c985414
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponentFloatRetained.java
@@ -0,0 +1,74 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * A 2D array of depth (Z) values in floating point format in the range [0,1].
+ * A value of 0.0 indicates the closest Z value to the user while a value of
+ * 1.0 indicates the farthest Z value.
+ */
+
+class DepthComponentFloatRetained extends DepthComponentRetained {
+ float depthData[];
+
+ /**
+ * Constructs a new floating-point depth (z-buffer) component object with
+ * the specified width and height.
+ * @param width the width of the array of depth values
+ * @param height the height of the array of depth values
+ */
+ void initialize(int width, int height) {
+ type = DEPTH_COMPONENT_TYPE_FLOAT;
+ depthData = new float[width * height];
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Copies the specified depth data to this object.
+ * @param depthData array of floats containing the depth data
+ */
+ void setDepthData(float[] depthData) {
+ int i;
+ for (i = 0; i < depthData.length; i++)
+ this.depthData[i] = depthData[i];
+ }
+
+
+ /**
+ * Copies the depth data from this object to the specified array.
+ * @param depthData array of floats that will receive a copy of
+ * the depth data
+ */
+ void getDepthData(float[] depthData) {
+ int i;
+ for (i = 0; i < this.depthData.length; i++)
+ depthData[i] = this.depthData[i];
+ }
+
+ /*
+ * retrieve depth data from input buffer
+ */
+ final void retrieveDepth(float[] buf, int wRead, int hRead) {
+ int srcOffset, dstOffset, i;
+
+ // Yup -> Ydown
+ for (srcOffset = (hRead - 1) * wRead, dstOffset = 0,
+ i = 0; i < hRead; i++,
+ srcOffset -= wRead, dstOffset += width) {
+
+ System.arraycopy(buf, srcOffset, depthData, dstOffset, wRead);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponentInt.java b/src/classes/share/javax/media/j3d/DepthComponentInt.java
new file mode 100644
index 0000000..e30b977
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponentInt.java
@@ -0,0 +1,115 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A 2D array of depth (Z) values in integer format. Values are in the
+ * range [0,(2**N)-1], where N is the pixel depth of the Z buffer.
+ */
+
+public class DepthComponentInt extends DepthComponent {
+
+ /**
+ * Package scope default constructor
+ */
+ DepthComponentInt() {
+ }
+
+ /**
+ * Constructs a new integer depth (z-buffer) component object with the
+ * specified width and height.
+ * @param width the width of the array of depth values
+ * @param height the height of the array of depth values
+ */
+ public DepthComponentInt(int width, int height) {
+ ((DepthComponentIntRetained)this.retained).initialize(width, height);
+ }
+
+ /**
+ * Copies the specified depth data to this object.
+ * @param depthData array of ints containing the depth data
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void setDepthData(int[] depthData) {
+ checkForLiveOrCompiled();
+ ((DepthComponentIntRetained)this.retained).setDepthData(depthData);
+ }
+
+ /**
+ * Copies the depth data from this object to the specified array.
+ * The array must be large enough to hold all of the ints.
+ * @param depthData array of ints that will receive a copy of
+ * the depth data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDepthData(int[] depthData) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(DepthComponent.ALLOW_DATA_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("DepthComponentInt0"));
+ ((DepthComponentIntRetained)this.retained).getDepthData(depthData);
+ }
+
+ /**
+ * Creates a retained mode DepthComponentIntRetained object that this
+ * DepthComponentInt component object will point to.
+ */
+ void createRetained() {
+ this.retained = new DepthComponentIntRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ DepthComponentIntRetained rt = (DepthComponentIntRetained) retained;
+ DepthComponentInt d = new DepthComponentInt(rt.width,
+ rt.height);
+ d.duplicateNodeComponent(this);
+ return d;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ // width, height is copied in cloneNode before
+ int len = getWidth()*getHeight();
+ int d[] = new int[len];
+ ((DepthComponentIntRetained) originalNodeComponent.retained).getDepthData(d);
+ ((DepthComponentIntRetained) retained).setDepthData(d);
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponentIntRetained.java b/src/classes/share/javax/media/j3d/DepthComponentIntRetained.java
new file mode 100644
index 0000000..8873691
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponentIntRetained.java
@@ -0,0 +1,74 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * A 2D array of depth (Z) values in integer format. Values are in the
+ * range [0,(2**N)-1], where N is the pixel depth of the Z buffer.
+ */
+
+class DepthComponentIntRetained extends DepthComponentRetained {
+ int depthData[];
+
+ /**
+ * Constructs a new integer depth (z-buffer) component object with the
+ * specified width and height.
+ * @param width the width of the array of depth values
+ * @param height the height of the array of depth values
+ */
+ void initialize(int width, int height) {
+ type = DEPTH_COMPONENT_TYPE_INT;
+ depthData = new int[width * height];
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Copies the specified depth data to this object.
+ * @param depthData array of ints containing the depth data
+ */
+ void setDepthData(int[] depthData) {
+ int i;
+ for (i = 0; i < depthData.length; i++)
+ this.depthData[i] = depthData[i];
+ }
+
+
+ /**
+ * Copies the depth data from this object to the specified array.
+ * @param depthData array of ints that will receive a copy of
+ * the depth data
+ */
+ void getDepthData(int[] depthData) {
+ int i;
+
+ for (i = 0; i < this.depthData.length; i++)
+ depthData[i] = this.depthData[i];
+ }
+
+ /**
+ * retrieve depth data from input buffer
+ */
+ final void retrieveDepth(int[] buf, int wRead, int hRead) {
+ int srcOffset, dstOffset, i;
+
+ // Yup -> Ydown
+ for (srcOffset = (hRead - 1) * wRead, dstOffset = 0,
+ i = 0; i < hRead; i++,
+ srcOffset -= wRead, dstOffset += width) {
+
+ System.arraycopy(buf, srcOffset, depthData, dstOffset, wRead);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponentNative.java b/src/classes/share/javax/media/j3d/DepthComponentNative.java
new file mode 100644
index 0000000..005495f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponentNative.java
@@ -0,0 +1,107 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A 2D array of depth (Z) values stored in the most efficient format for a
+ * particular device. Values are not accessible by the user and may only be
+ * used to read the Z values and subsequently write them back.
+ */
+
+public class DepthComponentNative extends DepthComponent {
+ /**
+ * Package scope defualt constructor for use by cloneNodeComponent
+ */
+ DepthComponentNative() {
+ }
+
+ /**
+ * Constructs a new native depth (z-buffer) component object with the
+ * specified width and height.
+ * @param width the width of the array of depth values
+ * @param height the height of the array of depth values
+ */
+ public DepthComponentNative(int width, int height) {
+ ((DepthComponentNativeRetained)this.retained).initialize(width, height);
+ }
+
+ /**
+ * Copies the depth data from this object to the specified array.
+ * @param depthData array of ints that will receive a copy of
+ * the depth data
+ */
+ void getDepthData(int[] depthData) {
+ ((DepthComponentNativeRetained)this.retained).getDepthData(depthData);
+ }
+
+ /**
+ * Creates a retained mode DepthComponentIntRetained object that this
+ * DepthComponentInt component object will point to.
+ */
+ void createRetained() {
+ this.retained = new DepthComponentNativeRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Creates a new DepthComponentNative object. Called from a Leaf node's
+ * <code>duplicateNode</code> method.
+ *
+ * @return a duplicate of the DepthComponentNative object.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public NodeComponent cloneNodeComponent() {
+ DepthComponentNativeRetained rt = (DepthComponentNativeRetained) retained;
+ DepthComponentNative d = new DepthComponentNative(rt.width,
+ rt.height);
+ d.duplicateNodeComponent(this);
+ return d;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ int originalData[] = ((DepthComponentNativeRetained)
+ originalNodeComponent.retained).depthData;
+
+ int currentData[] = ((DepthComponentNativeRetained) retained).depthData;
+
+ if (originalData != null) {
+ for (int i=0; i < originalData.length; i++)
+ currentData[i] = originalData[i];
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponentNativeRetained.java b/src/classes/share/javax/media/j3d/DepthComponentNativeRetained.java
new file mode 100644
index 0000000..9c29afa
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponentNativeRetained.java
@@ -0,0 +1,63 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A 2D array of depth (Z) values stored in the most efficient format for a
+ * particular device. Values are not accessible by the user and may only be
+ * used to read the Z values and subsequently write them back.
+ */
+
+class DepthComponentNativeRetained extends DepthComponentRetained {
+ // Change this to whatever native format is best...
+ int depthData[];
+
+ /**
+ * Constructs a new native depth (z-buffer) component object with the
+ * specified width and height.
+ * @param width the width of the array of depth values
+ * @param height the height of the array of depth values
+ */
+ void initialize(int width, int height) {
+ type = DEPTH_COMPONENT_TYPE_NATIVE;
+ depthData = new int[width * height];
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Copies the depth data from this object to the specified array.
+ * @param depthData array of ints that will receive a copy of
+ * the depth data
+ */
+ void getDepthData(int[] depthData) {
+ int i;
+ for (i = 0; i < this.depthData.length; i++)
+ depthData[i] = this.depthData[i];
+ }
+
+ /**
+ * retrieve depth data from input buffer
+ */
+ final void retrieveDepth(int[] buf, int wRead, int hRead) {
+ int srcOffset, dstOffset, i;
+
+ // Yup -> Ydown
+ for (srcOffset = (hRead - 1) * wRead, dstOffset = 0,
+ i = 0; i < hRead; i++,
+ srcOffset -= wRead, dstOffset += width) {
+
+ System.arraycopy(buf, srcOffset, depthData, dstOffset, wRead);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DepthComponentRetained.java b/src/classes/share/javax/media/j3d/DepthComponentRetained.java
new file mode 100644
index 0000000..3bb5965
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DepthComponentRetained.java
@@ -0,0 +1,48 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Abstract base class that defines a 2D array of depth (Z) values.
+ */
+
+
+abstract class DepthComponentRetained extends NodeComponentRetained {
+ // depth component types
+ static final int DEPTH_COMPONENT_TYPE_INT = 1;
+ static final int DEPTH_COMPONENT_TYPE_FLOAT = 2;
+ static final int DEPTH_COMPONENT_TYPE_NATIVE = DEPTH_COMPONENT_TYPE_INT;
+
+
+ // Width and height of DepthComponent---set by subclasses
+ int width;
+ int height;
+ int type;
+
+ /**
+ * Retrieves the width of this depth component object
+ * @return the width of the array of depth values
+ */
+ int getWidth() {
+ return width;
+ }
+
+ /**
+ * Retrieves the height of this depth component object
+ * @return the height of the array of depth values
+ */
+ int getHeight() {
+ return height;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/DetailTextureImage.java b/src/classes/share/javax/media/j3d/DetailTextureImage.java
new file mode 100644
index 0000000..46aae3b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DetailTextureImage.java
@@ -0,0 +1,219 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+
+class DetailTextureImage extends Object {
+
+ final static int NFORMAT = 7; // number of texture format
+
+ int objectIds[]; // texture object id, one per format
+ int refCount[]; // texture bin reference count,
+ // to keep track of if the texture
+ // object id is still being referenced
+ // by an active texture. If it
+ // goes to 0 for a particular format,
+ // the associated texture object
+ // will be destroyed.
+
+ int resourceCreationMask[]; // one creation mask per format
+
+ ImageComponent2DRetained image = null; // the image itself
+
+ Object resourceLock = new Object();
+
+ DetailTextureImage(ImageComponent2DRetained img) {
+ image = img;
+ }
+
+ native void bindTexture(long ctx, int objectId);
+
+ native void updateTextureImage(long ctx,
+ int numLevels, int level,
+ int format, int storedFormat,
+ int width, int height,
+ int boundaryWidth, byte[] data);
+
+
+ synchronized void incTextureBinRefCount(int format, TextureBin tb) {
+ if (refCount == null) {
+ refCount = new int[NFORMAT];
+ }
+ if (resourceCreationMask == null) {
+ resourceCreationMask = new int[NFORMAT];
+ }
+ refCount[format]++;
+
+ if (image != null &&
+ (image.isByReference() ||
+ image.source.getCapability(ImageComponent.ALLOW_IMAGE_WRITE))) {
+ tb.renderBin.addNodeComponent(image);
+ }
+ }
+
+ synchronized void decTextureBinRefCount(int format, TextureBin tb) {
+
+ if (refCount != null) {
+ refCount[format]--;
+ }
+ if (image != null &&
+ (image.isByReference() ||
+ image.source.getCapability(ImageComponent.ALLOW_IMAGE_WRITE))) {
+ tb.renderBin.removeNodeComponent(image);
+ }
+ }
+
+
+ synchronized void freeDetailTextureId(int id, int bitMask) {
+ synchronized(resourceLock) {
+ if (objectIds != null) {
+ for (int i=0; i < resourceCreationMask.length; i++) {
+ resourceCreationMask[i] &= ~bitMask;
+ if (resourceCreationMask[i] == 0) {
+ if (objectIds[i] == id) {
+ objectIds[i] = -1;
+ VirtualUniverse.mc.freeTexture2DId(id);
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ synchronized void freeTextureId(int format, int id) {
+ synchronized(resourceLock) {
+ if ((objectIds != null) && (objectIds[format] == id)) {
+ objectIds[format] = -1;
+ VirtualUniverse.mc.freeTexture2DId(objectIds[format]);
+ }
+ }
+ }
+
+ protected void finalize() {
+ if (objectIds != null) {
+ // memory not yet free
+ // send a message to the request renderer
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ boolean found = false;
+ for (int i=0; i < objectIds.length; i++) {
+ if (objectIds[i] > 0) {
+ for (Enumeration e = Screen3D.deviceRendererMap.elements();
+ e.hasMoreElements(); ) {
+ Renderer rdr = (Renderer) e.nextElement();
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = null;
+ renderMessage.args[1] = new Integer(objectIds[i]);
+ renderMessage.args[2] = "2D";
+ rdr.rendererStructure.addMessage(renderMessage);
+ }
+ objectIds[i] = -1;
+ found = true;
+ }
+ }
+ if (found) {
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+ }
+ }
+ }
+
+ void notifyImageComponentImageChanged(ImageComponentRetained image,
+ Object value) {
+ if (resourceCreationMask != null) {
+ synchronized(resourceLock) {
+ for (int i = 0; i < NFORMAT; i++) {
+ resourceCreationMask[i] = 0;
+ }
+ }
+ }
+ }
+
+ void bindTexture(Canvas3D cv, int format) {
+ synchronized(resourceLock) {
+ if (objectIds == null) {
+ objectIds = new int[NFORMAT];
+ for (int i = 0; i < NFORMAT; i++) {
+ objectIds[i] = -1;
+ }
+ }
+
+ if (objectIds[format] == -1) {
+ objectIds[format] = VirtualUniverse.mc.getTexture2DId();
+ }
+ cv.addTextureResource(objectIds[format], this);
+ }
+
+ bindTexture(cv.ctx, objectIds[format]);
+ }
+
+
+ void updateNative(Canvas3D cv, int format) {
+ if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_DETAIL) == 0) {
+ return;
+ }
+
+ boolean reloadTexture = false;
+
+ // bind the detail texture
+
+ bindTexture(cv, format);
+
+ if (cv.useSharedCtx && cv.screen.renderer.sharedCtx != 0) {
+ if ((resourceCreationMask[format] & cv.screen.renderer.rendererBit)
+ == 0) {
+ reloadTexture = true;
+ cv.makeCtxCurrent(cv.screen.renderer.sharedCtx);
+ bindTexture(cv, format);
+ }
+ } else {
+ if ((resourceCreationMask[format] & cv.canvasBit) == 0) {
+ reloadTexture = true;
+ }
+ }
+
+ // No D3D support yet
+
+ if (reloadTexture) {
+
+ updateTextureImage(cv.ctx, 1, 0, format, image.storedYupFormat,
+ image.width, image.height, 0, image.imageYup);
+
+
+ // Rendered image
+
+ }
+
+ if (cv.useSharedCtx) {
+ cv.makeCtxCurrent(cv.ctx);
+ synchronized(resourceLock) {
+ resourceCreationMask[format] |= cv.screen.renderer.rendererBit;
+ }
+ } else {
+ synchronized(resourceLock) {
+ resourceCreationMask[format] |= cv.canvasBit;
+ }
+ }
+ }
+}
+
+
+
diff --git a/src/classes/share/javax/media/j3d/DirectionalLight.java b/src/classes/share/javax/media/j3d/DirectionalLight.java
new file mode 100644
index 0000000..60fd994
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DirectionalLight.java
@@ -0,0 +1,188 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * A DirectionalLight node defines an oriented light with an origin at
+ * infinity. It has the same attributes as a Light node, with the
+ * addition of a directional vector to specify the direction in which the
+ * light shines. A directional light has parallel light rays that travel
+ * in one direction along the specified vector. Directional light contributes
+ * to diffuse and specular reflections, which in turn depend on the
+ * orientation of an object's surface but not its position. A directional
+ * light does not contribute to ambient reflections.
+ */
+
+public class DirectionalLight extends Light {
+ /**
+ * Specifies that the Node allows access to its object's direction
+ * information.
+ */
+ public static final int
+ ALLOW_DIRECTION_READ = CapabilityBits.DIRECTIONAL_LIGHT_ALLOW_DIRECTION_READ;
+
+ /**
+ * Specifies that the Node allows writing to its object's direction
+ * information.
+ */
+ public static final int
+ ALLOW_DIRECTION_WRITE = CapabilityBits.DIRECTIONAL_LIGHT_ALLOW_DIRECTION_WRITE;
+
+ /**
+ * Constructs a DirectionalLight node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * direction : (0,0,-1)<br>
+ * </ul>
+ */
+ public DirectionalLight() {
+ }
+
+ /**
+ * Constructs and initializes a directional light.
+ * @param color the color of the light source
+ * @param direction the direction vector pointing from the light
+ * to the object
+ */
+ public DirectionalLight(Color3f color, Vector3f direction) {
+ super(color);
+ ((DirectionalLightRetained)this.retained).initDirection(direction);
+ }
+
+ /**
+ * Constructs and initializes a directional light.
+ * @param lightOn flag indicating whether this light is on or off
+ * @param color the color of the light source
+ * @param direction the direction vector pointing from the light
+ * to the object
+ */
+ public DirectionalLight(boolean lightOn, Color3f color, Vector3f direction) {
+ super(lightOn, color);
+ ((DirectionalLightRetained)this.retained).initDirection(direction);
+ }
+
+ /**
+ * Creates the retained mode DirectionalLightRetained object that this
+ * DirectionalLight component object will point to.
+ */
+ void createRetained() {
+ this.retained = new DirectionalLightRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Set light direction.
+ * @param direction the new direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDirection(Vector3f direction) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_WRITE))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("DirectionalLight0"));
+
+ if (isLive())
+ ((DirectionalLightRetained)this.retained).setDirection(direction);
+ else
+ ((DirectionalLightRetained)this.retained).initDirection(direction);
+ }
+
+ /**
+ * Set light direction.
+ * @param x the new X direction
+ * @param y the new Y direction
+ * @param z the new Z direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDirection(float x, float y, float z) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_WRITE))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("DirectionalLight1"));
+
+ if (isLive())
+ ((DirectionalLightRetained)this.retained).setDirection(x,y,z);
+ else
+ ((DirectionalLightRetained)this.retained).initDirection(x,y,z);
+ }
+
+ /**
+ * Gets this Light's current direction and places it in the parameter specified.
+ * @param direction the vector that will receive this node's direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDirection(Vector3f direction) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_READ))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("DirectionalLight2"));
+
+ ((DirectionalLightRetained)this.retained).getDirection(direction);
+ }
+
+ /**
+ * 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) {
+ DirectionalLight d = new DirectionalLight();
+ d.duplicateNode(this, forceDuplicate);
+ return d;
+ }
+
+
+ /**
+ * Copies all DirectionalLight 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ Vector3f v = new Vector3f();
+ ((DirectionalLightRetained) originalNode.retained).getDirection(v);
+ ((DirectionalLightRetained) retained).initDirection(v);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DirectionalLightRetained.java b/src/classes/share/javax/media/j3d/DirectionalLightRetained.java
new file mode 100644
index 0000000..a9a17d0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DirectionalLightRetained.java
@@ -0,0 +1,203 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * An infinite directional light source object.
+ */
+
+class DirectionalLightRetained extends LightRetained
+{
+ static final int DIRECTION_CHANGED = LAST_DEFINED_BIT << 1;
+
+ // The direction in which this light source is pointing.
+ Vector3f direction = new Vector3f(0.0f, 0.0f, -1.0f);
+
+ // The transformed direction
+ Vector3f xformDirection = new Vector3f(0.0f, 0.0f, -1.0f);
+
+ DirectionalLightRetained() {
+ this.nodeType = NodeRetained.DIRECTIONALLIGHT;
+ lightType = 2;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ /**
+ * Initializes this light's direction from the vector provided.
+ * @param direction the new direction
+ */
+ void initDirection(Vector3f direction) {
+ this.direction.set(direction);
+ if (staticTransform != null) {
+ staticTransform.transform.transform(
+ this.direction, this.direction);
+ }
+ }
+
+ /**
+ * Sets this light's direction from the vector provided.
+ * and sends a message
+ * @param direction the new direction
+ */
+ void setDirection(Vector3f direction) {
+ initDirection(direction);
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.LIGHT_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(DIRECTION_CHANGED);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorLights);
+ else
+ createMessage.args[2] = new Integer(1);
+ createMessage.args[3] = mirrorLights.clone();
+ createMessage.args[4] = new Vector3f(direction);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ }
+
+
+ /**
+ * Initializes this light's direction from the three values provided.
+ * @param x the new x direction
+ * @param y the new y direction
+ * @param z the new z direction
+ */
+ void initDirection(float x, float y, float z) {
+ this.direction.x = x;
+ this.direction.y = y;
+ this.direction.z = z;
+
+ if (staticTransform != null) {
+ staticTransform.transform.transform(
+ this.direction, this.direction);
+ }
+ }
+
+ /**
+ * Sets this light's direction from the three values provided.
+ * @param x the new x direction
+ * @param y the new y direction
+ * @param z the new z direction
+ */
+ void setDirection(float x, float y, float z) {
+ setDirection(new Vector3f(x, y, z));
+ }
+
+
+ /**
+ * Retrieves this light's direction and places it in the
+ * vector provided.
+ * @param direction the variable to receive the direction vector
+ */
+ void getDirection(Vector3f direction) {
+ direction.set(this.direction);
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ invTransform.transform(direction, direction);
+ }
+ }
+
+
+
+ void setLive(SetLiveState s) {
+ super.setLive(s);
+ J3dMessage createMessage = super.initMessage(8);
+ Object[] objs = (Object[])createMessage.args[4];
+ objs[7] = new Vector3f(direction);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ }
+
+ /**
+ * This update function, and its native counterpart,
+ * updates a directional light. This includes its
+ * color and its transformed direction.
+ */
+ // Note : if you add any more fields here , you need to update
+ // updateLight() in RenderingEnvironmentStructure
+ void updateMirrorObject(Object[] objs) {
+ int i;
+ int component = ((Integer)objs[1]).intValue();
+ Transform3D trans;
+ int numLgts = ((Integer)objs[2]).intValue();
+
+ LightRetained[] mLgts = (LightRetained[]) objs[3];
+ DirectionalLightRetained ml;
+ if ((component & DIRECTION_CHANGED) != 0) {
+
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i].nodeType == NodeRetained.DIRECTIONALLIGHT) {
+ ml = (DirectionalLightRetained) mLgts[i];
+ ml.direction = (Vector3f)objs[4];
+ ml.getLastLocalToVworld().transform(ml.direction,
+ ml.xformDirection);
+ ml.xformDirection.normalize();
+ }
+ }
+ }
+
+ if ((component & INIT_MIRROR) != 0) {
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i].nodeType == NodeRetained.DIRECTIONALLIGHT) {
+ ml = (DirectionalLightRetained) mLgts[i];
+ ml.direction = (Vector3f)((Object[])objs[4])[7];
+ ml.getLastLocalToVworld().transform(ml.direction,
+ ml.xformDirection);
+ ml.xformDirection.normalize();
+ }
+ }
+ }
+ // call the parent's mirror object update routine
+ super.updateMirrorObject(objs);
+ }
+
+
+ native void updateLight(long ctx,
+ int lightSlot, float red, float green,
+ float blue, float x, float y, float z);
+ void update(long ctx, int lightSlot, double scale) {
+ updateLight(ctx, lightSlot, color.x, color.y, color.z,
+ xformDirection.x, xformDirection.y,
+ xformDirection.z);
+ }
+
+ // Clones only the retained side, internal use only
+ protected Object clone() {
+ DirectionalLightRetained dr =
+ (DirectionalLightRetained)super.clone();
+ dr.direction = new Vector3f(direction);
+ dr.xformDirection = new Vector3f(0.0f, 0.0f, -1.0f);
+ return dr;
+ }
+
+
+ // Called on the mirror object
+ void updateTransformChange() {
+ super.updateTransformChange();
+
+ getLastLocalToVworld().transform(direction, xformDirection);
+ xformDirection.normalize();
+
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ xform.transform.transform(direction, direction);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DisplayListRenderMethod.java b/src/classes/share/javax/media/j3d/DisplayListRenderMethod.java
new file mode 100644
index 0000000..c593567
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DisplayListRenderMethod.java
@@ -0,0 +1,256 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The RenderMethod interface is used to create various ways to render
+ * different geometries.
+ */
+
+class DisplayListRenderMethod implements RenderMethod {
+
+ /**
+ * display list buffer size
+ */
+ final int bufferSize = 128;
+
+ /**
+ * display list buffer
+ */
+ int[] buffer = new int[bufferSize];
+
+ native void callDisplayLists(int size, int[] buffer);
+
+ /**
+ * The actual rendering code for this RenderMethod
+ */
+ public boolean render(RenderMolecule rm, Canvas3D cv, int pass,
+ RenderAtomListInfo ra,
+ int dirtyBits) {
+
+ if (rm.doInfinite || rm.vwcBounds.intersect(cv.viewFrustum)) {
+ cv.updateState(pass, dirtyBits);
+ cv.callDisplayList(cv.ctx, rm.displayListId,
+ rm.isNonUniformScale);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean renderSeparateDlists(RenderMolecule rm,
+ Canvas3D cv,
+ int pass,
+ RenderAtomListInfo r, int dirtyBits) {
+
+ if (rm.doInfinite) {
+ cv.updateState(pass, dirtyBits);
+ while (r != null) {
+ cv.callDisplayList(cv.ctx,
+ ((GeometryArrayRetained)r.geometry()).dlistId,
+ rm.isNonUniformScale);
+ r = r.next;
+ }
+
+ return true;
+ }
+
+ boolean isVisible = false; // True if any of the RAs is visible.
+ while (r != null) {
+ if (cv.ra == r.renderAtom) {
+ if (cv.raIsVisible) {
+ cv.updateState(pass, dirtyBits);
+ cv.callDisplayList(cv.ctx,
+ ((GeometryArrayRetained)r.geometry()).dlistId,
+ rm.isNonUniformScale);
+ isVisible = true;
+ }
+ }
+ else {
+ if (r.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) {
+ cv.updateState(pass, dirtyBits);
+ cv.raIsVisible = true;
+ cv.callDisplayList(cv.ctx,
+ ((GeometryArrayRetained)r.geometry()).dlistId,
+ rm.isNonUniformScale);
+ isVisible = true;
+ }
+ else {
+ cv.raIsVisible = false;
+ }
+ cv.ra = r.renderAtom;
+ }
+ r = r.next;
+ }
+
+ return isVisible;
+ }
+
+
+
+ public boolean renderSeparateDlistPerRinfo(RenderMolecule rm,
+ Canvas3D cv,
+ int pass,
+ RenderAtomListInfo r,
+ int dirtyBits) {
+
+ if (rm.doInfinite) {
+ cv.updateState(pass, dirtyBits);
+ while (r != null) {
+ cv.callDisplayList(cv.ctx,r.renderAtom.dlistIds[r.index],
+ rm.isNonUniformScale);
+ r = r.next;
+ }
+ return true;
+ }
+ boolean isVisible = false; // True if any of the RAs is visible.
+ while (r != null) {
+ if (cv.ra == r.renderAtom) {
+ if (cv.raIsVisible) {
+ cv.updateState(pass, dirtyBits);
+ cv.callDisplayList(cv.ctx, r.renderAtom.dlistIds[r.index],
+ rm.isNonUniformScale);
+ isVisible = true;
+ }
+ }
+ else {
+ if (r.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) {
+ cv.updateState(pass, dirtyBits);
+ cv.raIsVisible = true;
+ cv.callDisplayList(cv.ctx, r.renderAtom.dlistIds[r.index],
+ rm.isNonUniformScale);
+ isVisible = true;
+ }
+ else {
+ cv.raIsVisible = false;
+ }
+ cv.ra = r.renderAtom;
+ }
+ r = r.next;
+ }
+ return isVisible;
+
+ }
+
+
+
+
+ void buildDisplayList(RenderMolecule rm, Canvas3D cv) {
+ RenderAtomListInfo ra;
+ boolean useAlpha;
+ GeometryArrayRetained geo;
+ useAlpha = rm.useAlpha;
+ Transform3D staticTransform;
+ Transform3D staticNormalTransform;
+
+ if ((rm.primaryRenderAtomList != null) &&
+ (rm.texCoordSetMapLen <= cv.numTexCoordSupported)) {
+
+ cv.newDisplayList(cv.ctx, rm.displayListId);
+
+ ra = rm.primaryRenderAtomList;
+
+ while (ra != null) {
+ geo = (GeometryArrayRetained)ra.geometry();
+ if (ra.renderAtom.geometryAtom.source.staticTransform == null) {
+ staticTransform = null;
+ staticNormalTransform = null;
+ } else {
+ staticTransform =
+ ra.renderAtom.geometryAtom.source.staticTransform.transform;
+ if ((geo.vertexFormat & GeometryArray.NORMALS) != 0) {
+ staticNormalTransform =
+ ra.renderAtom.geometryAtom.source.staticTransform.getNormalTransform();
+ } else {
+ staticNormalTransform = null;
+ }
+ }
+ geo.buildGA(cv, ra.renderAtom, false,
+ (useAlpha &&
+ ((geo.vertexFormat & GeometryArray.COLOR) != 0)),
+ rm.alpha,
+ rm.textureBin.attributeBin.ignoreVertexColors,
+ staticTransform,
+ staticNormalTransform);
+ ra = ra.next;
+ }
+ cv.endDisplayList(cv.ctx);
+ }
+ }
+
+ void buildIndividualDisplayList(RenderAtomListInfo ra, Canvas3D cv,
+ long ctx) {
+ GeometryArrayRetained geo;
+
+ geo = (GeometryArrayRetained)ra.geometry();
+ if ((geo.texCoordSetMap != null) &&
+ (geo.texCoordSetMap.length > cv.numTexCoordSupported)) {
+ return;
+ }
+
+ // Note, the dlistId does not change when renderer is building
+ cv.newDisplayList(ctx, geo.dlistId);
+
+ // No need to lock when it is indexed geometry since we have
+ // our own copy
+ // Note individual dlist is only created if alpha is not modifiable
+ // so, we don't need any renderMolecule specific information
+ geo.buildGA(cv, ra.renderAtom, false,
+ false,
+ 1.0f,
+ false,
+ null, null);
+ cv.endDisplayList(ctx);
+ }
+
+ void buildDlistPerRinfo(RenderAtomListInfo ra, RenderMolecule rm, Canvas3D cv) {
+ boolean useAlpha;
+ GeometryArrayRetained geo;
+ useAlpha = rm.useAlpha;
+ Transform3D staticTransform;
+ Transform3D staticNormalTransform;
+ int id;
+
+ geo = (GeometryArrayRetained)ra.geometry();
+ if ((rm.primaryRenderAtomList != null) &&
+ (rm.texCoordSetMapLen <= cv.numTexCoordSupported)) {
+
+ id = ra.renderAtom.dlistIds[ra.index];
+ cv.newDisplayList(cv.ctx, id);
+ geo = (GeometryArrayRetained)ra.geometry();
+ if (ra.renderAtom.geometryAtom.source.staticTransform == null) {
+ staticTransform = null;
+ staticNormalTransform = null;
+ } else {
+ staticTransform =
+ ra.renderAtom.geometryAtom.source.staticTransform.transform;
+ if ((geo.vertexFormat & GeometryArray.NORMALS) != 0) {
+ staticNormalTransform =
+ ra.renderAtom.geometryAtom.source.staticTransform.getNormalTransform();
+ } else {
+ staticNormalTransform = null;
+ }
+ }
+
+ geo.buildGA(cv, ra.renderAtom, false,
+ (useAlpha &&
+ ((geo.vertexFormat & GeometryArray.COLOR) != 0)),
+ rm.alpha,
+ rm.textureBin.attributeBin.ignoreVertexColors,
+ staticTransform,
+ staticNormalTransform);
+ cv.endDisplayList(cv.ctx);
+ }
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/DistanceLOD.java b/src/classes/share/javax/media/j3d/DistanceLOD.java
new file mode 100644
index 0000000..3148997
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DistanceLOD.java
@@ -0,0 +1,294 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Point3f;
+import java.util.Enumeration;
+
+/**
+ * This class defines a distance-based LOD behavior node that operates on
+ * a Switch group node to select one of the children of that Switch node
+ * based on the distance of this LOD node from the viewer.
+ * An array of <i>n</i> monotonically increasing distance values is
+ * specified, such that distances[0] is associated with the highest level of
+ * detail and distances[<i>n</i>-1] is associated with the lowest level of
+ * detail. Based on the actual distance from the viewer to
+ * this DistanceLOD node, these <i>n</i>
+ * distance values [0, <i>n</i>-1] select from among <i>n</i>+1
+ * levels of detail [0, <i>n</i>]. If <i>d</i> is the distance from
+ * the viewer to the LOD node, then the equation for determining
+ * which level of detail (child of the Switch node) is selected is:
+ * <p>
+ * <ul>
+ * 0, if <i>d</i> <= distances[0]
+ * <br>
+ * <i>i</i>, if distances[<i>i</i>-1] < <i>d</i> <= distances[<i>i</i>]
+ * <br>
+ * <i>n</i>, if d > distances[<i>n</i>-1]
+ * </ul>
+ * <p>
+ * Note that both the position and the array of distances are
+ * specified in the local coordinate system of this node.
+ */
+public class DistanceLOD extends LOD {
+
+ private double distances[];
+ private Point3f position = new Point3f(0.0f, 0.0f, 0.0f);
+
+ // variables for processStimulus
+ private Point3f center = new Point3f();
+ private Point3f viewPosition = new Point3f();
+
+ /**
+ * Constructs and initializes a DistanceLOD node with default values.
+ * Note that the default constructor creates a DistanceLOD object with
+ * a single distance value set to 0.0 and is, therefore, not useful.
+ */
+ public DistanceLOD() {
+ distances = new double[1];
+ distances[0] = 0.0;
+ }
+
+ /**
+ * Constructs and initializes a DistanceLOD node with the specified
+ * array of distances and a default position of (0,0,0).
+ * @param distances an array of values representing LOD cutoff distances
+ */
+ public DistanceLOD(float[] distances) {
+ this.distances = new double[distances.length];
+
+ for(int i=0;i<distances.length;i++) {
+ this.distances[i] = (double)distances[i];
+ }
+ }
+
+ /**
+ * Constructs and initializes a DistanceLOD node with the specified
+ * array of distances and the specified position.
+ * @param distances an array of values representing LOD cutoff distances
+ * @param position the position of this LOD node
+ */
+ public DistanceLOD(float[] distances, Point3f position) {
+ this.distances = new double[distances.length];
+
+ for(int i=0;i<distances.length;i++) {
+ this.distances[i] = (double)distances[i];
+ }
+ this.position.set(position);
+ }
+
+ /**
+ * Sets the position of this LOD node. This position is specified in
+ * the local coordinates of this node, and is
+ * the position from which the distance to the viewer is computed.
+ * @param position the new position
+ */
+ public void setPosition(Point3f position) {
+ if (((NodeRetained)retained).staticTransform != null) {
+ ((NodeRetained)retained).staticTransform.transform.transform(
+ position, this.position);
+ } else {
+ this.position.set(position);
+ }
+ }
+
+ /**
+ * Retrieves the current position of this LOD node. This position is
+ * in the local coordinates of this node.
+ * @param position the object that will receive the current position
+ */
+ public void getPosition(Point3f position) {
+ if (((NodeRetained)retained).staticTransform != null) {
+ Transform3D invTransform =
+ ((NodeRetained)retained).staticTransform.getInvTransform();
+ invTransform.transform(this.position, position);
+ } else {
+ position.set(this.position);
+ }
+ }
+
+ /**
+ * Returns a count of the number of LOD distance cut-off parameters.
+ * Note that the number of levels of detail (children of the Switch node)
+ * is one greater than the number of distance values.
+ * @return a count of the LOD cut-off distances
+ */
+ public int numDistances() {
+ return distances.length;
+ }
+
+ /**
+ * Returns a particular LOD cut-off distance.
+ * @param whichDistance an index specifying which LOD distance to return
+ * @return the cut-off distance value associated with the index provided
+ */
+ public double getDistance(int whichDistance) {
+ return distances[whichDistance];
+ }
+
+ /**
+ * Sets a particular LOD cut-off distance.
+ * @param whichDistance an index specifying which LOD distance to modify
+ * @param distance the cut-off distance associated with the index provided
+ */
+ public void setDistance(int whichDistance, double distance) {
+ distances[whichDistance] = distance;
+ }
+
+ /**
+ * Initialize method that sets up initial wakeup criteria.
+ */
+ public void initialize() {
+ // Insert wakeup condition into queue
+ wakeupOn(wakeupFrame);
+ }
+
+ /**
+ * Process stimulus method that computes appropriate level of detail.
+ * @param criteria an enumeration of the criteria that caused the
+ * stimulus
+ */
+ public void processStimulus(Enumeration criteria) {
+
+
+ // compute distance in virtual world
+ View v = this.getView();
+ if( v == null ) {
+ wakeupOn(wakeupFrame);
+ return;
+ }
+
+ ViewPlatform vp = v.getViewPlatform();
+ if (vp == null) {
+ return;
+ }
+
+ // Handle stimulus
+ double viewDistance = 0.0;
+ int nSwitches,i,index=0;
+
+ Transform3D localToWorldTrans = VirtualUniverse.mc.getTransform3D(null);
+
+ localToWorldTrans.set(((NodeRetained)this.retained).getCurrentLocalToVworld());
+
+
+ // DistanceLOD's location in virutal world
+ localToWorldTrans.transform( position, center);
+
+
+ viewPosition.x = (float)((ViewPlatformRetained)vp.retained).schedSphere.center.x;
+ viewPosition.y = (float)((ViewPlatformRetained)vp.retained).schedSphere.center.y;
+ viewPosition.z = (float)((ViewPlatformRetained)vp.retained).schedSphere.center.z;
+ viewDistance = center.distance( viewPosition);
+
+
+ // convert distance into local coordinates
+ viewDistance = viewDistance/localToWorldTrans.getDistanceScale();
+
+ nSwitches = numSwitches();
+
+ index = distances.length; // viewDistance > distances[n-1]
+
+ if( viewDistance <= distances[0] ) {
+ index = 0;
+ } else {
+ for (i=1; i < distances.length; i++) {
+ if ((viewDistance > distances[i-1]) &&
+ (viewDistance <= distances[i])) {
+ index = i;
+ break;
+ }
+ }
+ }
+
+ for(i=nSwitches-1; i>=0; i--) {
+ Switch sw = getSwitch(i);
+ // Optimize, this behavior is passive
+ // Note that we skip the capability check for getWhichChild()
+ if (((SwitchRetained) sw.retained).getWhichChild() !=
+ index) {
+ sw.setWhichChild(index);
+ }
+ }
+
+ VirtualUniverse.mc.addToTransformFreeList(localToWorldTrans);
+
+ // Insert wakeup condition into queue
+ wakeupOn(wakeupFrame);
+
+ }
+
+ /**
+ * 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) {
+ DistanceLOD d = new DistanceLOD();
+ d.duplicateNode(this, forceDuplicate);
+ return d;
+ }
+
+
+ /**
+ * Copies all DistanceLOD 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ DistanceLOD lod = (DistanceLOD) originalNode;
+
+ int numD = lod.numDistances();
+
+ // No API available to set the size of this array after initialize
+ this.distances = new double[numD];
+
+ for (int i = 0; i < numD; i++)
+ setDistance(i, lod.getDistance(i));
+
+ Point3f p = new Point3f();
+ lod.getPosition(p);
+ setPosition(p);
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ xform.transform.transform(position, position);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DrawingSurfaceObject.java b/src/classes/share/javax/media/j3d/DrawingSurfaceObject.java
new file mode 100644
index 0000000..5a7444f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DrawingSurfaceObject.java
@@ -0,0 +1,42 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The DrawingSurfaceObject class is used to manage native drawing surface
+ */
+
+abstract class DrawingSurfaceObject extends Object {
+
+ Canvas3D canvas;
+ boolean gotDsiLock = false;
+ boolean onScreen;
+
+ abstract boolean renderLock();
+ abstract void unLock();
+ abstract void getDrawingSurfaceObjectInfo();
+ abstract void invalidate();
+
+ DrawingSurfaceObject(Canvas3D cv) {
+ canvas = cv;
+ onScreen = !cv.offScreen;
+ }
+
+ synchronized boolean isLocked() {
+ return gotDsiLock;
+ }
+
+ synchronized void contextValidated() {
+ canvas.validCtx = true;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/DrawingSurfaceObjectAWT.java b/src/classes/share/javax/media/j3d/DrawingSurfaceObjectAWT.java
new file mode 100644
index 0000000..66c38b5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/DrawingSurfaceObjectAWT.java
@@ -0,0 +1,142 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.Point;
+
+/**
+ * The DrawingSurfaceObject class is used to manage native drawing surface
+ */
+
+class DrawingSurfaceObjectAWT extends DrawingSurfaceObject {
+
+ // drawing surface
+ long nativeDS = 0;
+ long dsi = 0;
+
+ boolean doLastUnlock = false;
+ boolean xineramaDisabled = false;
+
+ long display = 0;
+ int screenID = 0;
+
+ static long nativeAWT = 0;
+
+ native boolean lockAWT(long ds);
+ native void unlockAWT(long ds);
+ static native void lockGlobal(long awt);
+ static native void unlockGlobal(long awt);
+ native long getDrawingSurfaceAWT(Canvas3D cv, long awt);
+ native long getDrawingSurfaceInfo(long ds);
+ static native void freeResource(long awt, long ds, long dsi);
+ native int getDrawingSurfaceWindowIdAWT(Canvas3D cv, long ds, long dsi,
+ long display, int screenID,
+ boolean xineramaDisabled);
+
+ DrawingSurfaceObjectAWT(Canvas3D cv, long awt,
+ long display, int screenID,
+ boolean xineramaDisabled) {
+ super(cv);
+ nativeAWT = awt;
+
+ this.display = display;
+ this.screenID = screenID;
+ this.xineramaDisabled = xineramaDisabled;
+ }
+
+ synchronized boolean renderLock() {
+
+ if (onScreen) {
+ if (nativeDS == 0) {
+ return false;
+ } else {
+ if (lockAWT(nativeDS)) {
+ gotDsiLock = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ } else {
+ gotDsiLock = true;
+ lockGlobal(nativeAWT);
+ }
+ return true;
+ }
+
+ synchronized void unLock() {
+
+ if (gotDsiLock) {
+ if (onScreen) {
+ if (nativeDS != 0) {
+ unlockAWT(nativeDS);
+ gotDsiLock = false;
+ if (doLastUnlock) {
+ nativeDS = 0;
+ dsi = 0;
+ doLastUnlock = false;
+ }
+ }
+ } else {
+ unlockGlobal(nativeAWT);
+ gotDsiLock = false;
+ }
+ }
+ }
+
+
+ synchronized void getDrawingSurfaceObjectInfo() {
+ // get native drawing surface - ds
+ nativeDS = getDrawingSurfaceAWT(canvas, nativeAWT);
+
+ // get window id
+ if (nativeDS != 0) {
+ dsi = getDrawingSurfaceInfo(nativeDS);
+ if (dsi != 0) {
+ canvas.window = getDrawingSurfaceWindowIdAWT
+ (canvas, nativeDS, dsi, display, screenID,
+ xineramaDisabled);
+ }
+ }
+
+ }
+
+
+ synchronized void invalidate() {
+ if (gotDsiLock && (nativeDS != 0)) {
+ // Should not call unlock in AWT thread
+ // Otherwise IllegalMonitorException will throw
+ // unlockAWT(nativeDS);
+ // We don't reset nativeDS & dsi to 0 here.
+ // This allow Renderer to continue unLock.
+ doLastUnlock = true;
+ } else {
+ nativeDS = 0;
+ dsi = 0;
+ }
+ }
+
+ static void freeDrawingSurface(Object obj) {
+ long p[] = (long[]) obj;
+ freeResource(nativeAWT, p[0], p[1]);
+ }
+
+ long getDSI() {
+ return dsi;
+ }
+
+ long getDS() {
+ return nativeDS;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/EnvironmentSet.java b/src/classes/share/javax/media/j3d/EnvironmentSet.java
new file mode 100644
index 0000000..2083927
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/EnvironmentSet.java
@@ -0,0 +1,563 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+/**
+ * The LightBin manages a collection of EnvironmentSet objects.
+ * The number of objects managed depends upon the number of Lights
+ * in each EnvironmentSet and the number of lights supported by
+ * the underlying rendering layer.
+ */
+
+class EnvironmentSet extends Object implements ObjectUpdate{
+ // A list of pre-defined bits to indicate which component
+ // of the rendermolecule changed
+ static final int LIGHTENABLE_CHANGED = 0x01;
+ static final int AMBIENT_CHANGED = 0x02;
+ static final int FOG_CHANGED = 0x04;
+ static final int MODELCLIP_CHANGED = 0x08;
+
+ /**
+ * The ArrayList of Lights in this EnvironmentSet
+ */
+ ArrayList lights = new ArrayList();
+
+ /**
+ * The position of the light in the lightbin that the
+ * lights in this environment set corresponds to
+ */
+ int[] ltPos = null;
+
+
+ /**
+ * The arraylist of ambient lights in this env list
+ */
+ ArrayList ambLights = new ArrayList();
+
+ /**
+ * The LightBin that this EnvironmentSet resides
+ */
+ LightBin lightBin = null;
+
+ /**
+ * The bitmask of light slots that need to be enabled for this
+ */
+ long enableMask = 0;
+
+ /**
+ * The cached scene ambient component for this EnvirionmentSet
+ */
+ Color3f sceneAmbient = new Color3f();
+
+ /**
+ * The RenderBin for this EnvirionmentSet
+ */
+ RenderBin renderBin = null;
+
+ /**
+ * The fog for this EnvironmentSet
+ */
+ FogRetained fog = null;
+
+
+ /**
+ * The model clip for this EnvironmentSet
+ */
+ ModelClipRetained modelClip = null;
+
+ /**
+ * enable mask for the model clip planes in this environment set
+ */
+ int enableMCMask = 0; // enable mask used in modelClip.update()
+ int enableMCMaskCache = 0; // enable mask computed in renderBin that
+ // is copied into enableMCMask in updateObject
+
+ /**
+ * The references to the next and previous LightBins in the
+ * list.
+ */
+ EnvironmentSet next = null;
+ EnvironmentSet prev = null;
+
+ /**
+ * List of attrributeBins to be added next Frame
+ */
+ ArrayList addAttributeBins = new ArrayList();
+
+
+ /**
+ * Canvas Dirty Mask for
+ */
+ int canvasDirty = 0;
+
+ /**
+ * cached value of enable mask
+ */
+ long enableMaskCache = 0;
+
+ /**
+ *
+ */
+ boolean onUpdateList = false;
+
+ /**
+ * The list of AttributeBins in this EnvironmentSet
+ */
+ AttributeBin attributeBinList = null;
+
+ EnvironmentSet(RenderAtom ra, LightRetained[] lightList, FogRetained fog,
+ ModelClipRetained modelClip, RenderBin rb) {
+ renderBin = rb;
+ reset(ra, lightList, fog, modelClip);
+ }
+
+ void reset(RenderAtom ra, LightRetained[] lightList, FogRetained fog,
+ ModelClipRetained modelClip) {
+ int i;
+ LightRetained light;
+
+ prev = null;
+ next = null;
+ onUpdateList = false;
+ attributeBinList = null;
+ lights.clear();
+ ambLights.clear();
+ sceneAmbient.x = 0.0f;
+ sceneAmbient.y = 0.0f;
+ sceneAmbient.z = 0.0f;
+ if (lightList != null) {
+ for (i=0; i<lightList.length; i++) {
+ light = lightList[i];
+ if (light.nodeType == NodeRetained.AMBIENTLIGHT) {
+ ambLights.add(light);
+ sceneAmbient.x += light.color.x;
+ sceneAmbient.y += light.color.y;
+ sceneAmbient.z += light.color.z;
+ }
+ else {
+ lights.add(light);
+ }
+
+ light.environmentSets.add(this);
+ }
+ if (sceneAmbient.x > 1.0f) {
+ sceneAmbient.x = 1.0f;
+ }
+ if (sceneAmbient.y > 1.0f) {
+ sceneAmbient.y = 1.0f;
+ }
+ if (sceneAmbient.z > 1.0f) {
+ sceneAmbient.z = 1.0f;
+ }
+ }
+ this.fog = fog;
+ if (fog != null) {
+ fog.environmentSets.add(this);
+ }
+
+ this.modelClip = modelClip;
+ enableMCMaskCache = 0;
+ if (modelClip != null) {
+ modelClip.environmentSets.add(this);
+
+ for (i = 0; i < 6; i++) {
+ if (modelClip.enables[i])
+ enableMCMaskCache |= 1 << i;
+ }
+ enableMCMask = enableMCMaskCache;
+ }
+
+ // Allocate the ltPos array
+ ltPos = new int[lights.size()];
+ enableMask = 0;
+ }
+
+ /**
+ * This tests if the qiven lights and fog match this EnviornmentSet
+ */
+ boolean equals(RenderAtom ra, LightRetained[] lights, FogRetained fog,
+ ModelClipRetained modelClip) {
+ int i;
+
+
+ // First see if the lights match.
+ if (lights == null && ambLights == null) {
+ if (this.lights.size() == 0) {
+ if (this.fog == fog) {
+ return (true);
+ } else {
+ return (false);
+ }
+ } else {
+ return (false);
+ }
+ }
+
+ if ((this.lights.size() + this.ambLights.size())!= lights.length) {
+ return (false);
+ }
+
+ for (i=0; i<lights.length; i++) {
+ if (lights[i].nodeType == LightRetained.AMBIENTLIGHT) {
+ if (!this.ambLights.contains(lights[i])) {
+ return (false);
+ }
+ }
+ else {
+ if (!this.lights.contains(lights[i])) {
+ return (false);
+ }
+ }
+ }
+
+ // Now check fog
+ if (this.fog != fog) {
+ return (false);
+ }
+
+ // Now check model clip
+ if (this.modelClip != modelClip) {
+ return (false);
+ }
+
+ return (true);
+ }
+
+
+ /**
+ * This tests if the qiven lights match this EnviornmentSet
+ */
+ boolean equalLights(LightRetained[] lights) {
+ int i;
+
+ // First see if the lights match.
+ if (lights == null && ambLights == null) {
+ if (this.lights.size() == 0) {
+ return (true);
+ }
+ }
+
+ if ((this.lights.size() + this.ambLights.size())!= lights.length) {
+ return (false);
+ }
+
+ for (i=0; i<lights.length; i++) {
+ if (lights[i].nodeType == LightRetained.AMBIENTLIGHT) {
+ if (!this.ambLights.contains(lights[i])) {
+ return (false);
+ }
+ }
+ else {
+ if (!this.lights.contains(lights[i])) {
+ return (false);
+ }
+ }
+ }
+
+ return (true);
+ }
+
+
+ public void updateObject() {
+ int i;
+ AttributeBin a;
+
+ if (addAttributeBins.size() > 0) {
+ a = (AttributeBin)addAttributeBins.get(0);
+ if (attributeBinList == null) {
+ attributeBinList = a;
+
+ }
+ else {
+ a.next = attributeBinList;
+ attributeBinList.prev = a;
+ attributeBinList = a;
+ }
+ for (i = 1; i < addAttributeBins.size() ; i++) {
+ a = (AttributeBin) addAttributeBins.get(i);
+ a.next = attributeBinList;
+ attributeBinList.prev = a;
+ attributeBinList = a;
+ }
+ }
+
+ addAttributeBins.clear();
+
+ if (canvasDirty != 0) {
+ Canvas3D canvases[] = renderBin.view.getCanvases();
+
+ for (i = 0; i < canvases.length; i++) {
+ canvases[i].canvasDirty |= canvasDirty;
+ }
+
+ if ((canvasDirty & Canvas3D.AMBIENTLIGHT_DIRTY) != 0) {
+ updateSceneAmbient();
+ }
+
+ if ((canvasDirty & Canvas3D.LIGHTENABLES_DIRTY) != 0) {
+ enableMask = enableMaskCache;
+ }
+
+ if ((canvasDirty & Canvas3D.MODELCLIP_DIRTY) != 0) {
+ enableMCMask = enableMCMaskCache;
+ }
+
+ canvasDirty = 0;
+ }
+ onUpdateList = false;
+ }
+
+ /**
+ * Adds the given AttributeBin to this EnvironmentSet.
+ */
+ void addAttributeBin(AttributeBin a, RenderBin rb) {
+ a.environmentSet = this;
+ addAttributeBins.add(a);
+ if (!onUpdateList) {
+ rb.objUpdateList.add(this);
+ onUpdateList = true;
+ }
+ }
+
+ /**
+ * Removes the given AttributeBin from this EnvironmentSet.
+ */
+ void removeAttributeBin(AttributeBin a) {
+ LightRetained light;
+ int i;
+
+ a.environmentSet = null;
+ // If the attributeBin being remove is contained in addAttributeBins, then
+ // remove the attributeBin from the addList
+ if (addAttributeBins.contains(a)) {
+ addAttributeBins.remove(addAttributeBins.indexOf(a));
+ }
+ else {
+ if (a.prev == null) { // At the head of the list
+ attributeBinList = a.next;
+ if (a.next != null) {
+ a.next.prev = null;
+ }
+ } else { // In the middle or at the end.
+ a.prev.next = a.next;
+ if (a.next != null) {
+ a.next.prev = a.prev;
+ }
+ }
+ }
+ a.prev = null;
+ a.next = null;
+
+ if (a.definingRenderingAttributes != null &&
+ (a.definingRenderingAttributes.changedFrequent != 0))
+ a.definingRenderingAttributes = null;
+ a.onUpdateList &= ~AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
+
+ // Add this attributebin to the free list
+ renderBin.attrBinFreelist.add(a);
+
+ if (attributeBinList == null && addAttributeBins.size() == 0) {
+ // Now remove this environment set from all the lights and fogs
+ // that use this
+ int sz = lights.size();
+ for (i=0; i < sz; i++) {
+ ((LightRetained) lights.get(i)).environmentSets.remove(this);
+ }
+ sz = ambLights.size();
+ for (i = 0; i < sz; i++) {
+ ((LightRetained) ambLights.get(i)).environmentSets.remove(this);
+ }
+ if (fog != null) {
+ fog.environmentSets.remove(this);
+ }
+ lightBin.removeEnvironmentSet(this);
+ }
+ }
+
+ void updateSceneAmbient()
+ {
+ int i;
+ sceneAmbient.x = 0.0f;
+ sceneAmbient.y = 0.0f;
+ sceneAmbient.z = 0.0f;
+ for (i=0; i<ambLights.size(); i++) {
+ LightRetained aL = (LightRetained) ambLights.get(i);
+ if (aL.lightOn) {
+ sceneAmbient.x += aL.color.x;
+ sceneAmbient.y += aL.color.y;
+ sceneAmbient.z += aL.color.z;
+ }
+ }
+ if (sceneAmbient.x > 1.0f) {
+ sceneAmbient.x = 1.0f;
+ }
+ if (sceneAmbient.y > 1.0f) {
+ sceneAmbient.y = 1.0f;
+ }
+ if (sceneAmbient.z > 1.0f) {
+ sceneAmbient.z = 1.0f;
+ }
+ }
+
+ /**
+ * Renders this EnvironmentSet
+ */
+ void render(Canvas3D cv) {
+ AttributeBin a;
+
+ // include this EnvironmentSet to the to-be-updated list in Canvas
+ cv.setStateToUpdate(Canvas3D.ENVIRONMENTSET_BIT, this);
+
+ a = attributeBinList;
+ while (a != null) {
+ a.render(cv);
+ a = a.next;
+ }
+ }
+
+
+ void updateAttributes(Canvas3D cv) {
+ LightRetained light;
+ int i, numLights;
+ float red, green, blue;
+ double scale;
+
+ // within frame
+ if (cv.environmentSet != this ) {
+ if (cv.enableMask != enableMask) {
+ cv.setLightEnables(cv.ctx, enableMask, renderBin.maxLights);
+ cv.enableMask = enableMask;
+ }
+
+ if (cv.sceneAmbient.x != sceneAmbient.x ||
+ cv.sceneAmbient.y != sceneAmbient.y ||
+ cv.sceneAmbient.z != sceneAmbient.z ) {
+
+ cv.setSceneAmbient(cv.ctx,
+ sceneAmbient.x, sceneAmbient.y, sceneAmbient.z);
+
+ cv.sceneAmbient.x = sceneAmbient.x;
+ cv.sceneAmbient.y = sceneAmbient.y;
+ cv.sceneAmbient.z = sceneAmbient.z;
+ }
+
+
+ if (cv.fog != fog) {
+ if (fog != null) {
+ scale = lightBin.geometryBackground == null?
+ cv.canvasViewCache.getVworldToCoexistenceScale():
+ cv.canvasViewCache.getInfVworldToCoexistenceScale();
+ fog.update(cv.ctx, scale);
+ } else {
+ cv.disableFog(cv.ctx);
+ }
+ cv.fog = fog;
+ }
+
+ if (cv.modelClip != modelClip) {
+ if (modelClip != null) {
+ modelClip.update(cv, enableMCMask);
+ } else {
+ cv.disableModelClip(cv.ctx);
+ }
+ cv.modelClip = modelClip;
+ }
+
+ cv.environmentSet = this;
+ cv.canvasDirty &= ~(Canvas3D.LIGHTENABLES_DIRTY|
+ Canvas3D.AMBIENTLIGHT_DIRTY |
+ Canvas3D.FOG_DIRTY |
+ Canvas3D.MODELCLIP_DIRTY);
+
+ }
+ // across frames
+ else if ((cv.canvasDirty & (Canvas3D.LIGHTENABLES_DIRTY|
+ Canvas3D.AMBIENTLIGHT_DIRTY|
+ Canvas3D.FOG_DIRTY|
+ Canvas3D.MODELCLIP_DIRTY)) != 0) {
+
+ if ((cv.canvasDirty & Canvas3D.LIGHTENABLES_DIRTY) != 0) {
+ cv.setLightEnables(cv.ctx, enableMask, renderBin.maxLights);
+ cv.enableMask = enableMask;
+ }
+
+ if ((cv.canvasDirty & Canvas3D.AMBIENTLIGHT_DIRTY) != 0) {
+ cv.setSceneAmbient(cv.ctx, sceneAmbient.x,
+ sceneAmbient.y,
+ sceneAmbient.z);
+ cv.sceneAmbient.x = sceneAmbient.x;
+ cv.sceneAmbient.y = sceneAmbient.y;
+ cv.sceneAmbient.z = sceneAmbient.z;
+ }
+
+ if ((cv.canvasDirty & Canvas3D.FOG_DIRTY) != 0) {
+ if (fog != null) {
+ scale = lightBin.geometryBackground == null?
+ cv.canvasViewCache.getVworldToCoexistenceScale():
+ cv.canvasViewCache.getInfVworldToCoexistenceScale();
+ fog.update(cv.ctx, scale);
+ } else {
+ cv.disableFog(cv.ctx);
+ }
+ cv.fog = fog;
+ }
+
+ if ((cv.canvasDirty & Canvas3D.MODELCLIP_DIRTY) != 0) {
+ if (modelClip != null) {
+ modelClip.update(cv, enableMCMask);
+ } else {
+ cv.disableModelClip(cv.ctx);
+ }
+ cv.modelClip = modelClip;
+ }
+
+ cv.canvasDirty &= ~(Canvas3D.LIGHTENABLES_DIRTY|
+ Canvas3D.AMBIENTLIGHT_DIRTY |
+ Canvas3D.FOG_DIRTY |
+ Canvas3D.MODELCLIP_DIRTY);
+ }
+ else if ((cv.canvasDirty & Canvas3D.VWORLD_SCALE_DIRTY) != 0) {
+ if (fog instanceof LinearFogRetained) {
+ if (fog != null) {
+ scale = lightBin.geometryBackground == null?
+ cv.canvasViewCache.getVworldToCoexistenceScale():
+ cv.canvasViewCache.getInfVworldToCoexistenceScale();
+ fog.update(cv.ctx, scale);
+ } else {
+ cv.disableFog(cv.ctx);
+ }
+ cv.fog = fog;
+ }
+
+ if (modelClip != null) {
+ modelClip.update(cv, enableMCMask);
+ cv.modelClip = modelClip;
+ }
+ }
+ else if (cv.useStereo) {
+
+ // if using stereo, the vworldToEc matrix will be different
+ // for each stereo pass, in this case, we will need to
+ // update modelClip
+
+ if (modelClip != null) {
+ modelClip.update(cv, enableMCMask);
+ cv.modelClip = modelClip;
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/EventCatcher.java b/src/classes/share/javax/media/j3d/EventCatcher.java
new file mode 100644
index 0000000..5fb9d39
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/EventCatcher.java
@@ -0,0 +1,388 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.*;
+import java.awt.event.*;
+
+
+/**
+ * The EventCatcher class is used to track events on a Canvas3D using the
+ * 1.1 event model. Most events are sent to the canvas for processing.
+ */
+class EventCatcher extends Object implements ComponentListener, FocusListener,
+ KeyListener, MouseListener, MouseMotionListener, WindowListener {
+
+ // The canvas associated with this event catcher
+ Canvas3D canvas;
+ static final boolean DEBUG = false;
+ boolean stopped = false;
+
+ /**
+ * flags for event listeners
+ */
+ boolean componentEvents = false;
+ boolean focusEvents = false;
+ boolean keyEvents = false;
+ boolean mouseEvents = false;
+ boolean mouseMotionEvents = false;
+ boolean mouseListenerAdded = false;
+
+ EventCatcher(Canvas3D c) {
+ canvas = c;
+
+ if (VirtualUniverse.mc.isD3D()) {
+ enableComponentEvents();
+ enableKeyEvents();
+ }
+ }
+
+
+ void enableComponentEvents() {
+
+ if (!componentEvents) {
+ canvas.addComponentListener(this);
+ componentEvents = true;
+ }
+ }
+
+ /*
+ void disableComponentEvents() {
+ if (componentEvents) {
+ canvas.removeComponentListener(this);
+ componentEvents = false;
+ }
+ }
+ */
+
+ void enableFocusEvents() {
+ if (!focusEvents) {
+ canvas.addFocusListener(this);
+ focusEvents = true;
+ }
+ }
+
+
+ void disableFocusEvents() {
+ if (focusEvents) {
+ canvas.removeFocusListener(this);
+ focusEvents = false;
+ }
+ }
+
+ void enableKeyEvents() {
+ if (!keyEvents) {
+ canvas.addKeyListener(this);
+ keyEvents = true;
+ // listen for mouseEntered events for keyboard focusing
+ if (!mouseListenerAdded) {
+ canvas.addMouseListener(this);
+ mouseListenerAdded = true;
+ }
+ }
+ }
+
+ void disableKeyEvents() {
+ if (keyEvents) {
+ canvas.removeKeyListener(this);
+ keyEvents = false;
+ // listen for mouseEntered events for keyboard focusing
+ if (!mouseEvents) {
+ if (mouseListenerAdded) {
+ canvas.removeMouseListener(this);
+ mouseListenerAdded = false;
+ }
+ }
+ }
+ }
+
+
+
+ void enableMouseEvents() {
+ if (!mouseEvents) {
+ mouseEvents = true;
+ if (!mouseListenerAdded) {
+ canvas.addMouseListener(this);
+ mouseListenerAdded = true;
+ }
+ }
+ }
+
+ void disableMouseEvents() {
+ if (mouseEvents) {
+ mouseEvents = false;
+ if (!keyEvents) {
+ if (mouseListenerAdded) {
+ canvas.removeMouseListener(this);
+ mouseListenerAdded = false;
+ }
+ }
+ }
+ }
+
+ void enableMouseMotionEvents() {
+ if (!mouseMotionEvents) {
+ canvas.addMouseMotionListener(this);
+ mouseMotionEvents = true;
+ }
+ }
+
+
+ void disableMouseMotionEvents() {
+ if (mouseMotionEvents) {
+ canvas.removeMouseMotionListener(this);
+ mouseMotionEvents = false;
+ }
+ }
+
+
+ public void componentResized(ComponentEvent e) {
+ if (e.getSource() == canvas) {
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = true;
+ if (VirtualUniverse.mc.isD3D()) {
+ canvas.notifyD3DPeer(Canvas3D.RESIZE);
+ }
+ canvas.evaluateActive();
+ repaint();
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+ }
+
+ public void componentHidden(ComponentEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = false;
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ if (VirtualUniverse.mc.isD3D()) {
+ canvas.notifyD3DPeer(Canvas3D.RESIZE);
+ }
+ repaint();
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void componentShown(ComponentEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = true;
+ canvas.evaluateActive();
+ repaint();
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void focusGained(FocusEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void focusLost(FocusEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void keyTyped(KeyEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void keyPressed(KeyEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+
+ if (VirtualUniverse.mc.isD3D() &&
+ e.isAltDown() &&
+ (e.getKeyCode() == KeyEvent.VK_ENTER)) {
+ canvas.notifyD3DPeer(Canvas3D.TOGGLEFULLSCREEN);
+ }
+
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void keyReleased(KeyEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ if (stopped) {
+ stopped = false;
+ } else {
+ stopped = true;
+ }
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void mouseClicked(MouseEvent e) {
+// if (keyEvents &&
+// (VirtualUniverse.mc.getRenderingAPI() !=
+// MasterControl.RENDER_OPENGL_SOLARIS)) {
+// // bug 4362074
+// canvas.requestFocus();
+// }
+
+ if (mouseEvents) {
+ canvas.sendEventToBehaviorScheduler(e);
+ }
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void mouseEntered(MouseEvent e) {
+// if (keyEvents &&
+// (VirtualUniverse.mc.getRenderingAPI() ==
+// MasterControl.RENDER_OPENGL_SOLARIS)) {
+// // bug 4362074
+// canvas.requestFocus();
+// }
+ if (mouseEvents) {
+ canvas.sendEventToBehaviorScheduler(e);
+ }
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void mouseExited(MouseEvent e) {
+ if (mouseEvents)
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void mousePressed(MouseEvent e) {
+ if (mouseEvents)
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ if (mouseEvents)
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void mouseDragged(MouseEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void mouseMoved(MouseEvent e) {
+ canvas.sendEventToBehaviorScheduler(e);
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ }
+
+ public void windowActivated(WindowEvent e) {
+ windowOpened(e);
+ }
+
+ public void windowClosed(WindowEvent e) {
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = false;
+ canvas.evaluateActive();
+ }
+
+ public void windowClosing(WindowEvent e) {
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = false;
+ canvas.evaluateActive();
+ }
+
+ public void windowDeactivated(WindowEvent e) {
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ canvas.sendEventToBehaviorScheduler(e);
+ }
+
+ public void windowDeiconified(WindowEvent e) {
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = true;
+ if (canvas.view != null)
+ canvas.view.sendEventToSoundScheduler(e);
+ canvas.evaluateActive();
+ repaint();
+ }
+
+ public void windowIconified(WindowEvent e) {
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = false;
+ if (canvas.view != null)
+ canvas.view.sendEventToSoundScheduler(e);
+ canvas.evaluateActive();
+ }
+
+ public void windowOpened(WindowEvent e) {
+ if (DEBUG) {
+ System.out.println(e);
+ }
+ canvas.sendEventToBehaviorScheduler(e);
+ canvas.visible = true;
+ canvas.evaluateActive();
+ repaint();
+ }
+
+ void repaint() {
+ if (canvas.view != null) {
+ canvas.view.repaint();
+ }
+ }
+
+ void reset() {
+ focusEvents = false;
+ keyEvents = false;
+ componentEvents = false;
+ mouseEvents = false;
+ mouseMotionEvents = false;
+ mouseListenerAdded = false;
+ stopped = false;
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/ExceptionStrings.properties b/src/classes/share/javax/media/j3d/ExceptionStrings.properties
new file mode 100644
index 0000000..a556194
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ExceptionStrings.properties
@@ -0,0 +1,892 @@
+Alpha0=Alpha: time <= 0
+Appearance0=Appearance: no capability to set material
+Appearance1=Appearance: no capability to get material
+Appearance2=Appearance: no capability to set texture
+Appearance3=Appearance: no capability to get texture
+Appearance4=Appearance: no capability to set textureAttributes
+Appearance5=Appearance: no capability to get textureAttributes
+Appearance6=Appearance: no capability to set coloringAttributes
+Appearance7=Appearance: no capability to get coloringAttributes
+Appearance8=Appearance: no capability to set transparencyAttributes
+Appearance9=Appearance: no capability to get transparencyAttributes
+Appearance10=Appearance: no capability to set renderingAttributes
+Appearance11=Appearance: no capability to get renderingAttributes
+Appearance12=Appearance: no capability to set polygonAttributes
+Appearance13=Appearance: no capability to get polygonAttributes
+Appearance14=Appearance: no capability to set lineAttributes
+Appearance15=Appearance: no capability to get lineAttributes
+Appearance16=Appearance: no capability to set pointAttributes
+Appearance17=Appearance: no capability to get pointAttributes
+Appearance18=Appearance: no capability to set TexCoordGeneraion
+Appearance19=Appearance: no capability to get TexGen
+Appearance20=Appearance: no capability to set TextureUnitState
+Appearance21=Appearance: no capability to get TextureUnitState
+BoundingSphere0=BoundingSphere( Bounds ) unrecognized bounds object
+BoundingSphere2=set( Bounds) unrecognized bounds type
+BoundingSphere3=BoundingSphere.combine( Bounds) unrecognized bounds type
+BoundingSphere4=BoundingSphere.combine( Bounds[]) unrecognized bounds type
+BoundingSphere5=transform( Bounds, trans) unrecognized bounds type
+BoundingSphere6=sphere.intersect(Bounds ) bounds type not recognized=
+BoundingSphere7=sphere.intersect(Bounds[]) bounds type not recognized=
+BoundingSphere8=BoundingSphere.intersect(Bounds, newBoundingSphere) bounds type not recognized=
+BoundingSphere9=BoundingSphere.intersect(Bounds[], newBoundingSphere) bounds type not recognized=
+BoundingSphere10=sphere.closestIntersection(Bounds[]) unrecognized bounds type
+Background0=Background: no capability to set color
+Background2=Background: no capability to get color
+Background3=Background: no capability to set image
+Background4=Background: no capability to get image
+Background5=Background: no capability to set background geometry
+Background6=Background: no capability to get background geometry
+Background7=Background: no capability to set application bounds
+Background8=Background: no capability to get application bounds
+Background9=Background: no capability to set image scale mode
+Background10=Background: no capability to get image scale mode
+Background11=Background: illegal image scale mode
+AlternateAppearance0=AlternateAppearance: no capability to write appearance
+AlternateAppearance2=AlternateAppearance: no capability to read appearance
+AlternateAppearance3=AlternateAppearance: no capability to write influencing bounds
+AlternateAppearance4=AlternateAppearance: no capability to read influencing bounds
+AlternateAppearance7=AlternateAppearance: no capability to write scope
+AlternateAppearance8=AlternateAppearance: no capability to read scope
+AlternateAppearance9=AlternateAppearance: no capability to insert scope
+AlternateAppearance10=AlternateAppearance: no capability to remove scope
+AlternateAppearance11=AlternateAppearance: no capability to read scopes
+AlternateAppearance12=AlternateAppearance: no capability to append scope
+AlternateAppearanceRetained13=AlternateAppearance: Immediate mode alternate appearance may not be in scene graph
+AlternateAppearanceRetained14=AlternateAppearance: Immediate mode appearance may not be in scene graph
+AlternateAppearanceRetained15=AlternateAppearance: illegal node under SharedGroup Branch
+AlternateAppearanceRetained16=AlternateAppearance: illegal node under Background geometry Branch
+AudioDeviceEnumerator0=No more audio devices
+AuralAttributes0=AuralAttributes: no capability to set attribute gain
+AuralAttributes1=AuralAttributes: no capability to get attribute gain
+AuralAttributes2=AuralAttributes: no capability to set rolloff
+AuralAttributes3=AuralAttributes: no capability to get rolloff
+AuralAttributes4=AuralAttributes: no capability to set reflection coefficient
+AuralAttributes5=AuralAttributes: no capability to set reverberation delay
+AuralAttributes7=AuralAttributes: no capability to get reverberation delay
+AuralAttributes8=AuralAttributes: no capability to set reverberation order
+AuralAttributes9=AuralAttributes: no capability to get reverberation order
+AuralAttributes10=AuralAttributes: no capability to set distance filter
+AuralAttributes12=AuralAttributes: no capability to get distance filter
+AuralAttributes15=AuralAttributes: no capability to set Doppler scale factor
+AuralAttributes17=AuralAttributes: no capability to get Doppler scale factor
+AuralAttributes19=AuralAttributes: no capability to set Doppler velocity
+AuralAttributes20=AuralAttributes: no capability to get Doppler velocity
+AuralAttributes21=AuralAttributes: no capability to get reflection coefficient
+AuralAttributes22=AuralAttributes: no capability to set reflection delay
+AuralAttributes23=AuralAttributes: no capability to get reflection delay
+AuralAttributes24=AuralAttributes: no capability to set reverberation coefficient
+AuralAttributes25=AuralAttributes: no capability to get reverberation coefficient
+AuralAttributes26=AuralAttributes: no capability to set reverberation bounds
+AuralAttributes27=AuralAttributes: no capability to get reverberation bounds
+AuralAttributes28=AuralAttributes: no capability to set decay time
+AuralAttributes29=AuralAttributes: no capability to get decay time
+AuralAttributes30=AuralAttributes: no capability to set decay filter
+AuralAttributes31=AuralAttributes: no capability to get decay filter
+AuralAttributes32=AuralAttributes: no capability to set diffusion
+AuralAttributes33=AuralAttributes: no capability to get diffusion
+AuralAttributes34=AuralAttributes: no capability to set density
+AuralAttributes35=AuralAttributes: no capability to get density
+Behavior0=wakeupOn must be called from initialize or processStimulus
+Behavior1=illegal schedulingInterval value
+BehaviorRetained0=Behavior: illegal node under Background geometry Branch
+BehaviorRetained1=Behavior: illegal node under SharedGroup Branch
+BehaviorRetained2=Behavior: wakeupCondition criteria cannot be null
+ConeSound0=ConeSound: no capability to set distance attenuation
+ConeSound2=ConeSound: no capability to get distance attenuation
+ConeSound3=ConeSound: no capability to set direction
+ConeSound5=ConeSound: no capability to get direction
+ConeSound6=ConeSound: no capability to set cone attenuation
+ConeSound9=ConeSound: no capability to get cone attenuation
+ConeSound10=ConeSound: no capability to get max distance attenuation
+BoundingBox0=BoundingBox( Bounds) unrecognized bounds type
+BoundingBox1=BoundingBox( Bounds[]) unrecognized bounds type
+BoundingBox3=BoundingBox.combine( Bounds) unrecognized bounds type
+BoundingBox4=BoundingBox.combine( Bounds[]) unrecognized bounds type
+BoundingBox5=transform( Bounds, trans) unrecognized bounds type
+BoundingBox6=intersect(Bounds[]) unrecognized bounds type
+BoundingBox7=BoundingBox.intersect(Bounds, newBoundingBox) unrecognized bounds type
+BoundingBox9=box.closestIntersection(Bounds[]) unrecognized bounds type
+BoundingLeaf0=BoundingLeaf: no capability to write bounding region
+BoundingLeaf1=BoundingLeaf: no capability to read bounding region
+BoundingLeafRetained0=BoundingLeaf: illegal node under Background geometry Branch
+BoundingLeafRetained1=BoundingLeaf: illegal node under SharedGroup Branch
+BackgroundRetained0=Background: Background geometry BranchGroup cannot be referenced by multiple Background node
+BackgroundRetained1=Background: Immediate mode background may not be in scene graph
+BackgroundRetained3=Background: Background geometry BranchGroup is not at the root of a branch graph
+BackgroundRetained4=Background: Background geometry BranchGroup cannot be attached to a locale
+BackgroundRetained5=Background: illegal node under Background geometry Branch
+BackgroundRetained6=Background: illegal node under SharedGroup Branch
+Canvas3D0=Canvas3D: Cannot swap buffers when the renderer is running
+Canvas3D1=Canvas3D: Not in off-screen mode
+Canvas3D2=Canvas3D: Off-screening rendering is in progress
+Canvas3D3=Canvas3D: The specified ImageComponent2D is used by more than on Canvas3D
+Canvas3D7=*** ERROR: Canvas3D constructed with a null GraphicsConfiguration
+Canvas3D8=Canvas3D: The width of the associated Screen3D's size is <= 0
+Canvas3D9=Canvas3D: The height of the associated Screen3D's size is <= 0
+Canvas3D10=Canvas3D: Off-screen buffer is null
+Canvas3D11=Canvas3D: Java3D renderer is stopped
+Canvas3D12=Canvas3D: The physical width of the associated Screen3D is <= 0
+Canvas3D13=Canvas3D: The physical height of the associated Screen3D is <= 0
+Canvas3D14=Canvas3D: Illegal operation in off-screen mode
+Canvas3D15=Canvas3D: For offscreen rendering, byReference image should be an instance of BufferedImage
+Canvas3D16=Canvas3D: Offscreen rendering does not support FORMAT_CHANNEL8
+Canvas3D17=Canvas3D: GraphicsConfiguration is not compatible with Canvas3D
+Canvas3D18=*** This will cause a NullPointerException in a subsequent release
+Canvas3D19=Canvas3D: null GraphicsConfiguration
+Canvas3D20=Canvas3D does not support serialization
+Canvas3D21=*** ERROR: GraphicsConfiguration not created with GraphicsConfigTemplate3D
+Canvas3D22=*** This will cause an IllegalArgumentException in a subsequent release
+BoundingPolytope0=BoundingPolytope( Bounds) unrecognized bounds object
+BoundingPolytope1=BoundingPolytope( Bounds) unrecognized bounds type
+BoundingPolytope2=set( Bounds) unrecognized bounds type
+BoundingPolytope3=combine( Bounds) unrecognized bounds type
+BoundingPolytope4=BoundingPolytope.combine( Bounds ) unrecognized bounds type
+BoundingPolytope5=BoundingPolytope.transform( Bounds, transform ) unrecognized bounds type
+BoundingPolytope6=intersect(Bounds[]) unrecognized bounds type
+BoundingPolytope7=BoundingPolytope.intersect(Bounds[]) unrecognized bounds type
+BoundingPolytope8=intersect(Bounds, BoundingPolytope) bounds type not recognized=
+BoundingPolytope10=sphere.closestIntersection(Bounds[]) unrecognized bounds object
+BoundingPolytope11=Must specify at least 4 planes
+BranchGroup0=Cannot compile a live BranchGroup
+BranchGroup1=BranchGroup: no capability to detach
+BranchGroup2=Group: no capability to write children
+BranchGroup3=Picking can only work if BranchGroup is alive
+CachedFrustum0=Frustum must have aleast 6 planes
+CachedFrustum1=Frustum must have 6 planes
+Clip0=Clip: no capability to set back distance
+Clip1=Clip: no capability to get back distance
+Clip2=Clip: no capability to set application bounds
+Clip3=Clip: no capability to get application bounds
+ColoringAttributes0=ColoringAttributes: no capability to set color
+ColoringAttributes2=ColoringAttributes: no capability to get Color
+ColoringAttributes3=ColoringAttributes: no capability to set shademodel
+ColoringAttributes4=ColoringAttributes: no capability to get shademodel
+CompressedGeometry0=CompressedGeometry: start+size exceeds geometry length
+CompressedGeometry1=CompressedGeometry: no capability to get byte count
+CompressedGeometry2=CompressedGeometry: no capability to get geometry header
+CompressedGeometry3=CompressedGeometry: no capability to get geometry
+CompressedGeometry4=CompressedGeometry: target buffer is too small
+CompressedGeometry5=CompressedGeometry: no capability to get geometry
+CompressedGeometry6=CompressedGeometry: no capability to get data reference
+CompressedGeometry7=CompressedGeometry: cannot directly access data in byReference mode
+CompressedGeometry8=CompressedGeometry: must be in byReference mode to use this method
+CompressedGeometry9=CompressedGeometry: NIO buffer support is not currently implemented
+ClipRetained0=Clip: Immediate mode clip may not be in scene graph
+ClipRetained1=Clip: illegal node under Background geometry Branch
+ClipRetained2=Clip: illegal node under SharedGroup Branch
+DepthComponentInt0=DepthComponentInt: no capability to get data
+DepthComponent0=DepthComponent: no capability to get size
+ImageComponentRetained0=ImageComponent: illegal width value
+ImageComponentRetained1=ImageComponent: illegal height value
+ImageComponentRetained2=ImageComponent: illegal depth value
+ImageComponentRetained3=ImageComponent: illegal format value
+ExponentialFog0=ExponentialFog: no capability to write density
+ExponentialFog1=ExponentialFog: no capability to read density
+Fog0=Fog: no capability to write color
+Fog2=Fog: no capability to read color
+Fog3=Fog: no capability to write influencing bounds
+Fog4=Fog: no capability to read influencing bounds
+Fog7=Fog: no capability to write fog's scope
+Fog8=Fog: no capability to read fog's scope
+Fog9=Fog: no capability to insert scope
+Fog10=Fog: no capability to remove scope
+Fog11=Fog: no capability to read scopes
+Fog12=Fog: no capability to append scope
+DirectionalLight0=Light: no capability to set light's state
+DirectionalLight1=Light: no capability to set light's direction
+DirectionalLight2=Light: no capability to read light's direction
+FogRetained0=Fog: Immediate mode fog may not be in scene graph
+FogRetained1=Fog: illegal node under SharedGroup Branch
+DepthComponentFloat0=DepthComponentFloat: no capability to get data
+FontExtrusion0=FontExtrusion:invalid shape- non-monotonic
+FontExtrusion1=FontExtrusion: invalid shape- shape must start or end at x = 0.0f
+FontExtrusion2=FontExtrusion:method not implemented
+FontExtrusion3=FontExtrusion:invalid shape- multiple contours
+Group0=Group: no capability to set bounds
+Group1=Group: no capability to read user bounds
+Group2=SharedGroup must be referenced through a link node
+Group3=Group: only BranchGroup nodes may be set
+Group4=Group: no capability to detach BranchGroup
+Group6=Group: only a BranchGroup node may be inserted
+Group7=Group: only a BranchGroup node may be removed
+Group9=Group: no capability to read children
+Group12=Group: only a BranchGroup node may be added
+Group13=Group: no capability to set children
+Group14=Group: no capability to insert children
+Group15=Group: no capability to remove children
+Group16=Group: no capability to append children
+GeneralizedStrip0=GeneralizedStrip: strip ended incompletely
+GeometryArray0=GeometryArray: vertexFormat must include COORDINATES
+GeometryArray1=GeometryArray: no capability to get vertex count
+GeometryArray2=GeometryArray: no capability to get vertex format
+GeometryArray3=GeometryArray: no capability to set coordinate
+GeometryArray7=GeometryArray: no capability to set coordinates
+GeometryArray15=GeometryArray: no capability to set color
+GeometryArray21=GeometryArray: no capability to set colors
+GeometryArray33=GeometryArray: no capability to set normal
+GeometryArray35=GeometryArray: no capability to set normals
+GeometryArray39=GeometryArray: no capability to set texture coordinate
+GeometryArray42=GeometryArray: no capability to set texture coordinates
+GeometryArray48=GeometryArray: no capability to read coordinate
+GeometryArray52=GeometryArray: no capability to read coordinates
+GeometryArray56=GeometryArray: no capability to read color
+GeometryArray62=GeometryArray: no capability to read colors
+GeometryArray68=GeometryArray: no capability to read normal
+GeometryArray70=GeometryArray: no capability to read normals
+GeometryArray72=GeometryArray: no capability to read texture coordinate
+GeometryArray75=GeometryArray: no capability to read texture coordinates
+GeometryArray76=GeometryArray: has no colors
+GeometryArray77=GeometryArray: has no normals
+GeometryArray78=GeometryArray: has no normals
+GeometryArray79=GeometryArray: has no texture coordinates
+GeometryArray80=GeometryArray: INTERLEAVED flag set without setting BY_REFERENCE flag
+GeometryArray81=GeometryArray: no capability to update geometry data
+GeometryArray82=GeometryArray: cannot directly access data in BY_REFERENCE mode
+GeometryArray83=GeometryArray: must be in BY_REFERENCE mode to use this method
+GeometryArray84=GeometryArray: cannot access individual array references in INTERLEAVED mode
+GeometryArray85=GeometryArray: must be in INTERLEAVED mode to use this method
+GeometryArray86=GeometryArray: no capability to write data reference
+GeometryArray87=GeometryArray: no capability to read data reference
+GeometryArray88=GeometryArray: no capability to set valid vertex count
+GeometryArray89=GeometryArray: no capability to get valid vertex count
+GeometryArray90=GeometryArray: no capability to set initial index
+GeometryArray91=GeometryArray: no capability to get initial index
+GeometryArray92=GeometryArray: must be in COLOR_3 mode to use this method
+GeometryArray93=GeometryArray: must be in COLOR_4 mode to use this method
+GeometryArray94=GeometryArray: must be in TEXTURE_COORDINATE_2 mode to use this method
+GeometryArray95=GeometryArray: must be in TEXTURE_COORDINATE_3 mode to use this method
+GeometryArray96=GeometryArray: vertex count < 0
+GeometryArray97=GeometryArray: initial index < 0
+GeometryArray98=GeometryArray: array reference is already non-null
+GeometryArray99=GeometryArray: vertex array length is incorrect
+GeometryArray100=GeometryArray: initial vertex index + valid vertex count > vertex count
+GeometryArray101=GeometryArray: initial color index + valid vertex count > vertex count
+GeometryArray102=GeometryArray: initial normal index + valid vertex count > vertex count
+GeometryArray103=GeometryArray: initial tex coord index + valid vertex count > vertex count
+GeometryArray104=GeometryArray: initial coord index + valid vertex count > vertex count
+GeometryArray105=GeometryArray: must not be in BY_REFERENCE mode to use this method
+GeometryArray106=GeometryArray: texCoord set mapping is not specified
+GeometryArray107=GeometryArray: must specify at least one set of tex coord
+GeometryArray108=GeometryArray: invalid texCoord set mapping
+GeometryArray109=GeometryArray: must be in TEXTURE_COORDINATE_4 mode to use this method
+GeometryArray110=GeometryArray: validVertexCount should be greater than or equal to zero
+GeometryArray111=GeometryArray: normal array length is incorrect
+GeometryArray112=GeometryArray: color array length is incorrect
+GeometryArray113=GeometryArray: texture coord array length is incorrect
+GeometryArray114=GeometryArray: interleaved array length is incorrect
+GeometryArray115=GeometryArray: NIO buffer is null
+GeometryArray116=GeometryArray: Illegal NIO buffer type
+GeometryArray117=GeometryArray: USE_NIO_BUFFER flag set without setting BY_REFERENCE flag
+GeometryArray118=GeometryArray: must be in USE_NIO_BUFFER mode to use this method
+GeometryArray119=GeometryArray: must not be in USE_NIO_BUFFER mode to use this method
+GeometryArray120=GeometryArray: must be direct nio buffer
+GeometryArray121=GeometryArray: None of the TEXTURE_COORDINATE bits are set in vertexFormat
+GeometryArray122=GeometryArray: NORMALS bit is not set in vertexFormat
+GeometryArray123=GeometryArray: None of the COLOR bits are set in vertexFormat
+GeometryDecompressor0=GeometryDecompressor: start+length > data array size
+GeometryDecompressor1=GeometryDecompressor: bad delta normal in compressed buffer
+GeometryDecompressorRetained0=GeometryDecompressorRetained: bad buffer data type
+GeometryDecompressorRetained1=GeometryDecompressorRetained: unexpected vertexFormat/SetState in compressed buffer
+GeometryDecompressorRetained2=GeometryDecompressorRetained: unexpected color in compressed buffer
+GeometryDecompressorRetained3=GeometryDecompressorRetained: unexpected normal in compressed buffer
+GeometryDecompressorRetained4=GeometryDecompressorRetained: bad buffer data type
+GeometryDecompressorShape3D0=GeometryDecompressorShape3D: bad triangle output type
+GeometryDecompressorShape3D1=GeometryDecompressorShape3D: bad buffer data type
+GroupRetained0=Group.setChild: child already has a parent
+GroupRetained1=Group.insertChild: child already has a parent
+GroupRetained2=Group.addChild: child already has a parent
+GeometryRetained1=Geometry - intersect : Sorry! This method is not supported at present
+Light0=Light: no capability to set light's state
+Light1=Light: no capability to read light's state
+Light2=Light: no capability to write light's color
+Light3=Light: no capability to read light's color
+Light4=Light: no capability to write light's scope
+Light5=Light: no capability to read light's scope
+Light6=Light: no capability to insert scope
+Light7=Light: no capability to remove scope
+Light8=Light: no capability to read scopes
+Light9=Light: no capability to append scope
+Light11=Light: no capability to write influencing bounds
+Light12=Light: no capability to read influencing bounds
+GeometryStripArray0=GeometryStripArray: no capability to get number of strips
+GeometryStripArray1=GeometryStripArray: no capability to get strip vertex counts
+GeometryStripArray2=GeometryStripArray: no capability to set strip vertex counts
+GeometryStripArray3=GeometryStripArray: initial vertex index + valid vertex count > vertex count
+GeometryStripArray4=GeometryStripArray: initial color index + valid vertex count > vertex count
+GeometryStripArray5=GeometryStripArray: initial normal index + valid vertex count > vertex count
+GeometryStripArray6=GeometryStripArray: initial tex coord index + valid vertex count > vertex count
+GeometryStripArray7=GeometryStripArray: initial coord index + valid vertex count > vertex count
+GraphicsContext3D11=Background: Scene Graph background may not be in immediate mode
+GraphicsContext3D12=Fog: Scene Graph fog may not be in immediate mode
+GraphicsContext3D13=GraphicsContext3D: Light object is null
+GraphicsContext3D14=Light: Scene Graph light may not be in immediate mode
+GraphicsContext3D17=GraphicsContext3D: setSound object is null
+GraphicsContext3D21=readRaster: Scene Graph Raster may not be in immediate mode
+GraphicsContext3D22=Background: Background geometry can not be used in immediate mode context
+GraphicsContext3D23=Sound: Scene Graph sound may not be in immediate mode
+GraphicsContext3D25=ModelClip: Scene Graph ModelClip may not be in immediate mode
+GraphicsContext3D26=Shape3D: Scene Graph Shape3D may not be in immediate mode
+GraphicsContext3D27=ImageComponent2D size is smaller than read Raster size
+GraphicsContext3D28=DepthComponent size is smaller than read Raster size
+ImageComponent0=ImageComponent: no capability to get width
+ImageComponent1=ImageComponent: no capability to get height
+ImageComponent2=ImageComponent: no capability to get format
+GeometryStripArrayRetained0=Illegal stripVertexCounts
+ImageComponent2D0=ImageComponent2D: no capability to get image
+ImageComponent2D1=ImageComponent2D: no capability to set image
+ImageComponent2D2=ImageComponent2D: must be in BY_REFERENCE mode to use this method
+ImageComponent2D3=ImageComponent2D: illegal dimension
+ImageComponent2D4=ImageComponent2D: must be in BY_COPY mode to use this method
+ImageComponent2D5=ImageComponent2D: image is not an instanceof of BufferedImage
+ImageComponent3D0=ImageComponent3D: no capability to get depth
+ImageComponent3D1=ImageComponent3D - incompatible depth
+ImageComponent3D2=ImageComponent3D - incompatible width
+ImageComponent3D3=ImageComponent3D: no capability to get image
+ImageComponent3D4=ImageComponent3D - incompatible height
+ImageComponent3D5=ImageComponent3D: no capability to set image
+ImageComponent3D6=ImageComponent3D: must be in BY_REFERENCE mode to use this method
+ImageComponent3D7=ImageComponent3D: illegal dimension
+ImageComponent3D8=ImageComponent3D: must be in BY_COPY mode to use this method
+ImageComponent3D9=ImageComponent3D: image is not an instanceof of BufferedImage
+ImageComponent2DRetained0=ImageComponent2D - incompatible width
+ImageComponent2DRetained1=ImageComponent2D - incompatible height
+ImageComponent2DRetained2=Raster does not support FORMAT_CHANNEL8
+ImageComponent3DRetained0=ImageComponent3D: image is not an instanceof of BufferedImage
+Locale0=Locale.addBranchGraph: Branch Group already has a parent
+Locale1=Locale: no capability to detach BranchGroup
+Locale3=Locale.replaceBranchGraph: Branch Group already has a parent
+Locale4=Locale has been removed from its VirtualUniverse
+IndexedLineStripArray0=IndexedLineStripArray: illegal vertexCount
+IndexedLineStripArray1=IndexedLineStripArray: illegal indexCount
+IndexedGeometryArray0=IndexedGeometryArray: no capability to get index count
+IndexedGeometryArray1=IndexedGeometryArray: no capability to set coordinate index
+IndexedGeometryArray3=IndexedGeometryArray: no capability to set color index
+IndexedGeometryArray5=IndexedGeometryArray: no capability to set normal index
+IndexedGeometryArray7=IndexedGeometryArray: no capability to set texture coordinate index
+IndexedGeometryArray9=IndexedGeometryArray: no capability to get coordinate index
+IndexedGeometryArray11=IndexedGeometryArray: no capability to get color index
+IndexedGeometryArray13=IndexedGeometryArray: no capability to get normal index
+IndexedGeometryArray15=IndexedGeometryArray: no capability to get texture coordinate index
+IndexedGeometryArray16=IndexedGeometryArray: no capability to set valid index count
+IndexedGeometryArray17=IndexedGeometryArray: no capability to get valid index count
+IndexedGeometryArray18=IndexedGeometryArray: no capability to set initial index index
+IndexedGeometryArray19=IndexedGeometryArray: no capability to get initial index index
+IndexedGeometryArray20=IndexedGeometryArray: initial index < 0
+IndexedGeometryArray21=IndexedGeometryArray: valid index < 0
+IndexedGeometryArray22=IndexedGeometryArray: initial index Index +valid index count > index count
+IndexedGeometryArray23=IndexedGeometryArray: index coord value greater than the array length
+IndexedGeometryArray24=IndexedGeometryArray: index color value greater than the array length
+IndexedGeometryArray25=IndexedGeometryArray: index texcoord value greater than the array length
+IndexedGeometryArray26=IndexedGeometryArray: index normal value greater than the array length
+IndexedGeometryArray27=IndexedGeometryArray: index value less than zero
+IndexedLineArray0=IndexedLineArray: illegal vertexCount
+IndexedLineArray1=IndexedLineArray: illegal indexCount
+IndexedGeometryArrayRetained0=execute() called on indexed geometry
+IndexedGeometryStripArray0=IndexedGeometryStripArray: no capability to get number of strips
+IndexedGeometryStripArray1=IndexedGeometryStripArray: no capability to get strip index counts
+IndexedGeometryStripArray2=IndexedGeometryStripArray: no capability to set strip index counts
+LineAttributes0=LineAttributes: illegal line pattern
+LineAttributes1=LineAttributes: no capability to set line width
+LineAttributes2=LineAttributes: no capability to get line width
+LineAttributes3=LineAttributes: no capability to set line pattern
+LineAttributes4=setLinePattern: illegal line pattern
+LineAttributes5=LineAttributes: no capability to get line pattern
+LineAttributes6=LineAttributes: no capability to set line antialiasing
+LineAttributes7=LineAttributes: no capability to get line antialiasing
+LineAttributes8=LineAttributes: no capability to set line pattern mask
+LineAttributes9=LineAttributes: no capability to get line pattern mask
+LineAttributes10=LineAttributes: no capability to set line pattern scale factor
+LineAttributes11=LineAttributes: no capability to get line pattern scale factor
+LineArray0=LineArray: illegal vertexCount
+IndexedGeometryStripArrayRetained0=Illegal stripIndexCounts
+IndexedLineArrayRetained0=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.
+IndexedLineStripArrayRetained0=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.
+IndexedLineStripArrayRetained1=IndexedLineStripArray: stripVertexCounts element less than 2
+IndexedPointArray0=IndexedPointArray: illegal vertexCount
+IndexedPointArray1=IndexedPointArray: illegal indexCount
+IndexedPointArrayRetained0=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.
+IndexedQuadArray0=IndexedQuadArray: illegal vertexCount
+IndexedQuadArray1=IndexedQuadArray: illegal indexCount
+IndexedQuadArrayRetained0=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.
+IndexedTriangleArray0=IndexedTriangleArray: illegal vertexCount
+IndexedTriangleArray1=IndexedTriangleArray: illegal indexCount
+IndexedTriangleArrayRetained0=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.
+IndexedTriangleFanArray0=IndexedTriangleFanArray: illegal vertexCount
+IndexedTriangleFanArray1=IndexedTriangleFanArray: illegal indexCount
+IndexedTriangleFanArrayRetained0=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.
+IndexedTriangleFanArrayRetained1=IndexedTriangleFanArray: stripVertexCounts element less than 3
+IndexedTriangleStripArray0=IndexedTriangleStripArray: illegal vertexCount
+IndexedTriangleStripArray1=IndexedTriangleStripArray: illegal indexCount
+IndexedTriangleStripArrayRetained0=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.
+IndexedTriangleStripArrayRetained1=IndexedTriangleStripArray: stripVertexCounts element less than 3
+LightRetained0=Light: Immediate mode light may not be in scene graph
+LineStripArrayRetained0=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.
+LineStripArrayRetained1=stripVertexCounts element less than 2
+LineArrayRetained0=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.
+LineStripArray0=LineStripArray: illegal vertexCount
+Link0=Link: no capability to set SharedGroup
+Link1=Link: no capability to get SharedGroup
+LinkRetained0=Link: illegal node under Background geometry Branch
+LinkRetained1=Link: Scene Graph are not directed acyclic graphs
+LinearFog0=LinearFog: no capability to write distance
+LinearFog1=LinearFog: no capability to read distance
+PointArray0=PointArray: illegal vertexCount
+Material0=Material: no capability to set component
+Material2=Material: no capability to get component
+Material3=Material: no capability to set color target
+Material4=Material: no capability to get color target
+Material15=Material: no capability to set lighting
+Material16=Material: no capability to get lighting
+Morph0=Group: no capability to set bounds
+Morph1=Group: no capability to read user bounds
+Morph2=Morph: no capability to set geometryArrays
+Morph3=Morph: no capability to get geometryArrays
+Morph4=Morph: no capability to set appearance
+Morph5=Morph: no capability to get appearance
+Morph6=Morph: no capability to allow intersect
+Morph8=Morph: no capability to set morph weight vector
+Morph9=Morph: no capability to get morph weight vector
+Morph10=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.
+Morph11=Morph: no capability to set appearance override enable
+Morph12=Morph: no capability to get appearance override enable
+MediaContainer0=MediaContainer: setURL - bad URL
+MediaContainer1=MediaContainer: no capability to set cached flag
+MediaContainer2=MediaContainer: no capability to get cached flag
+MediaContainer3=MediaContainer: no capability to set URL
+MediaContainer4=MediaContainer: no capability to get URL
+MediaContainer5=MediaContainer: only one type of sound data may be simultaneously set non-null
+MorphRetained0=Morph: Incorrect number of GeometryArrays
+MorphRetained1=Morph: All GeometryArrays must have same vertexFormat, same vertexCount and same texCoordSetCount
+MorphRetained2=Morph: All GeometryArrays must be of same type
+MorphRetained5=Invalid SceneGraphPath encountered : localToVworld is null.
+MorphRetained7=Morph: number of weights not same as number of GeometryArrays
+MorphRetained8=Morph: sum of all weights is NOT 1.0
+Node0=Cannot get the parent of a live or compiled node
+Node1=Node: no capability to set bounds
+Node2=Node: no capability to read user bounds
+Node3=Node: no capability to read Pickable
+Node4=Node: no capability to set Collidable
+Node5=Node: no capability to set user auto compute bounds
+Node6=Node: no capability to read user auto compute bounds
+Node7=Node: local to vworld transform is undefined for a node that is not part of a live scene graph
+Node8=Node: no capability to read local to vworld transform
+Node9=Node: Invalid geometric bounds
+Node11=cloneTree: should be overridden in child
+Node12=cloneNode must be defined in subclass
+Node13=Node: Cannot clone a live or compiled scenegraph
+Node14=Node: no capability to set Pickable
+Node15=Node: Cannot compile, clone or getBounds on a scene graph that contains a cycle.
+Node16=Node: no capability to read Collidable
+Picking0=Cannot call picking under a SharedGroup node
+Picking2=Picking: Node has no parent and locale. This is illegal!
+NodeComponent0=NodeComponent:cloneNodeComponent must be defined in subclass
+NodeComponent1=Cannot duplicate a Compiled NodeComponent object
+NodeRetained0=Not supported in a Shared Graph
+NodeRetained1=Only supported in a Shared Graph
+NodeRetained2=invalid scene graph path
+NodeRetained3=No node object may exist in more than one virtual universe
+NodeRetained4=SharedGroup has no parent. This is illegal!
+NodeRetained5=Node has no parent and locale. This is illegal!
+OrderedGroup0=OrderedGroup: childIndexOrder.length != number of children
+OrderedGroup1=OrderedGroup: childIndexOrder[i] must be >= 0, for i in [0, numChildren-1]
+OrderedGroup2=OrderedGroup: childIndexOrder[i] must be < numChildren, for i in [0, numChildren-1]
+OrderedGroup3=OrderedGroup: childIndexOrder[i] must not equal to childIndexOrder[j], for i,j in [0,numChildren-1] and i != j
+OrderedGroup4=OrderedGroup: no capability to write child index order
+OrderedGroup5=OrderedGroup: no capability to read child index order
+OrderedGroup6=OrderedGroup: insertChild illegal when childIndexOrder != null
+OrientedShape3D0=OrientedShape3D: no capability to set alignment mode
+OrientedShape3D1=OrientedShape3D: no capability to get alignment mode
+OrientedShape3D2=OrientedShape3D: no capability to set alignment axis
+OrientedShape3D3=OrientedShape3D: no capability to get alignment axis
+OrientedShape3D4=OrientedShape3D: no capability to set rotation point
+OrientedShape3D5=OrientedShape3D: no capability to get rotation point
+OrientedShape3D6=OrientedShape3D: no capability to set constant scale enable
+OrientedShape3D7=OrientedShape3D: no capability to get constant scale enable
+OrientedShape3D8=OrientedShape3D: no capability to set scale
+OrientedShape3D9=OrientedShape3D: no capability to get scale
+PathInterpolator0=PathInterpolator: first knot is not 0.0
+PathInterpolator1=PathInterpolator: last knot is not 1.0
+PathInterpolator2=PathInterpolator: invalid knot value
+PhysicalBody0=non-rigid transform
+PointLight0=PointLight: no capability to set light's state
+PointLight1=PointLight: no capability to set light's position
+PointLight2=PointLight: no capability to read light's position
+PointLight3=PointLight: no capability to set light's attenuation
+PointLight5=PointLight: no capability to read light's attenuation
+PointSound0=PointSound: no capability to set position
+PointSound2=PointSound: no capability to get position
+PointSound3=PointSound: no capability to set distance attenuation
+PointSound4=PointSound: no capability to get max distance attenuation
+RotPosPathInterpolator0=RotPosPathInterpolator: length of knots, positions, and quats must be equal
+PhysicalEnvironment0=addInputDevice: InputDevice.getProcessingMode must return one of BLOCKING, NON_BLOCKING, or DEMAND_DRIVEN
+PhysicalEnvironment1=non-rigid transform
+PhysicalEnvironment2=Illegal policy value
+Raster0=Raster: no capability to set position
+Raster1=Raster: no capability to get position
+Raster2=Raster: no capability to get type
+Raster3=Raster: no capability to set image
+Raster4=Raster: no capability to get image
+Raster5=Raster: no capability to set depth component
+Raster6=Raster: no capability to get depth component
+Raster7=Raster: no capability to set offset
+Raster8=Raster: no capability to get offset
+Raster9=Raster: no capability to set size
+Raster10=Raster: no capability to set clip mode
+Raster11=Raster: no capability to get clip mode
+PointArrayRetained0=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.
+PointAttributes0=PointAttributes: no capability to set point size
+PointAttributes1=PointAttributes: no capability to get point size
+PointAttributes2=PointAttributes: no capability to set point antialiasing
+PointAttributes3=PointAttributes: no capability to get point antialiasing
+PolygonAttributes0=PolygonAttributes: illegal polygon mode
+PolygonAttributes2=PolygonAttributes: no capability to set polygon cull face
+PolygonAttributes3=setCullFace: illegal cull face
+PolygonAttributes4=PolygonAttributes: no capability to get polygon cull face
+PolygonAttributes5=PolygonAttributes: no capability to set back face normal flip flag
+PolygonAttributes6=PolygonAttributes: no capability to get back face normal flip flag
+PolygonAttributes7=PolygonAttributes: no capability to set polygon mode
+PolygonAttributes8=setPolygonMode: illegal polygon mode
+PolygonAttributes9=PolygonAttributes: no capability to get polygon mode
+PolygonAttributes10=PolygonAttributes: no capability to set polygon offset
+PolygonAttributes11=PolygonAttributes: no capability to get polygon offset
+PolygonAttributes12=PolygonAttributes: illegal cull face
+QuadArray0=QuadArray: illegal vertexCount
+QuadArrayRetained0=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.
+PositionPathInterpolator0=PositionPathInterpolator: length of knots and positions must be equal
+RenderingAttributes0=RenderingAttributes: no capability to set depth buffer mode
+RenderingAttributes1=RenderingAttributes: no capability to get depth buffer mode
+RenderingAttributes2=RenderingAttributes: no capability to set depth buffer write mode
+RenderingAttributes3=RenderingAttributes: no capability to get depth buffer write mode
+RenderingAttributes4=RenderingAttributes: no capability to set alpha test value
+RenderingAttributes5=RenderingAttributes: no capability to get alpha test value
+RenderingAttributes6=RenderingAttributes: no capability to set alpha test function
+RenderingAttributes7=RenderingAttributes: no capability to get alpha test function
+RenderingAttributes8=RenderingAttributes: no capability to set visibility
+RenderingAttributes9=RenderingAttributes: no capability to get visibility
+RenderingAttributes10=RenderingAttributes: no capability to set raster op
+RenderingAttributes11=RenderingAttributes: no capability to get raster op
+RenderingAttributes12=RenderingAttributes: no capability to set ignore vertex colors flag
+RenderingAttributes13=RenderingAttributes: no capability to get ignore vertex colors flag
+TriangleStripArrayRetained0=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.
+TriangleStripArrayRetained1=stripVertexCounts element less than 3
+RotationPathInterpolator0=RotationPathInterpolator: length of knots and quats must be of the same length
+RotPosScalePathInterpolator0=RotPosScalePathInterpolator: length of knots and positions must be of the same length
+RotPosScalePathInterpolator1=PositionPathInterpolator: length of knots and quats must be equal
+RotPosScalePathInterpolator2=PositionPathInterpolator: length of knots and scales must be equal
+SceneGraphObject0=Cannot modify capability bits on a live or compiled object
+SceneGraphObject1=Cannot modify capability isFrequent bits on a compiled object
+SceneGraphObject2=Object is either live or compiled
+SceneGraphObjectRetained0=CloneNotSupportedException
+SceneGraphPath0=SceneGraphPath : Node array pointer is null.
+SceneGraphPath1=SceneGraphPath : Node array bounds exceeded.
+SceneGraphPath2=Invalid SceneGraphPath: a Locale is not specified.
+SceneGraphPath3=Invalid SceneGraphPath: A member is not live.
+SceneGraphPath5=Invalid SceneGraphPath: A Link node has been excluded.
+SceneGraphPath9=Invalid SceneGraphPath: Locale and path are not associated.
+SceneGraphPath10=Invalid SceneGraphPath: a Node is not specified.
+SceneGraphPath11=Invalid SceneGraphPath: Not all nodes are on the same path or there is an ordering problem.
+Screen3D0=Screen3D: non-rigid transform
+Screen3D1=Screen3D: Cannot set screen size, screen is not in off-screen mode
+Sensor0=Sensor.setPredictor: Must use PREDICT_NONE or PREDICT_NEXT_FRAME_TIME
+Sensor1=Sensor.setPredictionPolicy: Illegal policy
+Sensor2=getRead(read, deltaT) must have value >= 0 for deltaT
+Sensor3=Sensor.lastRead(transform,kth); kth can't be bigger than the sensor read count
+Sensor4=Sensor.lastTime(k); k can't be bigger than the sensor read count
+Sensor5=Sensor.lastButtons(k, values); k can't be bigger than the sensor read count
+Sensor6=Sensor.lastButtons(k); k can't be bigger than the sensor read count
+SensorRead0=SensorRead: Array of button values is not long enough
+SensorRead1=SensorRead: Trying to set button values when this SensorRead object has no buttons
+Shape3D0=Group: no capability to set bounds
+Shape3D1=Group: no capability to read user bounds
+Shape3D2=Shape3D: no capability to set geometry
+Shape3D3=Shape3D: no capability to get geometry
+Shape3D4=Shape3D: no capability to set appearance
+Shape3D5=Shape3D: no capability to get appearance
+Shape3D6=Shape3D: no capability to allow intersect
+Shape3D7=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.
+Shape3D8=Shape3D: no capability to set appearance override enable
+Shape3D9=Shape3D: no capability to get appearance override enable
+Sound0=Sound: no capability to set sound data
+Sound1=Sound: no capability to get sound data
+Sound2=Sound: no capability to set initial gain
+Sound3=Sound: no capability to get initial gain
+Sound4=Sound: no capability to set loop
+Sound5=Sound: no capability to get loop
+Sound6=Sound: no capability to set release flag
+Sound7=Sound: no capability to get release flag
+Sound8=Sound: no capability to set continuous play flag
+Sound9=Sound: no capability to get continuous play flag
+Sound10=Sound: no capability set sound state
+Sound11=Sound: no capability to set scheduling bounds
+Sound12=Sound: no capability to get scheduling bounds
+Sound15=Sound: no capability set sound priority
+Sound16=Sound: no capability to get sound on priority
+Sound17=Sound: no capability to get duration
+Sound18=Sound: no capability to get playing state
+Sound20=Sound: no capability to get to get channels used for sound
+Sound21=Sound: no capability to get sound on flag
+Sound22=Sound: no capability to get ready state
+Sound23=Sound: no capability to set mute flag
+Sound24=Sound: no capability to get mute flag
+Sound25=Sound: no capability to set pause flag
+Sound26=Sound: no capability to get pause flag
+Sound27=Sound: no capability to set rate scale factor
+Sound28=Sound: no capability to get rate scale factor
+SoundRetained1=Sound source data could not be loaded
+SoundRetained2=Sound: Immediate mode sound may not be in scene graph
+SoundRetained3=Sound: illegal node under Background geometry Branch
+Switch0=Switch: no capability to set children
+Switch1=Switch: no capability to read switch index
+Switch2=Switch: no capability to set childMask
+Switch3=Switch: no capability to read switch childMask
+Switch4=Switch: no capability to read children
+Text3D0=Text3D: no capability to get Font3D
+Text3D1=Text3D: no capability to set Font3D
+Text3D2=Text3D: no capability to get string
+Text3D3=Text3D: no capability to set string
+Text3D4=Text3D: no capability to get position
+Text3D5=Text3D: no capability to set position
+Text3D6=Text3D: no capability to get allignment
+Text3D7=Text3D: no capability to set allignment
+Text3D8=Text3D: no capability to get path
+Text3D9=Text3D: no capability to set path
+Text3D10=Text3D: no capability to get bounding box
+Text3D11=Text3D: no capability to get character spacing
+Text3D12=Text3D: no capability to set character spacing
+Shape3DRetained3=Invalid SceneGraphPath encountered : localToVworld is null.
+Shape3DRetained5=Shape3D: the new geometry component is not of the same equivalence class as the existing geometry components.
+SharedGroup0=Cannot compile a live SharedGroup
+SharedGroup1=SharedGroup: No capability to get Links
+Soundscape0=Soundscape: no capability to set application bounds
+Soundscape1=Soundscape: no capability to get application bounds
+Soundscape4=Soundscape: no capability to set aural attributes
+Soundscape5=Soundscape: no capability to get an attribute set
+SoundscapeRetained0=Soundscape: illegal node under Background geometry Branch
+SoundscapeRetained1=Soundscape: illegal node under SharedGroup Branch
+SpotLight0=SpotLight: no capability to set light's spreadAngle
+SpotLight1=SpotLight: no capability to read light's spreadAngle
+SpotLight2=Light: no capability to set light's concentration
+SpotLight3=SpotLight: no capability to read light's concentration
+SpotLight4=SpotLight: no capability to set light's direction
+SpotLight6=SpotLight: no capability to read light's direction
+SharedGroupRetained0=SharedGroup: Illegal leaf nodes
+Text3DRetained0=execute() called on Text3D
+Text3DRetained1=Text3D - intersect : Sorry! Geometry type not supported.
+TexCoordGeneration0=TexCoordGeneration: no capability to set enable
+TexCoordGeneration1=TexCoordGeneration: no capability to get enable
+TexCoordGeneration2=TexCoordGeneration: no capability to get format
+TexCoordGeneration3=TexCoordGeneration: no capability to get mode
+TexCoordGeneration4=TexCoordGeneration: no capability to get plane
+TexCoordGeneration5=TexCoordGeneration: illegal texture generation mode
+TexCoordGeneration6=TexCoordGeneration: no capability to set plane
+Texture0=Texture: Illegal mipmapMode value
+Texture1=Texture: Illegal format value
+Texture2=Texture: width NOT power of 2
+Texture3=Texture: height NOT power of 2
+Texture4=Texture: no capability to get boundry mode
+Texture6=Texture: no capability to get filter
+Texture8=Texture: cannot use ImageComponent3D in Texture2D
+Texture9=Texture: no capability to get image
+Texture10=Texture: no capability to get mipmap mode
+Texture11=Texture: no capability to set enable
+Texture12=Texture: no capability to get enable
+Texture13=Texture: no capability to get boundry color
+Texture14=Texture: cannot use ImageComponent2D in Texture3D
+Texture15=Texture: no capability to set image
+Texture16=Texture: no capability to get width
+Texture17=Texture: no capability to get height
+Texture18=Texture: no capability to get number of mipmap levels
+Texture19=Texture: no capability to get format
+Texture20=Texture: number of images != number of mipmap levels
+Texture21=Texture: no capability to get sharpen texture information
+Texture22=Texture: the length of lod does not match the length of pts
+Texture23=Texture: no capability to get texture filter4 information
+Texture24=Texture: the length of weights < 4
+Texture25=Texture: Illegal anisotropic filter mode value
+Texture26=Texture: no capability to get anisotropic filter information
+Texture27=Texture: Illegal anisotropic filter degree
+Texture28=Texture: Illegal minification filter
+Texture29=Texture: Illegal magnification filter
+Texture30=Texture: boundary width < 0
+Texture31=Texture: illegal boundary mode value
+Texture32=Texture: no capability to set base level
+Texture33=Texture: no capability to set maximum level
+Texture34=Texture: no capability to get base level
+Texture35=Texture: no capability to get maximum level
+Texture36=Texture: baseLevel < 0 or baseLevel > maximum Level
+Texture37=Texture: maximumLevel < baseLevel or maximum Level > 2 powerof(max(width,height))
+Texture38=Texture: no capability to set minimum lod
+Texture39=Texture: no capability to set maximum lod
+Texture40=Texture: no capability to get minimum lod
+Texture41=Texture: no capability to get maximum lod
+Texture42=Texture: minimumLOD > maximumLOD
+Texture43=Texture: maximumLOD < minimumLOD
+Texture44=Texture: no capability to set lod offset
+Texture45=Texture: no capability to get lod offset
+Texture2D0=Texture: no capability to get detail texture information
+Texture2D1=Texture: Illegal detail texture mode value
+Texture2D2=Texture: Illegal detail texture level
+Texture2D3=Texture: the length of lod does not match the length of pts
+Texture3D0=Texture: no capability to get boundry mode
+Texture3D1=Texture: depth NOT power of 2
+Texture3D2=Texture: no capability to get depth
+TextureAttributes0=TextureAttributes: no capability to set TextureMode
+TextureAttributes1=TextureAttributes: no capability to get TextureMode
+TextureAttributes2=TextureAttributes: no capability to set TexEnv cplor
+TextureAttributes3=TextureAttributes: no capability to set TexEnv color
+TextureAttributes4=TextureAttributes: no capability to get TexEnv color
+TextureAttributes5=TextureAttributes: no capability to set texture coord transform
+TextureAttributes6=TextureAttributes: no capability to get texture coord transform
+TextureAttributes7=TextureAttributes: no capability to set perspective correction mode
+TextureAttributes8=TextureAttributes: no capability to get perspective correction mode
+TextureAttributes9=TextureAttributes: illegal perspective correction mode
+TextureAttributes10=TextureAttributes: illegal texture mode
+TextureAttributes11=TextureAttributes: no capability to set texture color table
+TextureAttributes12=TextureAttributes: no capability to get texture color table
+TextureAttributes13=TextureAttributes: table.length is not 3 or 4
+TextureAttributes14=TextureAttributes: component array length NOT power of 2
+TextureAttributes15=TextureAttributes: component array do not have same length
+TextureAttributes16=TextureAttributes: no capability to set CombineRgbMode
+TextureAttributes17=TextureAttributes: no capability to get CombineRgbMode
+TextureAttributes18=TextureAttributes: no capability to set CombineAlphaMode
+TextureAttributes19=TextureAttributes: no capability to get CombineAlphaMode
+TextureAttributes20=TextureAttributes: illegal combine mode
+TextureAttributes21=TextureAttributes: no capability to set CombineRgbSource
+TextureAttributes22=TextureAttributes: no capability to get CombineRgbSource
+TextureAttributes23=TextureAttributes: no capability to set CombineAlphaSource
+TextureAttributes24=TextureAttributes: no capability to get CombineAlphaSource
+TextureAttributes25=TextureAttributes: index out of range
+TextureAttributes26=TextureAttributes: illegal combine source
+TextureAttributes27=TextureAttributes: no capability to set CombineRgbFunction
+TextureAttributes28=TextureAttributes: no capability to get CombineRgbFunction
+TextureAttributes29=TextureAttributes: no capability to set CombineAlphaFunction
+TextureAttributes30=TextureAttributes: no capability to get CombineAlphaFunction
+TextureAttributes31=TextureAttributes: illegal combine function
+TextureAttributes32=TextureAttributes: no capability to set CombineRgbScale
+TextureAttributes33=TextureAttributes: no capability to get CombineRgbScale
+TextureAttributes34=TextureAttributes: no capability to set CombineAlphaScale
+TextureAttributes35=TextureAttributes: no capability to get CombineAlphaScale
+TextureAttributes36=TextureAttributes: value other than 1, 2, or 4
+TextureCubeMap1=TextureCubeMap: no capability set images
+TextureCubeMap2=TextureCubeMap: no capability get images
+TextureCubeMap3=TextureCubeMap: cannot use ImageComponent3D in TextureCubeMap
+TextureCubeMap4=TextureCubeMap: illegal cube map face
+TextureRetained0=cannot set image in default texture
+TextureRetained1=Texture:illegal image size
+TextureRetained3=Texture: mipmap image not set at level
+TextureUnitState0=TextureUnitState: no capability to set Texture
+TextureUnitState1=TextureUnitState: no capability to get Texture
+TextureUnitState2=TextureUnitState: no capability to set TextureAttributes
+TextureUnitState3=TextureUnitState: no capability to get TextureAttributes
+TextureUnitState4=TextureUnitState: no capability to set TexCoordGeneration
+TextureUnitState5=TextureUnitState: no capability to get TexCoordGeneration
+Transform3D0=Transform3D add
+Transform3D1=cannot invert matrix
+Transform3D4=Logic error: imax < 0
+TransformGroup0=TransformGroup: non-affine transform
+TransformGroup1=Group: no capability to set transform
+TransformGroup2=Group: no capability to get transform
+TransparencyAttributes0=Transparency: no capability to set transparency mode
+TransparencyAttributes1=Transparency: no capability to get transparency mode
+TransparencyAttributes2=Transparency: no capability to set component
+TransparencyAttributes3=Transparency: no capability to get component
+TransparencyAttributes4=Transparency: no capability to set blend function
+TransparencyAttributes5=Transparency: no capability to get blend function
+TransparencyAttributes6=Transparency: illegal transparency mode
+TransparencyAttributes7=Transparency: illegal source blend function
+TransparencyAttributes8=Transparency: illegal destination blend function
+Traverser0=Cycle found in SharedGroup
+TriangleArray0=TriangleArray: illegal vertexCount
+TriangleArrayRetained0=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.
+TriangleFanArray0=TriangleFanArray: illegal vertexCount
+View0=setViewPolicy: invalid value
+View1=setProjectionPolicy: invalid value
+View2=Cannot set projection when compatibility mode is disabled
+View4=Cannot get projection when compatibility mode is disabled
+View6=Cannot set VpcToEc when compatibility mode is disabled
+View7=non-affine viewing transform
+View8=Cannot get VpcToEc when compatibility mode is disabled
+View9=Cannot get transform when userHeadToVworldEnable is disabled
+View10=Sharing canvas with multiple views
+View13=PhysicalBody is null
+View14=PhysicalEnvironment is null
+View15=View.stopBehaviorScheduler: can't call stopBehaviorScheduler() in a canvas callback.
+View16=View.stopBehaviorScheduler: can't call stopBehaviorScheduler() in a behavior method.
+View17=View.startBehaviorScheduler: can't call startBehaviorScheduler() in a canvas callback.
+View18=View.startBehaviorScheduler: can't call startBehaviorScheduler() in a behavior method.
+View19=View.stopView: can't call stopView() in a canvas callback.
+View20=View.stopView: can't call stopView() in a behavior method.
+View21=View.startView: can't call startView() in a canvas callback.
+View22=View.startView: can't call startView() in a behavior method.
+View23=Can't add an input device when the PhysicalEnvironment object is null
+View24=Can't enumerate input devices when the PhysicalEnvironment object is null
+View25=Can't add an audio device when the PhysicalEnvironment object is null
+View26=Can't enumerate audio devices when the PhysicalEnvironment object is null
+View27=Minimum time cannot be less than 0
+View28=View.renderOnce: can't call renderOnce() in a canvas callback.
+View29=View.renderOnce: can't call renderOnce() in a behavior method.
+View30=View.renderOnce: can't call renderOnce() when view is currently running.
+View31=HMD mode not supported in CYCLOPEAN_EYE_VIEW mode.
+TriangleStripArray0=TriangleStripArray: illegal vertexCount.
+TriangleFanArrayRetained0=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.
+TriangleFanArrayRetained1=stripVertexCounts element less than 3
+ViewPlatform0=ViewPlatform: no capability to write policy
+ViewPlatform1=Illegal policy value
+ViewPlatform2=ViewPlatform: no capability to read policy
+ViewSpecificGroup1=ViewSpecificGroup: no capability to write view
+ViewSpecificGroup2=ViewSpecificGroup: no capability to read view
+ViewSpecificGroup3=ViewSpecificGroup: illegal node under Background geometry Branch
+WakeupOnCollisionEntry0=For collision, only Group, Shape3D, Morph, or BoundingLeaf nodes are permitted.
+WakeupOnCollisionEntry1=WakeupOnCollisionEntry: cannot use object in a background geometry branch to arm a collision
+WakeupOnCollisionEntry4=WakeupOnCollisionEntry: Illegal value for speed hint
+WakeupOnCollisionEntry5=WakeupOnCollisionEntry: Can only call getTriggeringPath from within a Behavior's processStimulus method
+WakeupOnCollisionEntry6=WakeupOnCollisionEntry: Can only call getTriggeringBounds from within a Behavior's processStimulus method
+WakeupOnCollisionEntry7=WakeupOnCollisionEntry: SceneGraphPath is not unique or Object is under a SharedGroup node
+WakeupOnSensorEntry0=WakeupOnSensorEntry: Can only call from within a Behavior's processStimulus method
+WakeupOnSensorExit0=WakeupOnSensorExit: Can only call from within a Behavior's processStimulus method
+WakeupOnViewPlatformEntry0=WakeupOnViewPlatformEntry: Can only call from within a Behavior's processStimulus method
+WakeupOnViewPlatformExit0=WakeupOnViewPlatformExit: Can only call from within a Behavior's processStimulus method
+ViewPlatformRetained0=non-congruent transform above ViewPlatform
+ViewPlatformRetained1=ViewPlatform: illegal node under Background geometry Branch
+ViewPlatformRetained2=ViewPlatform: illegal node under SharedGroup Branch
+ViewPlatformRetained3=ViewPlatform: illegal node under ViewSpecificGroup Branch
+VirtualUniverse0=Locale not attached to this VirtualUniverse
+WakeupOnCollisionExit0=For collision, only Group, Shape3D, Morph, or BoundingLeaf nodes are permitted.
+WakeupOnCollisionExit1=WakeupOnCollisionEntry: cannot use object in a background geometry branch to arm a collision
+WakeupOnCollisionExit3=WakeupOnCollisionEntry: cannot use object in a background geometry branch to arm a collision
+WakeupOnCollisionExit4=WakeupOnCollisionExit: Illegal value for speed hint
+WakeupOnCollisionExit5=WakeupOnCollisionExit: Can only call getTriggeringPath from within a Behavior's processStimulus method
+WakeupOnCollisionExit6=WakeupOnCollisionExit: Can only call getTriggeringBounds from within a Behavior's processStimulus method
+WakeupOnCollisionExit7=WakeupOnCollisionExit: SceneGraphPath is not unique or Object is under a SharedGroup node
+WakeupOnElapsedFrames0=WakeupOnElapsedFrames(int) requires an argument >= 0
+WakeupOnCollisionMovement0=For collision, only Group, Shape3D, Morph, or BoundingLeaf nodes are permitted.
+WakeupOnCollisionMovement1=WakeupOnCollisionEntry: cannot use object in a background geometry branch to arm a collision
+WakeupOnCollisionMovement4=WakeupOnCollisionMovement: Illegal value for speed hint
+WakeupOnCollisionMovement5=WakeupOnCollisionMovement: Can only call getTriggeringPath from within a Behavior's processStimulus method
+WakeupOnCollisionMovement6=WakeupOnCollisionMovement: Can only call getTriggeringBounds from within a Behavior's processStimulus method
+WakeupOnCollisionMovement7=WakeupOnCollisionMovement: SceneGraphPath is not unique or Object is under a SharedGroup node
+WakeupOnCollisionMovement8=WakeupOnCollisionEntry: cannot use object in a background geometry branch to arm a collision
+WakeupCriteriaEnumerator0=No more criterion
+WakeupOnElapsedTime0=WakeupOnElapsedTime(int) requires an argument > 0L
+ModelClip0=ModelClip: no capability to write influencing bounds
+ModelClip1=ModelClip: no capability to read influencing bounds
+ModelClip2=ModelClip: no capability to write plane
+ModelClip3=ModelClip: no capability to read plane
+ModelClip4=ModelClip: no capability to write enable
+ModelClip5=ModelClip: no capability to read enable
+ModelClip6=ModelClip: illegal plane num value
+ModelClip7=ModelClip: no capability to write scope
+ModelClip8=ModelClip: no capability to read scope
+ModelClip9=ModelClip: no capability to insert scope
+ModelClip10=ModelClip: no capability to remove scope
+ModelClip11=ModelClip: no capability to read scopes
+ModelClip12=ModelClip: no capability to append scope
+ModelClip13=ModelClip: no capability to write influencing bounding leaf
+ModelClip14=ModelClip: no capability to read influencing bounding leaf
+ModelClipRetained1=ModelClip: illegal node under SharedGroup Branch
+MasterControl0=OpenGL is not MT safe
+MasterControl1=Green threads are not supported
+J3DBuffer0=Native access to NIO buffer not supported
+J3DBuffer1=NIO buffer must be a direct buffer
+J3DBuffer2=NIO buffer must match native byte order of underlying platform
diff --git a/src/classes/share/javax/media/j3d/ExponentialFog.java b/src/classes/share/javax/media/j3d/ExponentialFog.java
new file mode 100644
index 0000000..9d6a9fc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ExponentialFog.java
@@ -0,0 +1,190 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+
+/**
+ * The ExponentialFog leaf node extends the Fog leaf node by adding a
+ * fog density that is used as the exponent of the fog equation. The
+ * density is defined in the local coordinate system of the node, but
+ * the actual fog equation will ideally take place in eye coordinates.
+ * <P>
+ * The fog blending factor, f, is computed as follows:
+ * <P><UL>
+ * f = e<sup>-(density * z)</sup><P>
+ * where
+ * <ul>z is the distance from the viewpoint.<br>
+ * density is the density of the fog.<P></ul></UL>
+ *
+ * In addition to specifying the fog density, ExponentialFog lets you
+ * specify the fog color, which is represented by R, G, and B
+ * color values, where a color of (0,0,0) represents black.
+ */
+public class ExponentialFog extends Fog {
+ /**
+ * Specifies that this ExponentialFog node allows read access to its
+ * density information.
+ */
+ public static final int
+ ALLOW_DENSITY_READ = CapabilityBits.EXPONENTIAL_FOG_ALLOW_DENSITY_READ;
+
+ /**
+ * Specifies that this ExponentialFog node allows write access to its
+ * density information.
+ */
+ public static final int
+ ALLOW_DENSITY_WRITE = CapabilityBits.EXPONENTIAL_FOG_ALLOW_DENSITY_WRITE;
+
+ /**
+ * Constructs an ExponentialFog node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * density : 1.0<br>
+ * </ul>
+ */
+ public ExponentialFog() {
+ // Just use the defaults
+ }
+
+ /**
+ * Constructs an ExponentialFog node with the specified fog color.
+ * @param color the fog color
+ */
+ public ExponentialFog(Color3f color) {
+ super(color);
+ }
+
+ /**
+ * Constructs an ExponentialFog node with the specified fog color
+ * and density.
+ * @param color the fog color
+ * @param density the density of the fog
+ */
+ public ExponentialFog(Color3f color, float density) {
+ super(color);
+ ((ExponentialFogRetained)this.retained).initDensity(density);
+ }
+
+ /**
+ * Constructs an ExponentialFog node with the specified fog color.
+ * @param r the red component of the fog color
+ * @param g the green component of the fog color
+ * @param b the blue component of the fog color
+ */
+ public ExponentialFog(float r, float g, float b) {
+ super(r, g, b);
+ }
+
+ /**
+ * Constructs an ExponentialFog node with the specified fog color
+ * and density.
+ * @param r the red component of the fog color
+ * @param g the green component of the fog color
+ * @param b the blue component of the fog color
+ * @param density the density of the fog
+ */
+ public ExponentialFog(float r, float g, float b, float density) {
+ super(r, g, b);
+ ((ExponentialFogRetained)this.retained).initDensity(density);
+ }
+
+ /**
+ * Sets fog density.
+ * @param density the new density of this fog
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDensity(float density) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DENSITY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ExponentialFog0"));
+
+ if (isLive())
+ ((ExponentialFogRetained)this.retained).setDensity(density);
+ else
+ ((ExponentialFogRetained)this.retained).initDensity(density);
+ }
+
+ /**
+ * Gets fog density.
+ * @return the density of this fog
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getDensity() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DENSITY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ExponentialFog1"));
+
+ return ((ExponentialFogRetained)this.retained).getDensity();
+ }
+
+ /**
+ * Creates the retained mode ExponentialFogRetained object that this
+ * ExponentialFog node will point to.
+ */
+ void createRetained() {
+ this.retained = new ExponentialFogRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * 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) {
+ ExponentialFog ef = new ExponentialFog();
+ ef.duplicateNode(this, forceDuplicate);
+ return ef;
+ }
+
+
+ /**
+ * Copies all ExponentialFog 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ((ExponentialFogRetained) retained).initDensity(
+ ((ExponentialFogRetained) originalNode.retained).getDensity());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ExponentialFogRetained.java b/src/classes/share/javax/media/j3d/ExponentialFogRetained.java
new file mode 100644
index 0000000..63bee5a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ExponentialFogRetained.java
@@ -0,0 +1,147 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+import java.util.ArrayList;
+
+/**
+ * The ExponentialFog leaf node defines distance parameters for
+ * exponential fog.
+ */
+class ExponentialFogRetained extends FogRetained {
+ // Fog density
+ float density = 1.0f;
+
+ // dirty bits for ExponentialFog
+ static final int DENSITY_CHANGED = FogRetained.LAST_DEFINED_BIT << 1;
+
+
+ ExponentialFogRetained() {
+ this.nodeType = NodeRetained.EXPONENTIALFOG;
+ }
+
+ /**
+ * initializes fog density
+ */
+ void initDensity(float density){
+ this.density = density;
+ }
+
+ /**
+ * Sets fog density and send a message
+ */
+ void setDensity(float density){
+ this.density = density;
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.FOG_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(DENSITY_CHANGED);
+ createMessage.args[2] = new Float(density);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ /**
+ * Gets fog density
+ */
+ float getDensity(){
+ return this.density;
+ }
+
+
+ void setLive(SetLiveState s) {
+ super.setLive(s);
+ GroupRetained group;
+
+ // Initialize the mirror object, this needs to be done, when
+ // renderBin is not accessing any of the fields
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.FOG_CHANGED;
+ createMessage.args[0] = this;
+ // a snapshot of all attributes that needs to be initialized
+ // in the mirror object
+ createMessage.args[1]= new Integer(INIT_MIRROR);
+ ArrayList addScopeList = new ArrayList();
+ for (int i = 0; i < scopes.size(); i++) {
+ group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.addAllNodesForScopedFog(mirrorFog, addScopeList, tempKey);
+ }
+ Object[] scopeInfo = new Object[2];
+ scopeInfo[0] = ((scopes.size() > 0) ? Boolean.TRUE:Boolean.FALSE);
+ scopeInfo[1] = addScopeList;
+ createMessage.args[2] = scopeInfo;
+ Color3f clr = new Color3f(color);
+ createMessage.args[3] = clr;
+
+ Object[] obj = new Object[5];
+ obj[0] = boundingLeaf;
+ obj[1] = (regionOfInfluence != null?regionOfInfluence.clone():null);
+ obj[2] = (inBackgroundGroup? Boolean.TRUE:Boolean.FALSE);
+ obj[3] = geometryBackground;
+ obj[4] = new Float(density);
+
+ createMessage.args[4] = obj;
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ }
+
+
+ /**
+ * This method and its native counterpart update the native context
+ * fog values.
+ */
+ native void update(long ctx, float red, float green, float blue, float density);
+
+ void update(long ctx, double scale) {
+ update(ctx, color.x, color.y, color.z, density);
+ }
+
+
+
+ // The update Object function.
+ // Note : if you add any more fields here , you need to update
+ // updateFog() in RenderingEnvironmentStructure
+ void updateMirrorObject(Object[] objs) {
+
+ int component = ((Integer)objs[1]).intValue();
+
+
+ if ((component & DENSITY_CHANGED) != 0)
+ ((ExponentialFogRetained)mirrorFog).density = ((Float)objs[2]).floatValue();
+
+ if ((component & INIT_MIRROR) != 0) {
+ ((ExponentialFogRetained)mirrorFog).density = ((Float)((Object[])objs[4])[4]).floatValue();
+
+ }
+
+ super.updateMirrorObject(objs);
+ }
+
+
+ // Clone the retained side only, internal use only
+ protected Object clone() {
+ ExponentialFogRetained efr =
+ (ExponentialFogRetained)super.clone();
+
+ efr.initDensity(getDensity());
+
+ return efr;
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/Fog.java b/src/classes/share/javax/media/j3d/Fog.java
new file mode 100644
index 0000000..4ca055d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Fog.java
@@ -0,0 +1,536 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+import javax.vecmath.Color3f;
+
+/**
+ * The Fog leaf node defines a set of fog parameters common to all
+ * types of fog. These parameters include the fog color and a region
+ * of influence in which this Fog node is active.
+ * A Fog node also contains a list of Group nodes that specifies the
+ * hierarchical scope of this Fog. If the scope list is empty, then
+ * the Fog node has universe scope: all nodes within the region of
+ * influence are affected by this Fog node. If the scope list is
+ * non-empty, then only those Leaf nodes under the Group nodes in the
+ * scope list are affected by this Fog node (subject to the
+ * influencing bounds).
+ * <p>
+ * If the regions of influence of multiple Fog nodes overlap, the
+ * Java 3D system will choose a single set of fog parameters for those
+ * objects that lie in the intersection. This is done in an
+ * implementation-dependent manner, but in general, the Fog node that
+ * is "closest" to the object is chosen.
+ */
+
+public abstract class Fog extends Leaf {
+ /**
+ * Specifies that this Fog node allows read access to its
+ * influencing bounds and bounds leaf information.
+ */
+ public static final int
+ ALLOW_INFLUENCING_BOUNDS_READ = CapabilityBits.FOG_ALLOW_INFLUENCING_BOUNDS_READ;
+
+ /**
+ * Specifies that this Fog node allows write access to its
+ * influencing bounds and bounds leaf information.
+ */
+ public static final int
+ ALLOW_INFLUENCING_BOUNDS_WRITE = CapabilityBits.FOG_ALLOW_INFLUENCING_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this Fog node allows read access to its color
+ * information.
+ */
+ public static final int
+ ALLOW_COLOR_READ = CapabilityBits.FOG_ALLOW_COLOR_READ;
+
+ /**
+ * Specifies that this Fog node allows write access to its color
+ * information.
+ */
+ public static final int
+ ALLOW_COLOR_WRITE = CapabilityBits.FOG_ALLOW_COLOR_WRITE;
+
+ /**
+ * Specifies that this Fog node allows read access to its scope
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_SCOPE_READ = CapabilityBits.FOG_ALLOW_SCOPE_READ;
+
+ /**
+ * Specifies that this Fog node allows write access to its scope
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_SCOPE_WRITE = CapabilityBits.FOG_ALLOW_SCOPE_WRITE;
+
+ /**
+ * Constructs a Fog node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * color : black (0,0,0)<br>
+ * scope : empty (universe scope)<br>
+ * influencing bounds : null<br>
+ * influencing bounding leaf : null<br>
+ * </ul>
+ */
+ public Fog() {
+ // Just use the defaults
+ }
+
+ /**
+ * Constructs a Fog node with the specified fog color.
+ * @param color the fog color
+ */
+ public Fog(Color3f color) {
+ ((FogRetained)this.retained).initColor(color);
+ }
+
+ /**
+ * Constructs a Fog node with the specified fog color.
+ * @param r the red component of the fog color
+ * @param g the green component of the fog color
+ * @param b the blue component of the fog color
+ */
+ public Fog(float r, float g, float b) {
+ ((FogRetained)this.retained).initColor(r, g, b);
+ }
+
+ /**
+ * Sets the fog color to the specified color.
+ * @param color the new fog color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog0"));
+
+ if (isLive())
+ ((FogRetained)this.retained).setColor(color);
+ else
+ ((FogRetained)this.retained).initColor(color);
+ }
+
+ /**
+ * Sets the fog color to the specified color.
+ * @param r the red component of the fog color
+ * @param g the green component of the fog color
+ * @param b the blue component of the fog color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setColor(float r, float g, float b) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog0"));
+
+ if (isLive())
+ ((FogRetained)this.retained).setColor(r, g, b);
+ else
+ ((FogRetained)this.retained).initColor(r, g, b);
+ }
+
+ /**
+ * Retrieves the fog color.
+ * @param color the vector that will receive the current fog color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog2"));
+
+ ((FogRetained)this.retained).getColor(color);
+ }
+
+ /**
+ * Sets the Fog's influencing region to the specified bounds.
+ * This is used when the influencing bounding leaf is set to null.
+ * @param region the bounds that contains the Fog's new influencing region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog3"));
+
+ if (isLive())
+ ((FogRetained)this.retained).setInfluencingBounds(region);
+ else
+ ((FogRetained)this.retained).initInfluencingBounds(region);
+
+ }
+
+ /**
+ * Retrieves the Fog node's influencing bounds.
+ * @return this Fog's influencing bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getInfluencingBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog4"));
+
+ return ((FogRetained)this.retained).getInfluencingBounds();
+ }
+
+ /**
+ * Sets the Fog's influencing region to the specified bounding leaf.
+ * When set to a value other than null, this overrides the influencing
+ * bounds object.
+ * @param region the bounding leaf node used to specify the Fog
+ * node's new influencing region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog3"));
+
+ if (isLive())
+ ((FogRetained)this.retained).setInfluencingBoundingLeaf(region);
+ else
+ ((FogRetained)this.retained).initInfluencingBoundingLeaf(region);
+ }
+
+ /**
+ * Retrieves the Fog node's influencing bounding leaf.
+ * @return this Fog's influencing bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getInfluencingBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog4"));
+
+ return ((FogRetained)this.retained).getInfluencingBoundingLeaf();
+ }
+
+
+ /**
+ * Replaces the node at the specified index in this Fog node's
+ * list of scopes with the specified Group node.
+ * By default, Fog nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be stored at the specified index.
+ * @param index the index of the Group node to be replaced.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void setScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog7"));
+
+
+ if (isLive())
+ ((FogRetained)this.retained).setScope(scope, index);
+ else
+ ((FogRetained)this.retained).initScope(scope, index);
+ }
+
+
+ /**
+ * Retrieves the Group node at the specified index from this Fog node's
+ * list of scopes.
+ * @param index the index of the Group node to be returned.
+ * @return the Group node at the specified index.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Group getScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog8"));
+
+ return ((FogRetained)this.retained).getScope(index);
+ }
+
+
+ /**
+ * Inserts the specified Group node into this Fog node's
+ * list of scopes at the specified index.
+ * By default, Fog nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be inserted at the specified index.
+ * @param index the index at which the Group node is inserted.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void insertScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog9"));
+
+ if (isLive())
+ ((FogRetained)this.retained).insertScope(scope, index);
+ else
+ ((FogRetained)this.retained).initInsertScope(scope, index);
+ }
+
+
+ /**
+ * Removes the node at the specified index from this Fog node's
+ * list of scopes. If this operation causes the list of scopes to
+ * become empty, then this Fog will have universe scope: all nodes
+ * within the region of influence will be affected by this Fog node.
+ * @param index the index of the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the group node at the
+ * specified index is part of a compiled scene graph
+ */
+ public void removeScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog10"));
+
+ if (isLive())
+ ((FogRetained)this.retained).removeScope(index);
+ else
+ ((FogRetained)this.retained).initRemoveScope(index);
+ }
+
+
+ /**
+ * Returns an enumeration of this Fog node's list of scopes.
+ * @return an Enumeration object containing all nodes in this Fog node's
+ * list of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Enumeration getAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog11"));
+
+ return (Enumeration) ((FogRetained)this.retained).getAllScopes();
+ }
+
+
+ /**
+ * Appends the specified Group node to this Fog node's list of scopes.
+ * By default, Fog nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be appended.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void addScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog12"));
+
+ if (isLive())
+ ((FogRetained)this.retained).addScope(scope);
+ else
+ ((FogRetained)this.retained).initAddScope(scope);
+ }
+
+
+ /**
+ * Returns the number of nodes in this Fog node's list of scopes.
+ * If this number is 0, then the list of scopes is empty and this
+ * Fog node has universe scope: all nodes within the region of
+ * influence are affected by this Fog node.
+ * @return the number of nodes in this Fog node's list of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int numScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog11"));
+
+ return ((FogRetained)this.retained).numScopes();
+ }
+
+
+ /**
+ * Retrieves the index of the specified Group node in this
+ * Fog node's list of scopes.
+ *
+ * @param scope the Group node to be looked up.
+ * @return the index of the specified Group node;
+ * returns -1 if the object is not in the list.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog8"));
+ return ((FogRetained)this.retained).indexOfScope(scope);
+ }
+
+
+ /**
+ * Removes the specified Group node from this Fog
+ * node's list of scopes. If the specified object is not in the
+ * list, the list is not modified. If this operation causes the
+ * list of scopes to become empty, then this Fog
+ * will have universe scope: all nodes within the region of
+ * influence will be affected by this Fog node.
+ *
+ * @param scope the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog10"));
+
+ if (isLive())
+ ((FogRetained)this.retained).removeScope(scope);
+ else
+ ((FogRetained)this.retained).initRemoveScope(scope);
+ }
+
+
+ /**
+ * Removes all Group nodes from this Fog node's
+ * list of scopes. The Fog node will then have
+ * universe scope: all nodes within the region of influence will
+ * be affected by this Fog node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if any group node in this
+ * node's list of scopes is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Fog10"));
+
+ if (isLive())
+ ((FogRetained)this.retained).removeAllScopes();
+ else
+ ((FogRetained)this.retained).initRemoveAllScopes();
+ }
+
+
+ /**
+ * Copies all Fog 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ FogRetained attr = (FogRetained) originalNode.retained;
+ FogRetained rt = (FogRetained) retained;
+
+ Color3f c = new Color3f();
+ attr.getColor(c);
+ rt.initColor(c);
+ rt.initInfluencingBounds(attr.getInfluencingBounds());
+
+ Enumeration elm = attr.getAllScopes();
+ while (elm.hasMoreElements()) {
+ // this reference will set correctly in updateNodeReferences() callback
+ rt.initAddScope((Group) elm.nextElement());
+ }
+
+ // this reference will set correctly in updateNodeReferences() callback
+ rt.initInfluencingBoundingLeaf(attr.getInfluencingBoundingLeaf());
+ }
+
+ /**
+ * Callback used to allow a node to check if any nodes referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any node references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding Node in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * node is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+
+ FogRetained rt = (FogRetained) retained;
+ BoundingLeaf bl = rt.getInfluencingBoundingLeaf();
+
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.initInfluencingBoundingLeaf((BoundingLeaf) o);
+ }
+
+
+ int num = rt.numScopes();
+ for (int i=0; i < num; i++) {
+ rt.initScope((Group) referenceTable.
+ getNewObjectReference(rt.getScope(i)), i);
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/FogRetained.java b/src/classes/share/javax/media/j3d/FogRetained.java
new file mode 100644
index 0000000..33a92b9
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/FogRetained.java
@@ -0,0 +1,789 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.ArrayList;
+
+/**
+ * The Fog leaf node defines Fog parameters.
+ * It also specifies an region of influence in which this fog node
+ * is active.
+ */
+abstract class FogRetained extends LeafRetained{
+
+ // Statics used when something in the fog changes
+ static final int COLOR_CHANGED = 0x0001;
+ static final int SCOPE_CHANGED = 0x0002;
+ static final int BOUNDS_CHANGED = 0x0004;
+ static final int BOUNDINGLEAF_CHANGED = 0x0008;
+ static final int INIT_MIRROR = 0x0010;
+ static final int CLEAR_MIRROR = 0x0020;
+ static final int LAST_DEFINED_BIT = 0x0020;
+
+ // Fog color.
+ Color3f color = new Color3f(0.0f, 0.0f, 0.0f);
+
+ /**
+ * The Boundary object defining the lights's region of influence.
+ */
+ Bounds regionOfInfluence = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * Vector of GroupRetained nodes that scopes this fog.
+ */
+ Vector scopes = new Vector();
+
+ // An int that is set when this fog is changed
+ int isDirty = 0xffff;
+
+ // This is true when this fog is referenced in an immediate mode context
+ boolean inImmCtx = false;
+
+ /**
+ * The transformed value of the applicationRegion.
+ */
+ Bounds region = null;
+
+ // A reference to the scene graph fog
+ FogRetained sgFog = null;
+
+ // The mirror copy of this fog
+ FogRetained mirrorFog = null;
+
+ // Target threads to be notified when light changes
+ static final int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+ // Boolean to indicate if this object is scoped (only used for mirror objects
+ boolean isScoped = false;
+
+ // The object that contains the dynamic HashKey - a string type object
+ // Used in scoping
+ HashKey tempKey = new HashKey(250);
+
+ /**
+ * The EnvironmentSets which reference this fog.
+ * Note that multiple RenderBin update thread may access
+ * this shared environmentSets simultaneously.
+ * So we use UnorderList when sync. all the operations.
+ */
+ UnorderList environmentSets = new UnorderList(1, EnvironmentSet.class);
+
+ // Is true, if the mirror fog is viewScoped
+ boolean isViewScoped = false;
+
+ FogRetained() {
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ /**
+ * Initialize the fog color to the specified color.
+ */
+ void initColor(Color3f color) {
+ this.color.set(color);
+ }
+ /**
+ * Sets the fog color to the specified color and send message
+ */
+ void setColor(Color3f color) {
+ this.color.set(color);
+ sendMessage(COLOR_CHANGED, new Color3f(color));
+ }
+
+ /**
+ * Sets the fog color to the specified color.
+ */
+ void initColor(float r, float g, float b) {
+ this.color.x = r;
+ this.color.y = g;
+ this.color.z = b;
+ }
+ /**
+ * Sets the fog color to the specified color and send message
+ */
+ void setColor(float r, float g, float b) {
+ initColor(r, g, b);
+ sendMessage(COLOR_CHANGED, new Color3f(r, g, b));
+ }
+
+ /**
+ * Retrieves the fog color.
+ */
+ void getColor(Color3f color) {
+ color.set(this.color);
+ }
+
+ /**
+ * Set the Fog's region of influence.
+ */
+ void initInfluencingBounds(Bounds region) {
+ if (region != null) {
+ this.regionOfInfluence = (Bounds) region.clone();
+ } else {
+ this.regionOfInfluence = null;
+ }
+ if (staticTransform != null) {
+ this.regionOfInfluence.transform(staticTransform.transform);
+ }
+ }
+
+ /**
+ * Set the Fog's region of influence and send message
+ */
+ void setInfluencingBounds(Bounds region) {
+ initInfluencingBounds(region);
+ sendMessage(BOUNDS_CHANGED,
+ (region != null ? region.clone() : null));
+ }
+
+ /**
+ * Get the Fog's region of Influence.
+ */
+ Bounds getInfluencingBounds() {
+ Bounds b = null;
+ if (regionOfInfluence != null) {
+ b = (Bounds)regionOfInfluence.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Set the Fog's region of influence to the specified Leaf node.
+ */
+ void initInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ } else {
+ boundingLeaf = null;
+ }
+ }
+
+ /**
+ * Set the Fog's region of influence to the specified Leaf node.
+ */
+ void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (boundingLeaf != null)
+ boundingLeaf.mirrorBoundingLeaf.removeUser(mirrorFog);
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorFog);
+ } else {
+ boundingLeaf = null;
+ }
+ sendMessage(BOUNDINGLEAF_CHANGED,
+ (boundingLeaf != null ?
+ boundingLeaf.mirrorBoundingLeaf : null));
+ }
+
+
+ /**
+ * Get the Fog's region of influence.
+ */
+ BoundingLeaf getInfluencingBoundingLeaf() {
+ return (boundingLeaf != null ?
+ (BoundingLeaf)boundingLeaf.source : null);
+ }
+
+ /**
+ * Replaces the specified scope with the scope provided.
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void initScope(Group scope, int index) {
+ scopes.setElementAt((GroupRetained)(scope.retained), index);
+
+ }
+
+ /**
+ * Replaces the specified scope with the scope provided.
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void setScope(Group scope, int index) {
+
+ ArrayList addScopeList = new ArrayList();
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group;
+ Object[] scopeInfo = new Object[3];
+
+
+ group = (GroupRetained) scopes.get(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedFog(mirrorFog, removeScopeList, tempKey);
+
+ group = (GroupRetained)scope.retained;
+ initScope(scope, index);
+ tempKey.reset();
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ group.addAllNodesForScopedFog(mirrorFog,addScopeList, tempKey);
+
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE:Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ /**
+ * Inserts the specified scope at specified index.before the
+ * fog is live
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void initInsertScope(Node scope, int index) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ group.setFogScope();
+ scopes.insertElementAt((GroupRetained)(scope.retained), index);
+ }
+
+ /**
+ * Inserts the specified scope at specified index and sends
+ * a message
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void insertScope(Node scope, int index) {
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+
+ initInsertScope(scope, index);
+ GroupRetained group = (GroupRetained)scope.retained;
+ tempKey.reset();
+ group.addAllNodesForScopedFog(mirrorFog,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+
+ void initRemoveScope(int index) {
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+ scopes.removeElementAt(index);
+ group.removeFogScope();
+
+ }
+
+ void removeScope(int index) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+
+ tempKey.reset();
+ group.removeAllNodesForScopedFog(mirrorFog, removeScopeList, tempKey);
+
+ initRemoveScope(index);
+ scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+
+ /**
+ * Returns the scope specified by the index.
+ * @param index which scope to return
+ * @return the scoperen at location index
+ */
+ Group getScope(int index) {
+ return (Group)(((GroupRetained)(scopes.elementAt(index))).source);
+ }
+
+ /**
+ * Returns an enumeration object of the scoperen.
+ * @return an enumeration object of the scoperen
+ */
+ Enumeration getAllScopes() {
+ Enumeration elm = scopes.elements();
+ Vector v = new Vector(scopes.size());
+ while (elm.hasMoreElements()) {
+ v.add( ((GroupRetained) elm.nextElement()).source);
+ }
+ return v.elements();
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes before
+ * the fog is alive
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void initAddScope(Group scope) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ scopes.addElement((GroupRetained)(scope.retained));
+ group.setFogScope();
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes.
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void addScope(Group scope) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scope.retained;
+
+ initAddScope(scope);
+ tempKey.reset();
+ group.addAllNodesForScopedFog(mirrorFog,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ /**
+ * Returns a count of this nodes' scopes.
+ * @return the number of scopes descendant from this node
+ */
+ int numScopes() {
+ return scopes.size();
+ }
+
+ /**
+ * Returns the index of the specified scope within this nodes' list of scopes
+ * @param scope whose index is desired
+ * @return index of specified scope
+ */
+ int indexOfScope(Group scope) {
+ if(scope != null)
+ return scopes.indexOf((GroupRetained)scope.retained);
+ else
+ return scopes.indexOf(null);
+ }
+
+ /**
+ * Removes the specified scope from this nodes' list of scopes
+ * @param scope to be removed. If the scope is not found,
+ * the method returns silently
+ */
+ void removeScope(Group scope) {
+ int i = indexOfScope(scope);
+ if(i >= 0)
+ removeScope(i);
+ }
+
+ void initRemoveScope(Group scope) {
+ int i = indexOfScope(scope);
+ if(i >= 0)
+ initRemoveScope(i);
+ }
+
+ /**
+ * Removes all the scopes from this node's list of scopes.
+ * The node should revert to universal
+ * scope after this method returns
+ */
+ void removeAllScopes() {
+ Object[] scopeInfo = new Object[3];
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group;
+ int n = scopes.size();
+
+ tempKey.reset();
+ for(int index = n-1; index >= 0; index--) {
+ group = (GroupRetained)scopes.elementAt(index);
+ group.removeAllNodesForScopedFog(mirrorFog, removeScopeList, tempKey);
+ initRemoveScope(index);
+ }
+ scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = Boolean.FALSE;
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ /**
+ * Removes all scopes from this node
+ */
+ void initRemoveAllScopes() {
+ int n = scopes.size();
+ for(int index = n-1; index >= 0; index--)
+ initRemoveScope(index);
+ }
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+ boolean isScoped() {
+ return (scopes != null);
+ }
+
+ /**
+ * This abstract method is used to update the current native
+ * context fog values.
+ */
+ abstract void update(long ctx, double scale);
+
+
+ void updateImmediateMirrorObject(Object[] objs) {
+ GroupRetained group;
+ Vector currentScopes;
+ int i, nscopes;
+ Transform3D trans;
+
+ int component = ((Integer)objs[1]).intValue();
+ if ((component & BOUNDS_CHANGED) != 0) {
+ mirrorFog.regionOfInfluence = (Bounds) objs[2];
+ if (mirrorFog.boundingLeaf == null) {
+ if (objs[2] != null) {
+ mirrorFog.region = ((Bounds)mirrorFog.regionOfInfluence).copy(mirrorFog.region);
+ mirrorFog.region.transform(
+ mirrorFog.regionOfInfluence,
+ getCurrentLocalToVworld());
+ }
+ else {
+ mirrorFog.region = null;
+ }
+ }
+ }
+ else if ((component & BOUNDINGLEAF_CHANGED) != 0) {
+ mirrorFog.boundingLeaf = (BoundingLeafRetained)objs[2];
+ if (objs[2] != null) {
+ mirrorFog.region = (Bounds)mirrorFog.boundingLeaf.transformedRegion;
+ }
+ else {
+ if (mirrorFog.regionOfInfluence != null) {
+ mirrorFog.region = ((Bounds)mirrorFog.regionOfInfluence).copy(mirrorFog.region);
+ mirrorFog.region.transform(
+ mirrorFog.regionOfInfluence,
+ getCurrentLocalToVworld());
+ }
+ else {
+ mirrorFog.region = null;
+ }
+
+ }
+ }
+ else if ((component & SCOPE_CHANGED) != 0) {
+ Object[] scopeList = (Object[])objs[2];
+ ArrayList addList = (ArrayList)scopeList[0];
+ ArrayList removeList = (ArrayList)scopeList[1];
+ boolean isScoped = ((Boolean)scopeList[2]).booleanValue();
+
+ if (addList != null) {
+ mirrorFog.isScoped = isScoped;
+ for (i = 0; i < addList.size(); i++) {
+ Shape3DRetained obj = ((GeometryAtom)addList.get(i)).source;
+ obj.addFog(mirrorFog);
+ }
+ }
+
+ if (removeList != null) {
+ mirrorFog.isScoped = isScoped;
+ for (i = 0; i < removeList.size(); i++) {
+ Shape3DRetained obj = ((GeometryAtom)removeList.get(i)).source;
+ obj.removeFog(mirrorFog);
+ }
+ }
+ }
+
+
+ }
+
+ /**
+ * The update Object function.
+ */
+ void updateMirrorObject(Object[] objs) {
+
+ int component = ((Integer)objs[1]).intValue();
+ if ((component & COLOR_CHANGED) != 0) {
+ mirrorFog.color.set((Color3f)objs[2]);
+ }
+ if ((component & INIT_MIRROR) != 0) {
+ mirrorFog.color.set((Color3f)objs[3]);
+ }
+ }
+
+
+ /**
+ * Note: This routine will only be called on
+ * the mirror object - will update the object's
+ * cached region and transformed region
+ */
+ void updateBoundingLeaf() {
+ if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) {
+ region = boundingLeaf.transformedRegion;
+ } else {
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence,
+ getCurrentLocalToVworld());
+ } else {
+ region = null;
+ }
+ }
+ }
+
+ /**
+ * This setLive routine just calls the superclass's method (after
+ * checking for use by an immediate context). It is up to the
+ * subclasses of fog to add themselves to the list of fogs
+ */
+ void setLive(SetLiveState s) {
+ GroupRetained group;
+ Vector currentScopes;
+ int i, nscopes;
+ TransformGroupRetained[] tlist;
+
+ if (inImmCtx) {
+ throw new IllegalSharingException(J3dI18N.getString("FogRetained0"));
+ }
+ super.doSetLive(s);
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("FogRetained1"));
+ }
+
+
+ // Create the mirror object
+ // Initialization of the mirror object during the INSERT_NODE
+ // message (in updateMirrorObject)
+ if (mirrorFog == null) {
+ // mirrorFog = (FogRetained)this.clone(true);
+ mirrorFog = (FogRetained)this.clone();
+ // Assign the bounding leaf of this mirror object as null
+ // it will later be assigned to be the mirror of the lights
+ // bounding leaf object
+ mirrorFog.boundingLeaf = null;
+ mirrorFog.sgFog = this;
+ }
+ // initMirrorObject();
+ // If bounding leaf is not null, add the mirror object as a user
+ // so that any changes to the bounding leaf will be received
+ if (boundingLeaf != null) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorFog);
+ }
+
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(mirrorFog);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(mirrorFog);
+ }
+
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorFog, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+
+
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorFog, Targets.ENV_TARGETS);
+ }
+ mirrorFog.switchState = (SwitchState)s.switchStates.get(0);
+
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+
+
+
+ super.markAsLive();
+
+
+ }
+ // This is called on the parent object
+ void initMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ Object[] scopeInfo = (Object[]) args[2];
+ Boolean scoped = (Boolean)scopeInfo[0];
+ ArrayList shapeList = (ArrayList)scopeInfo[1];
+ BoundingLeafRetained bl=(BoundingLeafRetained)((Object[])args[4])[0];
+ Bounds bnds = (Bounds)((Object[])args[4])[1];
+
+ mirrorFog.inBackgroundGroup = ((Boolean)((Object[])args[4])[2]).booleanValue();
+ mirrorFog.geometryBackground = (BackgroundRetained)((Object[])args[4])[3];
+ for (int i = 0; i < shapeList.size(); i++) {
+ shape = ((GeometryAtom)shapeList.get(i)).source;
+ shape.addFog(mirrorFog);
+ }
+ mirrorFog.isScoped = scoped.booleanValue();
+
+ if (bl != null) {
+ mirrorFog.boundingLeaf = bl.mirrorBoundingLeaf;
+ mirrorFog.region = boundingLeaf.transformedRegion;
+ } else {
+ mirrorFog.boundingLeaf = null;
+ mirrorFog.region = null;
+ }
+
+ if (bnds != null) {
+ mirrorFog.regionOfInfluence = bnds;
+ if (mirrorFog.region == null) {
+ mirrorFog.region = (Bounds)regionOfInfluence.clone();
+ mirrorFog.region.transform(regionOfInfluence, getLastLocalToVworld());
+ }
+ }
+ else {
+ mirrorFog.regionOfInfluence = null;
+ }
+
+ }
+
+ // This is called on the parent object
+ void clearMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ ArrayList shapeList = (ArrayList)args[2];
+ ArrayList removeScopeList = new ArrayList();
+
+ for (int i = 0; i < shapeList.size(); i++) {
+ shape = ((GeometryAtom)shapeList.get(i)).source;
+ shape.removeFog(mirrorFog);
+ }
+ mirrorFog.isScoped = false;
+
+
+
+ }
+
+
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it removes itself to the list of fogs
+ */
+ void clearLive(SetLiveState s) {
+ int i, j;
+ GroupRetained group;
+
+ super.clearLive(s);
+
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorFog, Targets.ENV_TARGETS);
+ }
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+
+ // Remove this mirror light as users of the bounding leaf
+ if (mirrorFog.boundingLeaf != null)
+ mirrorFog.boundingLeaf.removeUser(mirrorFog);
+
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(mirrorFog);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(mirrorFog);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorFog, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+
+ if (scopes.size() > 0) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.FOG_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(CLEAR_MIRROR);
+ ArrayList removeScopeList = new ArrayList();
+ for (i = 0; i < scopes.size(); i++) {
+ group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.removeAllNodesForScopedFog(mirrorFog, removeScopeList, tempKey);
+ }
+ createMessage.args[2] = removeScopeList;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ // Clone the retained side only, internal use only
+ protected Object clone() {
+ FogRetained fr = (FogRetained)super.clone();
+
+ fr.color = new Color3f(color);
+ Bounds b = getInfluencingBounds();
+ if (b != null) {
+ fr.initInfluencingBounds(b);
+ }
+
+ fr.scopes = new Vector();
+ fr.isDirty = 0xffff;
+ fr.inImmCtx = false;
+ fr.region = null;
+ fr.sgFog = null;
+ fr.mirrorFog = null;
+ fr.environmentSets = new UnorderList(1, EnvironmentSet.class);
+ return fr;
+ }
+
+ void updateTransformChange() {
+ }
+
+ // Called on mirror object
+ void updateImmediateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (boundingLeaf == null) {
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence,
+ sgFog.getCurrentLocalToVworld());
+ }
+
+ }
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.FOG_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (regionOfInfluence != null) {
+ regionOfInfluence.transform(xform.transform);
+ }
+ }
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ leafList.add(mirrorFog);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Font3D.java b/src/classes/share/javax/media/j3d/Font3D.java
new file mode 100644
index 0000000..c1fbe48
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Font3D.java
@@ -0,0 +1,1105 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import com.sun.j3d.utils.geometry.*;
+import com.sun.j3d.internal.FastVector;
+import java.awt.Font;
+import java.awt.font.*;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.AffineTransform;
+import javax.vecmath.*;
+import java.awt.Shape;
+import java.awt.geom.PathIterator;
+import java.util.*;
+
+/**
+ * The Font3D object is used to store extruded 2D glyphs. These
+ * 3D glyphs can then be used to construct Text3D NodeComponent
+ * objects.
+ * <P>
+ * A 3D Font consists of a Java 2D font, a tesellation tolerance,
+ * and an extrusion path. The extrusion
+ * path creates depth by describing how the edge of a glyph varies
+ * in the Z axis.
+ * <P>
+ * The construction of a Text3D object requires a Font3D object.
+ * The Font3D object describes the style of the text, such as its
+ * depth. The text also needs other classes, such as java.awt.Font and
+ * FontExtrusion. The Font object describes the font name (Helvetica,
+ * Courier, etc.), the font style (bold, Italic, etc.), and point
+ * size. The FontExtrusion object extends Font3D by describing
+ * the extrusion path for the Font3D object (how the edge of the
+ * font glyph varies in the Z axis).
+ *<P>
+ * To ensure correct rendering, the 2D Font object should be created
+ * with the default AffineTransform. The point size of the 2D font will
+ * be used as a rough measure of how fine a tesselation to use when
+ * creating the Font3D object: the larger the point size, in
+ * general, the finer the tesselation.
+ * <P>
+ * Custom 3D fonts as well as methods to store 3D fonts
+ * to disk will be addressed in a future release.
+ *
+ * @see java.awt.Font
+ * @see FontExtrusion
+ * @see Text3D
+ */
+public class Font3D extends NodeComponent {
+
+ Font font;
+ double tessellationTolerance;
+ FontExtrusion fontExtrusion;
+ FontRenderContext frc;
+ // Used by triangulateGlyphs method to split contour data into islands.
+ final static float EPS = 0.000001f;
+
+ // Map glyph code to GeometryArrayRetained
+ Hashtable geomHash = new Hashtable(20);
+
+ /**
+ * Constructs a Font3D object from the specified Font and
+ * FontExtrusion objects, using the default value for the
+ * tessellation tolerance. The default value is as follows:
+ *
+ * <ul>
+ * tessellation tolerance : 0.01<br>
+ * </ul>
+ * <P>
+ * The FontExtrusion object contains the extrusion path to use on
+ * the 2D Font glyphs. To ensure correct rendering the font must
+ * be created with the default AffineTransform. Passing null for
+ * the FontExtrusion parameter results in no extrusion being done.
+ *
+ * @param font the Java 2D font used to create the 3D font object
+ * @param extrudePath the extrusion path used to describe how
+ * the edge of the font varies along the Z axis
+ */
+ public Font3D(Font font, FontExtrusion extrudePath) {
+ this(font, 0.01, extrudePath);
+ }
+
+ /**
+ * Constructs a Font3D object from the specified Font and
+ * FontExtrusion objects, using the specified tessellation
+ * tolerance.
+ * The FontExtrusion object contains the extrusion path to use on
+ * the 2D Font glyphs. To ensure correct rendering, the font must
+ * be created with the default AffineTransform. Passing null for
+ * the FontExtrusion parameter results in no extrusion being done.
+ *
+ * @param font the Java 2D font used to create the 3D font object.
+ * @param tessellationTolerance the tessellation tolerance value
+ * used in tessellating the glyphs of the 2D Font.
+ * This corresponds to the <code>flatness</code> parameter in
+ * the <code>java.awt.Shape.getPathIterator</code> method.
+ * @param extrudePath the extrusion path used to describe how
+ * the edge of the font varies along the Z axis.
+ *
+ * @since Java 3D 1.2
+ */
+ public Font3D(Font font,
+ double tessellationTolerance,
+ FontExtrusion extrudePath) {
+
+ this.font = font;
+ this.tessellationTolerance = tessellationTolerance;
+ this.fontExtrusion = extrudePath;
+ this.frc = new FontRenderContext(new AffineTransform(),
+ true, true);
+ }
+
+ /**
+ * Returns the Java 2D Font used to create this Font3D object.
+ * @return Font object used by this Font3D
+ */
+ public Font getFont() {
+ return this.font;
+ }
+
+
+ /**
+ * Returns the tessellation tolerance with which this Font3D was
+ * created.
+ * @return the tessellation tolerance used by this Font3D
+ *
+ * @since Java 3D 1.2
+ */
+ public double getTessellationTolerance() {
+ return tessellationTolerance;
+ }
+
+
+ /**
+ * Copies the FontExtrusion object used to create this Font3D object
+ * into the specified parameter.
+ *
+ * @param extrudePath object that will receive the
+ * FontExtrusion information for this Font3D object
+ */
+ public void getFontExtrusion(FontExtrusion extrudePath) {
+ extrudePath = this.fontExtrusion;
+ }
+
+ /**
+ * Returns the 3D bounding box of the specified glyph code.
+ *
+ * @param glyphCode the glyphCode from the original 2D Font
+ * @param bounds the 3D glyph's bounds
+ */
+ public void getBoundingBox(int glyphCode, BoundingBox bounds){
+ int[] gCodes = {glyphCode};
+ GlyphVector gVec = font.createGlyphVector(frc, gCodes);
+ Rectangle2D.Float bounds2d = (Rectangle2D.Float)
+ (((GlyphMetrics)(gVec.getGlyphMetrics(0))).getBounds2D());
+
+ Point3d lower = new Point3d(bounds2d.x, bounds2d.y, 0.0);
+ Point3d upper;
+ if (fontExtrusion != null) {
+ upper = new Point3d(bounds2d.x + bounds2d.width,
+ bounds2d.y + bounds2d.height,
+ fontExtrusion.length);
+ } else {
+ upper = new Point3d(bounds2d.x + bounds2d.width,
+ bounds2d.y + bounds2d.height,
+ 0.0);
+ }
+ bounds.setLower(lower);
+ bounds.setUpper(upper);
+ }
+
+
+ // Triangulate glyph with 'unicode' if not already done.
+ GeometryArrayRetained triangulateGlyphs(GlyphVector gv, char c) {
+ Character ch = new Character(c);
+ GeometryArrayRetained geo = (GeometryArrayRetained) geomHash.get(ch);
+
+ if (geo == null) {
+ // Font Y-axis is downwards, so send affine transform to flip it.
+ Rectangle2D bnd = gv.getVisualBounds();
+ AffineTransform aTran = new AffineTransform();
+ double tx = bnd.getX() + 0.5 * bnd.getWidth();
+ double ty = bnd.getY() + 0.5 * bnd.getHeight();
+ aTran.setToTranslation(-tx, -ty);
+ aTran.scale(1.0, -1.0);
+ aTran.translate(tx, -ty);
+ Shape shape = gv.getOutline();
+ PathIterator pIt = shape.getPathIterator(aTran, tessellationTolerance);
+ int flag= -1, numContours = 0, numPoints = 0, i, j, k, num=0, vertCnt;
+ UnorderList coords = new UnorderList(100, Point3f.class);
+ float tmpCoords[] = new float[6];
+ float lastX= .0f, lastY= .0f;
+ float firstPntx = Float.MAX_VALUE, firstPnty = Float.MAX_VALUE;
+ GeometryInfo gi = null;
+ NormalGenerator ng = new NormalGenerator();
+ FastVector contours = new FastVector(10);
+ float maxY = -Float.MAX_VALUE;
+ int maxYIndex = 0, beginIdx = 0, endIdx = 0, start = 0;
+
+ boolean setMaxY = false;
+
+
+ while (!pIt.isDone()) {
+ Point3f vertex = new Point3f();
+ flag = pIt.currentSegment(tmpCoords);
+ if (flag == PathIterator.SEG_CLOSE){
+ if (num > 0) {
+ if (setMaxY) {
+ // Get Previous point
+ beginIdx = start;
+ endIdx = numPoints-1;
+ }
+ contours.addElement(num);
+ num = 0;
+ numContours++;
+ }
+ } else if (flag == PathIterator.SEG_MOVETO){
+ vertex.x = tmpCoords[0];
+ vertex.y = tmpCoords[1];
+ lastX = vertex.x;
+ lastY = vertex.y;
+
+ if ((lastX == firstPntx) && (lastY == firstPnty)) {
+ pIt.next();
+ continue;
+ }
+ setMaxY = false;
+ coords.add(vertex);
+ firstPntx = lastX;
+ firstPnty = lastY;
+ if (num> 0){
+ contours.addElement(num);
+ num = 0;
+ numContours++;
+ }
+ num++;
+ numPoints++;
+ // skip checking of first point,
+ // since the last point will repeat this.
+ start = numPoints ;
+ } else if (flag == PathIterator.SEG_LINETO){
+ vertex.x = tmpCoords[0];
+ vertex.y = tmpCoords[1];
+ //Check here for duplicate points. Code
+ //later in this function can not handle
+ //duplicate points.
+
+ if ((vertex.x == lastX) && (vertex.y == lastY)) {
+ pIt.next();
+ continue;
+ }
+ if (vertex.y > maxY) {
+ maxY = vertex.y;
+ maxYIndex = numPoints;
+ setMaxY = true;
+ }
+ lastX = vertex.x;
+ lastY = vertex.y;
+ coords.add(vertex);
+ num++;
+ numPoints++;
+ }
+ pIt.next();
+ }
+
+ // No data(e.g space, control characters)
+ // Two point can't form a valid contour
+ if (numPoints == 0){
+ return null;
+ }
+
+
+
+ // Determine font winding order use for side triangles
+ Point3f p1 = new Point3f(), p2 = new Point3f(), p3 = new Point3f();
+ boolean flip_side_orient = true;
+ Point3f vertices[] = (Point3f []) coords.toArray(false);
+
+ if (endIdx - beginIdx > 0) {
+ // must be true unless it is a single line
+ // define as "MoveTo p1 LineTo p2 Close" which is
+ // not a valid font definition.
+
+ if (maxYIndex == beginIdx) {
+ p1.set(vertices[endIdx]);
+ } else {
+ p1.set(vertices[maxYIndex-1]);
+ }
+ p2.set(vertices[maxYIndex]);
+ if (maxYIndex == endIdx) {
+ p3.set(vertices[beginIdx]);
+ } else {
+ p3.set(vertices[maxYIndex+1]);
+ }
+
+ if (p3.x != p2.x) {
+ if (p1.x != p2.x) {
+ // Use the one with smallest slope
+ if (Math.abs((p2.y - p1.y)/(p2.x - p1.x)) >
+ Math.abs((p3.y - p2.y)/(p3.x - p2.x))) {
+ flip_side_orient = (p3.x > p2.x);
+ } else {
+ flip_side_orient = (p2.x > p1.x);
+ }
+ } else {
+ flip_side_orient = (p3.x > p2.x);
+ }
+ } else {
+ // p1.x != p2.x, otherwise all three
+ // point form a straight vertical line with
+ // the middle point the highest. This is not a
+ // valid font definition.
+ flip_side_orient = (p2.x > p1.x);
+ }
+ }
+
+ // Build a Tree of Islands
+ int startIdx = 0;
+ IslandsNode islandsTree = new IslandsNode(-1, -1);
+ int contourCounts[] = contours.getData();
+
+
+ for (i= 0;i < contours.getSize(); i++) {
+ endIdx = startIdx + contourCounts[i];
+ islandsTree.insert(new IslandsNode(startIdx, endIdx), vertices);
+ startIdx = endIdx;
+ }
+
+ coords = null; // Free memory
+ contours = null;
+ contourCounts = null;
+
+ // Compute islandCounts[][] and outVerts[][]
+ UnorderList islandsList = new UnorderList(10, IslandsNode.class);
+ islandsTree.collectOddLevelNode(islandsList, 0);
+ IslandsNode nodes[] = (IslandsNode []) islandsList.toArray(false);
+ int islandCounts[][] = new int[islandsList.arraySize()][];
+ Point3f outVerts[][] = new Point3f[islandCounts.length][];
+ int nchild, sum;
+ IslandsNode node;
+
+ for (i=0; i < islandCounts.length; i++) {
+ node = nodes[i];
+ nchild = node.numChild();
+ islandCounts[i] = new int[nchild + 1];
+ islandCounts[i][0] = node.numVertices();
+ sum = 0;
+ sum += islandCounts[i][0];
+ for (j=0; j < nchild; j++) {
+ islandCounts[i][j+1] = node.getChild(j).numVertices();
+ sum += islandCounts[i][j+1];
+ }
+ outVerts[i] = new Point3f[sum];
+ startIdx = 0;
+ for (k=node.startIdx; k < node.endIdx; k++) {
+ outVerts[i][startIdx++] = vertices[k];
+ }
+
+ for (j=0; j < nchild; j++) {
+ endIdx = node.getChild(j).endIdx;
+ for (k=node.getChild(j).startIdx; k < endIdx; k++) {
+ outVerts[i][startIdx++] = vertices[k];
+ }
+ }
+ }
+
+
+
+ islandsTree = null; // Free memory
+ islandsList = null;
+ vertices = null;
+
+ contourCounts = new int[1];
+ int currCoordIndex = 0, vertOffset = 0;
+ ArrayList triangData = new ArrayList();
+
+ Point3f q1 = new Point3f(), q2 = new Point3f(), q3 = new Point3f();
+ Vector3f n1 = new Vector3f(), n2 = new Vector3f();
+ numPoints = 0;
+ //Now loop thru each island, calling triangulator once per island.
+ //Combine triangle data for all islands together in one object.
+ for (i=0;i < islandCounts.length;i++) {
+ contourCounts[0] = islandCounts[i].length;
+ numPoints += outVerts[i].length;
+ gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
+ gi.setCoordinates(outVerts[i]);
+ gi.setStripCounts(islandCounts[i]);
+ gi.setContourCounts(contourCounts);
+ ng.generateNormals(gi);
+
+ GeometryArray ga = gi.getGeometryArray(false, false, false);
+ vertOffset += ga.getVertexCount();
+
+ triangData.add(ga);
+ }
+ // Multiply by 2 since we create 2 faces of the font
+ // Second term is for side-faces along depth of the font
+ if (fontExtrusion == null)
+ vertCnt = vertOffset;
+ else{
+ if (fontExtrusion.shape == null)
+ vertCnt = vertOffset * 2 + numPoints *6;
+ else{
+ vertCnt = vertOffset * 2 + numPoints * 6 *
+ (fontExtrusion.pnts.length -1);
+ }
+ }
+
+ // TODO: Should use IndexedTriangleArray to avoid
+ // duplication of vertices. To create triangles for
+ // side faces, every vertex is duplicated currently.
+ TriangleArray triAry = new TriangleArray(vertCnt,
+ GeometryArray.COORDINATES |
+ GeometryArray.NORMALS);
+
+ boolean flip_orient[] = new boolean[islandCounts.length];
+ boolean findOrient;
+ // last known non-degenerate normal
+ Vector3f goodNormal = new Vector3f();
+
+
+ for (j=0;j < islandCounts.length;j++) {
+ GeometryArray ga = (GeometryArray)triangData.get(j);
+ vertOffset = ga.getVertexCount();
+
+ findOrient = false;
+
+ //Create the triangle array
+ for (i= 0; i < vertOffset; i+= 3, currCoordIndex += 3){
+ //Get 3 points. Since triangle is known to be flat, normal
+ // must be same for all 3 points.
+ ga.getCoordinate(i, p1);
+ ga.getNormal(i, n1);
+ ga.getCoordinate(i+1, p2);
+ ga.getCoordinate(i+2, p3);
+
+ if (!findOrient) {
+ //Check here if triangles are wound incorrectly and need
+ //to be flipped.
+ if (!getNormal(p1,p2, p3, n2)) {
+ continue;
+ }
+
+ if (n2.z >= EPS) {
+ flip_orient[j] = false;
+ } else if (n2.z <= -EPS) {
+ flip_orient[j] = true;
+ } else {
+ continue;
+ }
+ findOrient = true;
+ }
+ if (flip_orient[j]){
+ //New Triangulator preserves contour orientation. If contour
+ //input is wound incorrectly, swap 2nd and 3rd points to
+ //sure all triangles are wound correctly for j3d.
+ q1.x = p2.x; q1.y = p2.y; q1.z = p2.z;
+ p2.x = p3.x; p2.y = p3.y; p2.z = p3.z;
+ p3.x = q1.x; p3.y = q1.y; p3.z = q1.z;
+ n1.x = -n1.x; n1.y = -n1.y; n1.z = -n1.z;
+ }
+
+
+ if (fontExtrusion != null) {
+ n2.x = -n1.x;n2.y = -n1.y;n2.z = -n1.z;
+
+ triAry.setCoordinate(currCoordIndex, p1);
+ triAry.setNormal(currCoordIndex, n2);
+ triAry.setCoordinate(currCoordIndex+1, p3);
+ triAry.setNormal(currCoordIndex+1, n2);
+ triAry.setCoordinate(currCoordIndex+2, p2);
+ triAry.setNormal(currCoordIndex+2, n2);
+
+ q1.x = p1.x; q1.y = p1.y; q1.z = p1.z + fontExtrusion.length;
+ q2.x = p2.x; q2.y = p2.y; q2.z = p2.z + fontExtrusion.length;
+ q3.x = p3.x; q3.y = p3.y; q3.z = p3.z + fontExtrusion.length;
+
+ triAry.setCoordinate(currCoordIndex+vertOffset, q1);
+ triAry.setNormal(currCoordIndex+vertOffset, n1);
+ triAry.setCoordinate(currCoordIndex+1+vertOffset, q2);
+ triAry.setNormal(currCoordIndex+1+vertOffset, n1);
+ triAry.setCoordinate(currCoordIndex+2+vertOffset, q3);
+ triAry.setNormal(currCoordIndex+2+vertOffset, n1);
+ } else {
+ triAry.setCoordinate(currCoordIndex, p1);
+ triAry.setNormal(currCoordIndex, n1);
+ triAry.setCoordinate(currCoordIndex+1, p2);
+ triAry.setNormal(currCoordIndex+1, n1);
+ triAry.setCoordinate(currCoordIndex+2, p3);
+ triAry.setNormal(currCoordIndex+2, n1);
+ }
+
+ }
+ if (fontExtrusion != null) {
+ currCoordIndex += vertOffset;
+ }
+ }
+
+ //Now add side triangles in both cases.
+
+ // Since we duplicated triangles with different Z, make sure
+ // currCoordIndex points to correct location.
+ if (fontExtrusion != null){
+ if (fontExtrusion.shape == null){
+ boolean smooth;
+ // we'll put a crease if the angle between the normals is
+ // greater than 44 degrees
+ float threshold = (float) Math.cos(44.0*Math.PI/180.0);
+ float cosine;
+ // need the previous normals to check for smoothing
+ Vector3f pn1 = null, pn2 = null;
+ // need the next normals to check for smoothing
+ Vector3f n3 = new Vector3f(), n4 = new Vector3f();
+ // store the normals for each point because they are
+ // the same for both triangles
+ Vector3f p1Normal = new Vector3f();
+ Vector3f p2Normal = new Vector3f();
+ Vector3f p3Normal = new Vector3f();
+ Vector3f q1Normal = new Vector3f();
+ Vector3f q2Normal = new Vector3f();
+ Vector3f q3Normal = new Vector3f();
+
+ for (i=0;i < islandCounts.length;i++){
+ for (j=0, k=0, num =0;j < islandCounts[i].length;j++){
+ num += islandCounts[i][j];
+ p1.x = outVerts[i][num - 1].x;
+ p1.y = outVerts[i][num - 1].y;
+ p1.z = 0.0f;
+ q1.x = p1.x; q1.y = p1.y; q1.z = p1.z+fontExtrusion.length;
+ p2.z = 0.0f;
+ q2.z = p2.z+fontExtrusion.length;
+ for (int m=0; m < num;m++) {
+ p2.x = outVerts[i][m].x;
+ p2.y = outVerts[i][m].y;
+ q2.x = p2.x;
+ q2.y = p2.y;
+ if (getNormal(p1, q1, p2, n1)) {
+
+ if (!flip_side_orient) {
+ n1.negate();
+ }
+ goodNormal.set(n1);
+ break;
+ }
+ }
+
+ for (;k < num;k++){
+ p2.x = outVerts[i][k].x;p2.y = outVerts[i][k].y;p2.z = 0.0f;
+ q2.x = p2.x; q2.y = p2.y; q2.z = p2.z+fontExtrusion.length;
+
+ if (!getNormal(p1, q1, p2, n1)) {
+ n1.set(goodNormal);
+ } else {
+ if (!flip_side_orient) {
+ n1.negate();
+ }
+ goodNormal.set(n1);
+ }
+
+ if (!getNormal(p2, q1, q2, n2)) {
+ n2.set(goodNormal);
+ } else {
+ if (!flip_side_orient) {
+ n2.negate();
+ }
+ goodNormal.set(n2);
+ }
+ // if there is a previous normal, see if we need to smooth
+ // this normal or make a crease
+
+ if (pn1 != null) {
+ cosine = n1.dot(pn2);
+ smooth = cosine > threshold;
+ if (smooth) {
+ p1Normal.x = (pn1.x + pn2.x + n1.x);
+ p1Normal.y = (pn1.y + pn2.y + n1.y);
+ p1Normal.z = (pn1.z + pn2.z + n1.z);
+ normalize(p1Normal);
+
+ q1Normal.x = (pn2.x + n1.x + n2.x);
+ q1Normal.y = (pn2.y + n1.y + n2.y);
+ q1Normal.z = (pn2.z + n1.z + n2.z);
+ normalize(q1Normal);
+ } // if smooth
+ else {
+ p1Normal.x = n1.x; p1Normal.y = n1.y; p1Normal.z = n1.z;
+ q1Normal.x = n1.x+n2.x;
+ q1Normal.y = n1.y+n2.y;
+ q1Normal.z = n1.z+ n2.z;
+ normalize(q1Normal);
+ } // else
+ } // if pn1 != null
+ else {
+ pn1 = new Vector3f();
+ pn2 = new Vector3f();
+ p1Normal.x = n1.x;
+ p1Normal.y = n1.y;
+ p1Normal.z = n1.z;
+
+ q1Normal.x = (n1.x + n2.x);
+ q1Normal.y = (n1.y + n2.y);
+ q1Normal.z = (n1.z + n2.z);
+ normalize(q1Normal);
+ } // else
+
+ // if there is a next, check if we should smooth normal
+
+ if (k+1 < num) {
+ p3.x = outVerts[i][k+1].x; p3.y = outVerts[i][k+1].y;
+ p3.z = 0.0f;
+ q3.x = p3.x; q3.y = p3.y; q3.z = p3.z + fontExtrusion.length;
+
+ if (!getNormal(p2, q2, p3, n3)) {
+ n3.set(goodNormal);
+ } else {
+ if (!flip_side_orient) {
+ n3.negate();
+ }
+ goodNormal.set(n3);
+ }
+
+ if (!getNormal(p3, q2, q3, n4)) {
+ n4.set(goodNormal);
+ } else {
+ if (!flip_side_orient) {
+ n4.negate();
+ }
+ goodNormal.set(n4);
+ }
+
+ cosine = n2.dot(n3);
+ smooth = cosine > threshold;
+
+ if (smooth) {
+ p2Normal.x = (n1.x + n2.x + n3.x);
+ p2Normal.y = (n1.y + n2.y + n3.y);
+ p2Normal.z = (n1.z + n2.z + n3.z);
+ normalize(p2Normal);
+
+ q2Normal.x = (n2.x + n3.x + n4.x);
+ q2Normal.y = (n2.y + n3.y + n4.y);
+ q2Normal.z = (n2.z + n3.z + n4.z);
+ normalize(q2Normal);
+ } else { // if smooth
+ p2Normal.x = n1.x + n2.x;
+ p2Normal.y = n1.y + n2.y;
+ p2Normal.z = n1.z + n2.z;
+ normalize(p2Normal);
+ q2Normal.x = n2.x; q2Normal.y = n2.y; q2Normal.z = n2.z;
+ } // else
+ } else { // if k+1 < num
+ p2Normal.x = (n1.x + n2.x);
+ p2Normal.y = (n1.y + n2.y);
+ p2Normal.z = (n1.z + n2.z);
+ normalize(p2Normal);
+
+ q2Normal.x = n2.x;
+ q2Normal.y = n2.y;
+ q2Normal.z = n2.z;
+ } // else
+
+ // add pts for the 2 tris
+ // p1, q1, p2 and p2, q1, q2
+
+ if (flip_side_orient) {
+ triAry.setCoordinate(currCoordIndex, p1);
+ triAry.setNormal(currCoordIndex, p1Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, q1Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, p2);
+ triAry.setNormal(currCoordIndex, p2Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, p2);
+ triAry.setNormal(currCoordIndex, p2Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, q1Normal);
+ currCoordIndex++;
+ } else {
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, q1Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, p1);
+ triAry.setNormal(currCoordIndex, p1Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, p2);
+ triAry.setNormal(currCoordIndex, p2Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, q1Normal);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, p2);
+ triAry.setNormal(currCoordIndex, p2Normal);
+ currCoordIndex++;
+ }
+ triAry.setCoordinate(currCoordIndex, q2);
+ triAry.setNormal(currCoordIndex, q2Normal);
+ currCoordIndex++;
+ pn1.x = n1.x; pn1.y = n1.y; pn1.z = n1.z;
+ pn2.x = n2.x; pn2.y = n2.y; pn2.z = n2.z;
+ p1.x = p2.x; p1.y = p2.y; p1.z = p2.z;
+ q1.x = q2.x; q1.y = q2.y; q1.z = q2.z;
+
+ }// for k
+
+ // set the previous normals to null when we are done
+ pn1 = null;
+ pn2 = null;
+ }// for j
+ }//for i
+ } else { // if shape
+ int m, offset=0;
+ Point3f P2 = new Point3f(), Q2 = new Point3f(), P1=new Point3f();
+ Vector3f nn = new Vector3f(), nn1= new Vector3f(),
+ nn2= new Vector3f(), nn3= new Vector3f();
+ Vector3f nna = new Vector3f(), nnb=new Vector3f();
+ float length;
+ boolean validNormal = false;
+
+ // fontExtrusion.shape is specified, and is NOT straight line
+ for (i=0;i < islandCounts.length;i++){
+ for (j=0, k= 0, offset = num =0;j < islandCounts[i].length;j++){
+ num += islandCounts[i][j];
+
+ p1.x = outVerts[i][num - 1].x;
+ p1.y = outVerts[i][num - 1].y;
+ p1.z = 0.0f;
+ q1.x = p1.x; q1.y = p1.y; q1.z = p1.z+fontExtrusion.length;
+ p3.z = 0.0f;
+ for (m=num-2; m >= 0; m--) {
+ p3.x = outVerts[i][m].x;
+ p3.y = outVerts[i][m].y;
+
+ if (getNormal(p3, q1, p1, nn1)) {
+ if (!flip_side_orient) {
+ nn1.negate();
+ }
+ goodNormal.set(nn1);
+ break;
+ }
+ }
+ for (;k < num;k++){
+ p2.x = outVerts[i][k].x;p2.y = outVerts[i][k].y;p2.z = 0.0f;
+ q2.x = p2.x; q2.y = p2.y; q2.z = p2.z+fontExtrusion.length;
+ getNormal(p1, q1, p2, nn2);
+
+ p3.x = outVerts[i][(k+1)==num ? offset:(k+1)].x;
+ p3.y = outVerts[i][(k+1)==num ? offset:(k+1)].y;
+ p3.z = 0.0f;
+ if (!getNormal(p3,p2,q2, nn3)) {
+ nn3.set(goodNormal);
+ } else {
+ if (!flip_side_orient) {
+ nn3.negate();
+ }
+ goodNormal.set(nn3);
+ }
+
+ // Calculate normals at the point by averaging normals
+ // of two faces on each side of the point.
+ nna.x = (nn1.x+nn2.x);
+ nna.y = (nn1.y+nn2.y);
+ nna.z = (nn1.z+nn2.z);
+ normalize(nna);
+
+ nnb.x = (nn3.x+nn2.x);
+ nnb.y = (nn3.y+nn2.y);
+ nnb.z = (nn3.z+nn2.z);
+ normalize(nnb);
+
+ P1.x = p1.x;P1.y = p1.y;P1.z = p1.z;
+ P2.x = p2.x;P2.y = p2.y; P2.z = p2.z;
+ Q2.x = q2.x;Q2.y = q2.y; Q2.z = q2.z;
+ for (m=1;m < fontExtrusion.pnts.length;m++){
+ q1.z = q2.z = fontExtrusion.pnts[m].x;
+ q1.x = P1.x + nna.x * fontExtrusion.pnts[m].y;
+ q1.y = P1.y + nna.y * fontExtrusion.pnts[m].y;
+ q2.x = P2.x + nnb.x * fontExtrusion.pnts[m].y;
+ q2.y = P2.y + nnb.y * fontExtrusion.pnts[m].y;
+
+ if (!getNormal(p1, q1, p2, n1)) {
+ n1.set(goodNormal);
+ } else {
+ if (!flip_side_orient) {
+ n1.negate();
+ }
+ goodNormal.set(n1);
+ }
+
+ if (flip_side_orient) {
+ triAry.setCoordinate(currCoordIndex, p1);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+ } else {
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, p1);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+ }
+ triAry.setCoordinate(currCoordIndex, p2);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+
+ if (!getNormal(p2, q1, q2, n1)) {
+ n1.set(goodNormal);
+ } else {
+ if (!flip_side_orient) {
+ n1.negate();
+ }
+ goodNormal.set(n1);
+ }
+
+ if (flip_side_orient) {
+ triAry.setCoordinate(currCoordIndex, p2);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+ } else {
+ triAry.setCoordinate(currCoordIndex, q1);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+
+ triAry.setCoordinate(currCoordIndex, p2);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+ }
+ triAry.setCoordinate(currCoordIndex, q2);
+ triAry.setNormal(currCoordIndex, n1);
+ currCoordIndex++;
+
+ p1.x = q1.x;p1.y = q1.y;p1.z = q1.z;
+ p2.x = q2.x;p2.y = q2.y;p2.z = q2.z;
+ }// for m
+ p1.x = P2.x; p1.y = P2.y; p1.z = P2.z;
+ q1.x = Q2.x; q1.y = Q2.y; q1.z = Q2.z;
+ nn1.x = nn2.x;nn1.y = nn2.y;nn1.z = nn2.z;
+ }// for k
+ offset = num;
+ }// for j
+ }//for i
+ }// if shape
+ }// if fontExtrusion
+ geo = (GeometryArrayRetained) triAry.retained;
+ geomHash.put(ch, geo);
+ }
+
+ return geo;
+ }
+
+
+ static boolean getNormal(Point3f p1, Point3f p2, Point3f p3, Vector3f normal) {
+ Vector3f v1 = new Vector3f();
+ Vector3f v2 = new Vector3f();
+
+ // Must compute normal
+ v1.sub(p2, p1);
+ v2.sub(p2, p3);
+ normal.cross(v1, v2);
+ normal.negate();
+
+ float length = normal.length();
+
+ if (length > 0) {
+ length = 1 / length;
+ normal.x *= length;
+ normal.y *= length;
+ normal.z *= length;
+ return true;
+ }
+ return false;
+ }
+
+
+ // check if 2 contours are inside/outside/intersect one another
+ // INPUT:
+ // vertCnt1, vertCnt2 - number of vertices in 2 contours
+ // begin1, begin2 - starting indices into vertices for 2 contours
+ // vertices - actual vertex data
+ // OUTPUT:
+ // status == 1 - intersecting contours
+ // 2 - first contour inside the second
+ // 3 - second contour inside the first
+ // 0 - disjoint contours(2 islands)
+
+ static int check2Contours(int begin1, int end1, int begin2, int end2,
+ Point3f[] vertices) {
+ int i, j;
+ boolean inside2, inside1;
+
+ inside2 = pointInPolygon2D(vertices[begin1].x, vertices[begin1].y,
+ begin2, end2, vertices);
+
+ for (i=begin1+1; i < end1;i++) {
+ if (pointInPolygon2D(vertices[i].x, vertices[i].y,
+ begin2, end2, vertices) != inside2) {
+ return 1; //intersecting contours
+ }
+ }
+
+ // Since we are using point in polygon test and not
+ // line in polygon test. There are cases we miss the interesting
+ // if we are not checking the reverse for all points. This happen
+ // when two points form a line pass through a polygon but the two
+ // points are outside of it.
+
+ inside1 = pointInPolygon2D(vertices[begin2].x, vertices[begin2].y,
+ begin1, end1, vertices);
+
+ for (i=begin2+1; i < end2;i++) {
+ if (pointInPolygon2D(vertices[i].x, vertices[i].y,
+ begin1, end1, vertices) != inside1) {
+ return 1; //intersecting contours
+ }
+ }
+
+ if (!inside2) {
+ if (!inside1) {
+ return 0; // disjoint countours
+ }
+ // inside2 = false and inside1 = true
+ return 3; // second contour inside first
+ }
+
+ // must be inside2 = true and inside1 = false
+ // Note that it is not possible inside2 = inside1 = true
+ // unless two contour overlap to each others.
+ //
+ return 2; // first contour inside second
+ }
+
+ // Test if 2D point (x,y) lies inside polygon represented by verts.
+ // z-value of polygon vertices is ignored. Sent only to avoid data-copy.
+ // Uses ray-shooting algorithm to compute intersections along +X axis.
+ // This algorithm works for all polygons(concave, self-intersecting) and
+ // is best solution here due to large number of polygon vertices.
+ // Point is INSIDE if number of intersections is odd, OUTSIDE if number
+ // of intersections is even.
+ static boolean pointInPolygon2D(float x, float y, int begIdx, int endIdx,
+ Point3f[] verts){
+
+ int i, num_intersections = 0;
+ float xi;
+
+ for (i=begIdx;i < endIdx-1;i++) {
+ if ((verts[i].y >= y && verts[i+1].y >= y) ||
+ (verts[i].y < y && verts[i+1].y < y))
+ continue;
+
+ xi = verts[i].x + (verts[i].x - verts[i+1].x)*(y - verts[i].y)/
+ (verts[i].y - verts[i+1].y);
+
+ if (x < xi) num_intersections++;
+ }
+
+ // Check for segment from last vertex to first vertex.
+
+ if (!((verts[i].y >= y && verts[begIdx].y >= y) ||
+ (verts[i].y < y && verts[begIdx].y < y))) {
+ xi = verts[i].x + (verts[i].x - verts[begIdx].x)*(y - verts[i].y)/
+ (verts[i].y - verts[begIdx].y);
+
+ if (x < xi) num_intersections++;
+ }
+
+ return ((num_intersections % 2) != 0);
+ }
+
+
+ static final boolean normalize(Vector3f v) {
+ float len = v.length();
+
+ if (len > 0) {
+ len = 1.0f/len;
+ v.x *= len;
+ v.y *= len;
+ v.z *= len;
+ return true;
+ }
+ return false;
+ }
+
+
+ // A Tree of islands form based on contour, each parent's contour
+ // enclosed all the child. We built this since Triangular fail to
+ // handle the case of multiple concentrated contours. i.e. if
+ // 4 contours A > B > C > D. Triangular will fail recongized
+ // two island, one form by A & B and the other by C & D.
+ // Using this tree we can separate out every 2 levels and pass
+ // in to triangular to workaround its limitation.
+ static private class IslandsNode {
+
+ private ArrayList islandsList = null;
+ int startIdx, endIdx;
+
+ IslandsNode(int startIdx, int endIdx) {
+ this.startIdx = startIdx;
+ this.endIdx = endIdx;
+ islandsList = null;
+ }
+
+ void addChild(IslandsNode node) {
+
+ if (islandsList == null) {
+ islandsList = new ArrayList(5);
+ }
+ islandsList.add(node);
+ }
+
+ void removeChild(IslandsNode node) {
+ islandsList.remove(islandsList.indexOf(node));
+ }
+
+ IslandsNode getChild(int idx) {
+ return (IslandsNode) islandsList.get(idx);
+ }
+
+ int numChild() {
+ return (islandsList == null ? 0 : islandsList.size());
+ }
+
+ int numVertices() {
+ return endIdx - startIdx;
+ }
+
+ void insert(IslandsNode newNode, Point3f[] vertices) {
+ boolean createNewLevel = false;
+
+ if (islandsList != null) {
+ IslandsNode childNode;
+ int status;
+
+ for (int i=numChild()-1; i>=0; i--) {
+ childNode = getChild(i);
+ status = check2Contours(newNode.startIdx, newNode.endIdx,
+ childNode.startIdx, childNode.endIdx,
+ vertices);
+ switch (status) {
+ case 2: // newNode inside childNode, go down recursively
+ childNode.insert(newNode, vertices);
+ return;
+ case 3:// childNode inside newNode,
+ // continue to search other childNode also
+ // inside this one and group them together.
+ newNode.addChild(childNode);
+ createNewLevel = true;
+ break;
+ default: // intersecting or disjoint
+
+ }
+ }
+ }
+
+ if (createNewLevel) {
+ // Remove child in newNode from this
+ for (int i=newNode.numChild()-1; i>=0; i--) {
+ removeChild(newNode.getChild(i));
+ }
+ // Add the newNode to parent
+ }
+ addChild(newNode);
+ }
+
+ // Return a list of node with odd number of level
+ void collectOddLevelNode(UnorderList list, int level) {
+ if ((level % 2) == 1) {
+ list.add(this);
+ }
+ if (islandsList != null) {
+ level++;
+ for (int i=numChild()-1; i>=0; i--) {
+ getChild(i).collectOddLevelNode(list, level);
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/FontExtrusion.java b/src/classes/share/javax/media/j3d/FontExtrusion.java
new file mode 100644
index 0000000..a2d5a5c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/FontExtrusion.java
@@ -0,0 +1,241 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.*;
+import java.lang.Math;
+import java.awt.Shape;
+import java.awt.geom.PathIterator;
+import java.util.ArrayList;
+
+ /**
+ * The FontExtrusion object is used to describe the extrusion path
+ * for a Font3D object. The extrusion path is used in conjunction
+ * with a Font2D object. The extrusion path defines the edge contour
+ * of 3D text. This contour is perpendicular to the face of the text.
+ * The extrusion has it's origin at the edge of the glyph with 1.0 being
+ * the height of the tallest glyph. Contour must be monotonic in x.
+ * <P>
+ * The shape of the extrusion path is, by default, a straight line
+ * from 0.0 to 0.2 (known as a straight bevel). The shape may be
+ * modified via the extrusionShape parameter, a Shape object that
+ * describes the 3D contour of a Font3D object.
+ * <P>
+ * User is responsible for data sanity and must make sure that
+ * extrusionShape does not cause intersection of adjacent glyphs
+ * or within single glyph. Else undefined output may be generated.
+ *
+ * @see java.awt.Font
+ * @see Font3D
+ */
+public class FontExtrusion extends Object {
+
+ // Default FontExtrusion is a straight line of length .2
+ float length = 0.2f;
+ Shape shape;
+ Point2f [] pnts;
+
+ double tessellationTolerance = 0.01;
+
+ /**
+ * Constructs a FontExtrusion object with default parameters. The
+ * default parameters are as follows:
+ *
+ * <ul>
+ * extrusion shape : null<br>
+ * tessellation tolerance : 0.01<br>
+ * </ul>
+ *
+ * A null extrusion shape specifies that a straight line from 0.0
+ * to 0.2 (straight bevel) is used.
+ *
+ * @see Font3D
+ */
+ public FontExtrusion() {
+ shape = null;
+ }
+
+ /**
+ * Constructs a FontExtrusion object with the specified shape, using
+ * the default tessellation tolerance. The
+ * specified shape is used to construct the edge
+ * contour of a Font3D object. Each shape begins with an implicit
+ * point at 0.0. Contour must be monotonic in x.
+ *
+ * @param extrusionShape the shape object to use to generate the
+ * extrusion path.
+ * A null shape specifies that a straight line from 0.0 to 0.2
+ * (straight bevel) is used.
+ *
+ * @exception IllegalArgumentException if multiple contours in
+ * extrusionShape, or contour is not monotonic or least x-value
+ * of a contour point is not 0.0f
+ *
+ * @see Font3D
+ */
+ public FontExtrusion(Shape extrusionShape) {
+ setExtrusionShape(extrusionShape);
+ }
+
+
+ /**
+ * Constructs a FontExtrusion object with the specified shape, using
+ * the specified tessellation tolerance. The
+ * specified shape is used to construct the edge
+ * contour of a Font3D object. Each shape begins with an implicit
+ * point at 0.0. Contour must be monotonic in x.
+ *
+ * @param extrusionShape the shape object to use to generate the
+ * extrusion path.
+ * A null shape specifies that a straight line from 0.0 to 0.2
+ * (straight bevel) is used.
+ * @param tessellationTolerance the tessellation tolerance value
+ * used in tessellating the extrusion shape.
+ * This corresponds to the <code>flatness</code> parameter in
+ * the <code>java.awt.Shape.getPathIterator</code> method.
+ *
+ * @exception IllegalArgumentException if multiple contours in
+ * extrusionShape, or contour is not monotonic or least x-value
+ * of a contour point is not 0.0f
+ *
+ * @see Font3D
+ *
+ * @since Java 3D 1.2
+ */
+ public FontExtrusion(Shape extrusionShape,
+ double tessellationTolerance) {
+
+ this.tessellationTolerance = tessellationTolerance;
+ setExtrusionShape(extrusionShape);
+ }
+
+
+ /**
+ * Sets the FontExtrusion's shape parameter. This
+ * parameter is used to construct the 3D contour of a Font3D object.
+ *
+ * @param extrusionShape the shape object to use to generate the
+ * extrusion path.
+ * A null shape specifies that a straight line from 0.0 to 0.2
+ * (straight bevel) is used.
+ *
+ * @exception IllegalArgumentException if multiple contours in
+ * extrusionShape, or contour is not monotonic or least x-value
+ * of a contour point is not 0.0f
+ *
+ * @see Font3D
+ * @see java.awt.Shape
+ */
+ public void setExtrusionShape(Shape extrusionShape) {
+ shape = extrusionShape;
+ if (shape == null) return;
+
+ PathIterator pIt = shape.getPathIterator(null, tessellationTolerance);
+ ArrayList coords = new ArrayList();
+ float tmpCoords[] = new float[6], prevX = 0.0f;
+ int flag, n = 0, inc = -1;
+
+ // Extrusion shape is restricted to be single contour, monotonous
+ // increasing, non-self-intersecting curve. Throw exception otherwise
+ while (!pIt.isDone()) {
+ Point2f vertex = new Point2f();
+ flag = pIt.currentSegment(tmpCoords);
+ if (flag == PathIterator.SEG_LINETO){
+ vertex.x = tmpCoords[0];
+ vertex.y = tmpCoords[1];
+ if (inc == -1){
+ if (prevX < vertex.x) inc = 0;
+ else if (prevX > vertex.x) inc = 1;
+ }
+ //Flag 'inc' indicates if curve is monotonic increasing or
+ // monotonic decreasing. It is set to -1 initially and remains
+ // -1 if consecutive x values are same. Once 'inc' is set to
+ // 1 or 0, exception is thrown is curve changes direction.
+ if (((inc == 0) && (prevX > vertex.x)) ||
+ ((inc == 1) && (prevX < vertex.x)))
+ throw new IllegalArgumentException(J3dI18N.getString("FontExtrusion0"));
+
+ prevX = vertex.x;
+ n++;
+ coords.add(vertex);
+ }else if (flag == PathIterator.SEG_MOVETO){
+ if (n != 0)
+ throw new IllegalArgumentException(J3dI18N.getString("FontExtrusion3"));
+
+ vertex.x = tmpCoords[0];
+ vertex.y = tmpCoords[1];
+ prevX = vertex.x;
+ n++;
+ coords.add(vertex);
+ }
+ pIt.next();
+ }
+
+ //if (inc == 1){
+ //Point2f vertex = new Point2f(0.0f, 0.0f);
+ //coords.add(vertex);
+ //}
+ int i, num = coords.size();
+ pnts = new Point2f[num];
+ //System.out.println("num "+num+" inc "+inc);
+ if (inc == 0){
+ for (i=0;i < num;i++){
+ pnts[i] = (Point2f)coords.get(i);
+ //System.out.println("i "+i+" x "+ pnts[i].x+" y "+pnts[i].y);
+ }
+ }
+ else {
+ for (i=0;i < num;i++) {
+ pnts[i] = (Point2f)coords.get(num - i -1);
+ //System.out.println("i "+i+" x "+ pnts[i].x+" y "+pnts[i].y);
+ }
+ }
+
+ //Force last y to be zero until Text3D face scaling is implemented
+ pnts[num-1].y = 0.0f;
+ if (pnts[0].x != 0.0f)
+ throw new IllegalArgumentException(J3dI18N.getString("FontExtrusion1"));
+
+ //Compute straight line distance between first and last points.
+ float dx = (pnts[0].x - pnts[num-1].x);
+ float dy = (pnts[0].y - pnts[num-1].y);
+ length = (float)Math.sqrt(dx*dx + dy*dy);
+ }
+
+
+ /**
+ * Gets the FontExtrusion's shape parameter. This
+ * parameter is used to construct the 3D contour of a Font3D object.
+ *
+ * @return extrusionShape the shape object used to generate the
+ * extrusion path
+ *
+ * @see Font3D
+ * @see java.awt.Shape
+ */
+ public Shape getExtrusionShape() {
+ return shape;
+ }
+
+
+ /**
+ * Returns the tessellation tolerance with which this FontExtrusion was
+ * created.
+ * @return the tessellation tolerance used by this FontExtrusion
+ *
+ * @since Java 3D 1.2
+ */
+ public double getTessellationTolerance() {
+ return tessellationTolerance;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/FreeListManager.java b/src/classes/share/javax/media/j3d/FreeListManager.java
new file mode 100644
index 0000000..b923af2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/FreeListManager.java
@@ -0,0 +1,98 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+class FreeListManager {
+
+ private static final boolean DEBUG = false;
+
+ // constants that represent the freelists managed by the Manager
+ static final int MESSAGE = 0;
+ static final int BHLEAF = 1;
+ static final int TRANSFORM3D = 2;
+ static final int BHINTERNAL = 3;
+ static final int DISPLAYLIST = 4;
+ static final int TEXTURE2D = 5;
+ static final int TEXTURE3D = 6;
+ static final int CANVASBIT = 7;
+ static final int VECTOR3D = 8;
+ static final int POINT3D = 9;
+ static int MAXINT = 9;
+
+ // what list we are going to shrink next
+ private static int currlist = 0;
+
+ // the freelists managed by the manager
+ static MemoryFreeList[] freelist = new MemoryFreeList[MAXINT+1];
+
+ static void createFreeLists() {
+ freelist[MESSAGE] = new MemoryFreeList("javax.media.j3d.J3dMessage");
+ freelist[BHLEAF] = new MemoryFreeList("javax.media.j3d.BHLeafNode");
+ freelist[TRANSFORM3D] = new MemoryFreeList("javax.media.j3d.Transform3D");
+ freelist[BHINTERNAL] = new MemoryFreeList("javax.media.j3d.BHInternalNode");
+ freelist[DISPLAYLIST] = new IntegerFreeList();
+ freelist[TEXTURE2D] = new IntegerFreeList();
+ freelist[TEXTURE3D] = new IntegerFreeList();
+ freelist[CANVASBIT] = new IntegerFreeList();
+ freelist[POINT3D] = new MemoryFreeList("javax.vecmath.Point3d");
+ freelist[VECTOR3D] = new MemoryFreeList("javax.vecmath.Vector3d");
+ }
+
+ // allows list to be created. The listId for the new list is returned.
+ static int createNewFreeList(String className) {
+ MAXINT++;
+ MemoryFreeList[] temp = freelist;
+ freelist = new MemoryFreeList[MAXINT+1];
+ System.arraycopy(temp, 0, freelist, 0, MAXINT+1);
+ freelist[MAXINT] = new MemoryFreeList(className);
+ return MAXINT;
+ }
+
+
+ // 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];
+ }
+ }
+
+ static Object getObject(int listId) {
+ return freelist[listId].getObject();
+ }
+
+ static void freeObject(int listId, Object obj) {
+ freelist[listId].add(obj);
+ }
+
+ static void clearList(int listId) {
+ freelist[listId].clear();
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/GeneralizedStrip.java b/src/classes/share/javax/media/j3d/GeneralizedStrip.java
new file mode 100644
index 0000000..85ac1b6
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeneralizedStrip.java
@@ -0,0 +1,875 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.* ;
+
+
+/**
+ * This class provides static methods to support topological
+ * transformations on generalized strips. This is used by the
+ * GeometryDecompressor. These methods only need to look at the
+ * vertex replacement flags to determine how the vertices in the strip
+ * are connected. The connections are rearranged in different ways to
+ * transform generalized strips to GeometryArray representations.
+ *
+ * @see GeneralizedStripFlags
+ * @see GeneralizedVertexList
+ * @see GeometryDecompressor
+ */
+class GeneralizedStrip {
+ private static final boolean debug = false ;
+
+ // Private convenience copies of various constants.
+ private static final int CW =
+ GeneralizedStripFlags.FRONTFACE_CW ;
+ private static final int CCW =
+ GeneralizedStripFlags.FRONTFACE_CCW ;
+ private static final int RESTART_CW =
+ GeneralizedStripFlags.RESTART_CW ;
+ private static final int RESTART_CCW =
+ GeneralizedStripFlags.RESTART_CCW ;
+ private static final int REPLACE_MIDDLE =
+ GeneralizedStripFlags.REPLACE_MIDDLE ;
+ private static final int REPLACE_OLDEST =
+ GeneralizedStripFlags.REPLACE_OLDEST ;
+
+ /**
+ * The IntList is like an ArrayList, but avoids the Integer
+ * object wrapper and accessor overhead for simple lists of ints.
+ */
+ static class IntList {
+ /**
+ * The array of ints.
+ */
+ int ints[] ;
+
+ /**
+ * The number of ints in this instance.
+ */
+ int count ;
+
+ /**
+ * Construct a new empty IntList of the given initial size.
+ * @param initialSize initial size of the backing array
+ */
+ IntList(int initialSize) {
+ ints = new int[initialSize] ;
+ count = 0 ;
+ }
+
+ /**
+ * Constructs an IntList with the given contents.
+ * @param ints the array of ints to use as the contents
+ */
+ IntList(int ints[]) {
+ this.ints = ints ;
+ this.count = ints.length ;
+ }
+
+ /**
+ * Add a new int to the end of this list.
+ * @param i the int to be appended to this list
+ */
+ void add(int i) {
+ if (count == ints.length) {
+ int newints[] = new int[2*count] ;
+ System.arraycopy(ints, 0, newints, 0, count) ;
+ ints = newints ;
+ if (debug)
+ System.out.println
+ ("GeneralizedStrip.IntList: reallocated " +
+ (2*count) + " ints") ;
+ }
+ ints[count++] = i ;
+ }
+
+ /**
+ * Trim the backing array to the current count and return the
+ * resulting backing array.
+ */
+ int[] trim() {
+ if (count != ints.length) {
+ int newints[] = new int[count] ;
+ System.arraycopy(ints, 0, newints, 0, count) ;
+ ints = newints ;
+ }
+ return ints ;
+ }
+
+ /**
+ * Fill the list with consecutive integers starting from 0.
+ */
+ void fillAscending() {
+ for (int i = 0 ; i < ints.length ; i++)
+ ints[i] = i ;
+
+ count = ints.length ;
+ }
+
+ public String toString() {
+ String s = new String("[") ;
+ for (int i = 0 ; i < count-1 ; i++)
+ s = s + Integer.toString(ints[i]) + ", " ;
+ return s + Integer.toString(ints[count-1]) + "]" ;
+ }
+ }
+
+ /**
+ * The StripArray class is used as the output of some conversion methods
+ * in the GeneralizedStrip class.
+ */
+ static class StripArray {
+ /**
+ * A list of indices into the vertices of the original generalized
+ * strip. It specifies the order in which vertices in the original
+ * strip should be followed to build GeometryArray objects.
+ */
+ IntList vertices ;
+
+ /**
+ * A list of strip counts.
+ */
+ IntList stripCounts ;
+
+ /**
+ * Creates a StripArray with the specified vertices and stripCounts.
+ * @param vertices IntList containing vertex indicies.
+ * @param stripCounts IntList containing strip lengths.
+ */
+ StripArray(IntList vertices, IntList stripCounts) {
+ this.vertices = vertices ;
+ this.stripCounts = stripCounts ;
+ }
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a 2-element array of
+ * StripArray objects. The first StripArray will contain triangle strips
+ * and the second will contain triangle fans.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @return a 2-element array containing strips in 0 and fans in 1
+ */
+ static StripArray[] toStripsAndFans(GeneralizedStripFlags vertices,
+ int frontFace) {
+
+ int size = vertices.getFlagCount() ;
+
+ // Initialize IntLists to worst-case sizes.
+ IntList stripVerts = new IntList(size*3) ;
+ IntList fanVerts = new IntList(size*3) ;
+ IntList stripCounts = new IntList(size) ;
+ IntList fanCounts = new IntList(size) ;
+
+ toStripsAndFans(vertices, frontFace,
+ stripVerts, stripCounts, fanVerts, fanCounts) ;
+
+ // Construct the StripArray output.
+ StripArray sa[] = new StripArray[2] ;
+
+ if (stripCounts.count > 0)
+ sa[0] = new StripArray(stripVerts, stripCounts) ;
+
+ if (fanCounts.count > 0)
+ sa[1] = new StripArray(fanVerts, fanCounts) ;
+
+ return sa ;
+ }
+
+ private static void toStripsAndFans(GeneralizedStripFlags vertices,
+ int frontFace,
+ IntList stripVerts,
+ IntList stripCounts,
+ IntList fanVerts,
+ IntList fanCounts) {
+ int newFlag, curFlag, winding ;
+ int v, size, stripStart, stripLength ;
+ boolean transition = false ;
+
+ stripStart = 0 ;
+ stripLength = 3 ;
+ curFlag = vertices.getFlag(0) ;
+ winding = (curFlag == RESTART_CW ? CW : CCW) ;
+ size = vertices.getFlagCount() ;
+
+ // Vertex replace flags for the first 3 vertices are irrelevant since
+ // they can only define a single triangle. The first meaningful
+ // replace flag starts at the 4th vertex.
+ v = 3 ;
+ if (v < size)
+ curFlag = vertices.getFlag(v) ;
+
+ while (v < size) {
+ newFlag = vertices.getFlag(v) ;
+
+ if ((newFlag == curFlag) &&
+ (newFlag != RESTART_CW) && (newFlag != RESTART_CCW)) {
+ // The last flag was the same as this one, and it wasn't a
+ // restart: proceed to the next vertex.
+ stripLength++ ;
+ v++ ;
+
+ } else {
+ // Either this vertex flag changed from the last one, or
+ // the flag explicitly specifies a restart: process the
+ // last strip and start up a new one.
+ if (curFlag == REPLACE_MIDDLE)
+ addFan(fanVerts, fanCounts, stripStart, stripLength,
+ frontFace, winding, transition) ;
+ else
+ addStrip(stripVerts, stripCounts, stripStart, stripLength,
+ frontFace, winding) ;
+
+ // Restart: skip to the 4th vertex of the new strip.
+ if ((newFlag == RESTART_CW) || (newFlag == RESTART_CCW)) {
+ winding = (newFlag == RESTART_CW ? CW : CCW) ;
+ stripStart = v ;
+ stripLength = 3 ;
+ v += 3 ;
+ transition = false ;
+ if (v < size)
+ curFlag = vertices.getFlag(v) ;
+ }
+ // Strip/fan transition: decrement start of strip.
+ else {
+ if (newFlag == REPLACE_OLDEST) {
+ // Flip winding order when transitioning from fans
+ // to strips.
+ winding = (winding == CW ? CCW : CW) ;
+ stripStart = v-2 ;
+ stripLength = 3 ;
+ } else {
+ // Flip winding order when transitioning from
+ // strips to fans only if the preceding strip has
+ // an even number of vertices.
+ if ((stripLength & 0x01) == 0)
+ winding = (winding == CW ? CCW : CW) ;
+ stripStart = v-3 ;
+ stripLength = 4 ;
+ }
+ v++ ;
+ transition = true ;
+ curFlag = newFlag ;
+ }
+ }
+ }
+
+ // Finish off the last strip or fan.
+ // If v > size then the strip is degenerate.
+ if (v == size)
+ if (curFlag == REPLACE_MIDDLE)
+ addFan(fanVerts, fanCounts, stripStart, stripLength,
+ frontFace, winding, transition) ;
+ else
+ addStrip(stripVerts, stripCounts, stripStart, stripLength,
+ frontFace, winding) ;
+ else
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeneralizedStrip0")) ;
+
+ if (debug) {
+ System.out.println("GeneralizedStrip.toStripsAndFans") ;
+ if (v > size)
+ System.out.println(" ended with a degenerate triangle:" +
+ " number of vertices: " + (v-size)) ;
+
+ System.out.println("\n number of strips: " + stripCounts.count) ;
+ if (stripCounts.count > 0) {
+ System.out.println(" number of vertices: " + stripVerts.count) ;
+ System.out.println(" vertices/strip: " +
+ (float)stripVerts.count/stripCounts.count) ;
+ System.out.println(" strip counts: " + stripCounts.toString()) ;
+ // System.out.println(" indices: " + stripVerts.toString()) ;
+ }
+
+ System.out.println("\n number of fans: " + fanCounts.count) ;
+ if (fanCounts.count > 0) {
+ System.out.println(" number of vertices: " + fanVerts.count) ;
+ System.out.println(" vertices/strip: " +
+ (float)fanVerts.count/fanCounts.count) ;
+ System.out.println(" fan counts: " + fanCounts.toString()) ;
+ // System.out.println(" indices: " + fanVerts.toString()) ;
+ }
+ System.out.println("\n total vertices: " +
+ (stripVerts.count + fanVerts.count) +
+ "\n original number of vertices: " + size +
+ "\n") ;
+ }
+ }
+
+ //
+ // Java 3D specifies that the vertices of front-facing polygons
+ // have counter-clockwise (CCW) winding order when projected to
+ // the view surface. Polygons with clockwise (CW) vertex winding
+ // will be culled as back-facing by default.
+ //
+ // Generalized triangle strips can flip the orientation of their
+ // triangles with the RESTART_CW and RESTART_CCW vertex flags.
+ // Strips flagged with an orientation opposite to what has been
+ // specified as front-facing must have their windings reversed in
+ // order to have the correct face orientation when represented as
+ // GeometryArray objects.
+ //
+ private static void addStrip(IntList stripVerts,
+ IntList stripCounts,
+ int start, int length,
+ int frontFace, int winding) {
+ int vindex = start ;
+
+ if (winding == frontFace) {
+ // Maintain original order.
+ stripCounts.add(length) ;
+ while (vindex < start + length) {
+ stripVerts.add(vindex++) ;
+ }
+ } else if ((length & 0x1) == 1) {
+ // Reverse winding order if number of vertices is odd.
+ stripCounts.add(length) ;
+ vindex += length-1 ;
+ while (vindex >= start) {
+ stripVerts.add(vindex--) ;
+ }
+ } else if (length == 4) {
+ // Swap middle vertices.
+ stripCounts.add(4) ;
+ stripVerts.add(vindex) ;
+ stripVerts.add(vindex+2) ;
+ stripVerts.add(vindex+1) ;
+ stripVerts.add(vindex+3) ;
+ } else {
+ // Make the 1st triangle a singleton with reverse winding.
+ stripCounts.add(3) ;
+ stripVerts.add(vindex) ;
+ stripVerts.add(vindex+2) ;
+ stripVerts.add(vindex+1) ;
+ if (length > 3) {
+ // Copy the rest of the vertices in original order.
+ vindex++ ;
+ stripCounts.add(length-1) ;
+ while (vindex < start + length) {
+ stripVerts.add(vindex++) ;
+ }
+ }
+ }
+ }
+
+ private static void addFan(IntList fanVerts,
+ IntList fanCounts,
+ int start, int length,
+ int frontFace, int winding,
+ boolean transition) {
+ int vindex = start ;
+ fanVerts.add(vindex++) ;
+
+ if (winding == frontFace) {
+ if (transition) {
+ // Skip 1st triangle if this is the result of a transition.
+ fanCounts.add(length-1) ;
+ vindex++ ;
+ } else {
+ fanCounts.add(length) ;
+ fanVerts.add(vindex++) ;
+ }
+ while (vindex < start + length) {
+ fanVerts.add(vindex++) ;
+ }
+ } else {
+ // Reverse winding order.
+ vindex += length-2 ;
+ while (vindex > start+1) {
+ fanVerts.add(vindex--) ;
+ }
+ if (transition) {
+ // Skip 1st triangle if this is the result of a transition.
+ fanCounts.add(length-1) ;
+ } else {
+ fanCounts.add(length) ;
+ fanVerts.add(vindex) ;
+ }
+ }
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a StripArray containing
+ * exclusively strips.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @return a StripArray containing the converted strips
+ */
+ static StripArray toTriangleStrips(GeneralizedStripFlags vertices,
+ int frontFace) {
+
+ int size = vertices.getFlagCount() ;
+
+ // initialize lists to worst-case sizes.
+ IntList stripVerts = new IntList(size*3) ;
+ IntList fanVerts = new IntList(size*3) ;
+ IntList stripCounts = new IntList(size) ;
+ IntList fanCounts = new IntList(size) ;
+
+ toStripsAndFans(vertices, frontFace,
+ stripVerts, stripCounts, fanVerts, fanCounts) ;
+
+ if (fanCounts.count == 0)
+ if (stripCounts.count > 0)
+ return new StripArray(stripVerts, stripCounts) ;
+ else
+ return null ;
+
+ // convert each fan to one or more strips
+ int i, v = 0 ;
+ for (i = 0 ; i < fanCounts.count ; i++) {
+ fanToStrips(v, fanCounts.ints[i], fanVerts.ints,
+ stripVerts, stripCounts, false) ;
+ v += fanCounts.ints[i] ;
+ }
+
+ // create the StripArray output
+ StripArray sa = new StripArray(stripVerts, stripCounts) ;
+
+ if (debug) {
+ System.out.println("GeneralizedStrip.toTriangleStrips" +
+ "\n number of strips: " +
+ sa.stripCounts.count) ;
+ if (sa.stripCounts.count > 0) {
+ System.out.println(" number of vertices: " +
+ sa.vertices.count +
+ "\n vertices/strip: " +
+ ((float)sa.vertices.count /
+ (float)sa.stripCounts.count)) ;
+ System.out.print(" strip counts: [") ;
+ for (i = 0 ; i < sa.stripCounts.count-1 ; i++)
+ System.out.print(sa.stripCounts.ints[i] + ", ") ;
+ System.out.println(sa.stripCounts.ints[i] + "]") ;
+ }
+ System.out.println() ;
+ }
+ return sa ;
+ }
+
+ private static void fanToStrips(int v, int length, int fans[],
+ IntList stripVerts,
+ IntList stripCounts,
+ boolean convexPlanar) {
+ if (convexPlanar) {
+ // Construct a strip by criss-crossing across the interior.
+ stripCounts.add(length) ;
+ stripVerts.add(fans[v]) ;
+
+ int j = v + 1 ;
+ int k = v + (length - 1) ;
+ while (j <= k) {
+ stripVerts.add(fans[j++]) ;
+ if (j > k) break ;
+ stripVerts.add(fans[k--]) ;
+ }
+ } else {
+ // Traverse non-convex or non-planar fan, biting off 3-triangle
+ // strips or less. First 5 vertices produce 1 strip of 3
+ // triangles, and every 4 vertices after that produce another
+ // strip of 3 triangles. Each remaining strip adds 2 vertices.
+ int fanStart = v ;
+ v++ ;
+ while (v+4 <= fanStart + length) {
+ stripVerts.add(fans[v]) ;
+ stripVerts.add(fans[v+1]) ;
+ stripVerts.add(fans[fanStart]) ;
+ stripVerts.add(fans[v+2]) ;
+ stripVerts.add(fans[v+3]) ;
+ stripCounts.add(5) ;
+ v += 3 ;
+ }
+
+ // Finish off the fan.
+ if (v+1 < fanStart + length) {
+ stripVerts.add(fans[v]) ;
+ stripVerts.add(fans[v+1]) ;
+ stripVerts.add(fans[fanStart]) ;
+ v++ ;
+
+ if (v+1 < fanStart + length) {
+ stripVerts.add(fans[v+1]) ;
+ stripCounts.add(4) ;
+ }
+ else
+ stripCounts.add(3) ;
+ }
+ }
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning an array of vertex
+ * references representing the original generalized strip as individual
+ * triangles. Each sequence of three consecutive vertex references in the
+ * output defines a single triangle.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @return an array of indices into the original vertex array
+ */
+ static int[] toTriangles(GeneralizedStripFlags vertices, int frontFace) {
+
+ int vertexCount = 0 ;
+ StripArray sa[] = toStripsAndFans(vertices, frontFace) ;
+
+ if (sa[0] != null)
+ vertexCount = 3 * getTriangleCount(sa[0].stripCounts) ;
+ if (sa[1] != null)
+ vertexCount += 3 * getTriangleCount(sa[1].stripCounts) ;
+
+ if (debug)
+ System.out.println("GeneralizedStrip.toTriangles\n" +
+ " number of triangles: " + vertexCount/3 + "\n" +
+ " number of vertices: " + vertexCount + "\n") ;
+ int t = 0 ;
+ int triangles[] = new int[vertexCount] ;
+
+ if (sa[0] != null)
+ t = stripsToTriangles(t, triangles,
+ 0, sa[0].vertices.ints,
+ 0, sa[0].stripCounts.ints,
+ sa[0].stripCounts.count) ;
+ if (sa[1] != null)
+ t = fansToTriangles(t, triangles,
+ 0, sa[1].vertices.ints,
+ 0, sa[1].stripCounts.ints,
+ sa[1].stripCounts.count) ;
+ return triangles ;
+ }
+
+ private static int stripsToTriangles(int tstart, int tbuff[],
+ int vstart, int vertices[],
+ int stripStart, int stripCounts[],
+ int stripCount) {
+ int t = tstart ;
+ int v = vstart ;
+ for (int i = 0 ; i < stripCount ; i++) {
+ for (int j = 0 ; j < stripCounts[i+stripStart] - 2 ; j++) {
+ if ((j & 0x01) == 0) {
+ // even-numbered triangles
+ tbuff[t*3 +0] = vertices[v+0] ;
+ tbuff[t*3 +1] = vertices[v+1] ;
+ tbuff[t*3 +2] = vertices[v+2] ;
+ } else {
+ // odd-numbered triangles
+ tbuff[t*3 +0] = vertices[v+1] ;
+ tbuff[t*3 +1] = vertices[v+0] ;
+ tbuff[t*3 +2] = vertices[v+2] ;
+ }
+ t++ ; v++ ;
+ }
+ v += 2 ;
+ }
+ return t ;
+ }
+
+ private static int fansToTriangles(int tstart, int tbuff[],
+ int vstart, int vertices[],
+ int stripStart, int stripCounts[],
+ int stripCount) {
+ int t = tstart ;
+ int v = vstart ;
+ for (int i = 0 ; i < stripCount ; i++) {
+ for (int j = 0 ; j < stripCounts[i+stripStart] - 2 ; j++) {
+ tbuff[t*3 +0] = vertices[v] ;
+ tbuff[t*3 +1] = vertices[v+j+1] ;
+ tbuff[t*3 +2] = vertices[v+j+2] ;
+ t++ ;
+ }
+ v += stripCounts[i+stripStart] ;
+ }
+ return t ;
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a 2-element array of
+ * StripArray objects. The first StripArray will contain triangle strips
+ * and the second will contain individual triangles in the vertices
+ * field. Short strips will be converted to individual triangles.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @param shortStripSize strips this size or less will be converted to
+ * individual triangles if there are more than maxShortStrips of them
+ * @param maxShortStrips maximum number of short strips allowed before
+ * creating individual triangles
+ * @return a 2-element array containing strips in 0 and triangles in 1
+ */
+ static StripArray[] toStripsAndTriangles(GeneralizedStripFlags vertices,
+ int frontFace, int shortStripSize,
+ int maxShortStrips) {
+ int longStripCount = 0 ;
+ int longStripVertexCount = 0 ;
+ int shortStripCount = 0 ;
+ int triangleCount = 0 ;
+
+ StripArray sa[] = new StripArray[2] ;
+ StripArray ts = toTriangleStrips(vertices, frontFace) ;
+
+ for (int i = 0 ; i < ts.stripCounts.count ; i++)
+ if (ts.stripCounts.ints[i] <= shortStripSize) {
+ shortStripCount++ ;
+ triangleCount += ts.stripCounts.ints[i] - 2 ;
+ } else {
+ longStripCount++ ;
+ longStripVertexCount += ts.stripCounts.ints[i] ;
+ }
+
+ if (debug)
+ System.out.print("GeneralizedStrip.toStripsAndTriangles\n" +
+ " short strip size: " + shortStripSize +
+ " short strips tolerated: " + maxShortStrips +
+ " number of short strips: " + shortStripCount +
+ "\n\n") ;
+
+ if (shortStripCount <= maxShortStrips) {
+ sa[0] = ts ;
+ sa[1] = null ;
+ } else {
+ int si = 0 ; int newStripVerts[] = new int[longStripVertexCount] ;
+ int ci = 0 ; int newStripCounts[] = new int[longStripCount] ;
+ int ti = 0 ; int triangles[] = new int[3*triangleCount] ;
+ int vi = 0 ;
+
+ for (int i = 0 ; i < ts.stripCounts.count ; i++) {
+ if (ts.stripCounts.ints[i] <= shortStripSize) {
+ ti = stripsToTriangles(ti, triangles,
+ vi, ts.vertices.ints,
+ i, ts.stripCounts.ints, 1) ;
+ vi += ts.stripCounts.ints[i] ;
+ } else {
+ newStripCounts[ci++] = ts.stripCounts.ints[i] ;
+ for (int j = 0 ; j < ts.stripCounts.ints[i] ; j++)
+ newStripVerts[si++] = ts.vertices.ints[vi++] ;
+ }
+ }
+
+ if (longStripCount > 0)
+ sa[0] = new StripArray(new IntList(newStripVerts),
+ new IntList(newStripCounts)) ;
+ else
+ sa[0] = null ;
+
+ sa[1] = new StripArray(new IntList(triangles), null) ;
+
+ if (debug) {
+ System.out.println(" triangles separated: " + triangleCount) ;
+ if (longStripCount > 0) {
+ System.out.println
+ (" new vertices/strip: " +
+ ((float)longStripVertexCount/(float)longStripCount)) ;
+
+ System.out.print(" long strip counts: [") ;
+ for (int i = 0 ; i < longStripCount-1 ; i++)
+ System.out.print(newStripCounts[i++] + ", ") ;
+
+ System.out.println
+ (newStripCounts[longStripCount-1] + "]\n") ;
+ }
+ }
+ }
+ return sa ;
+ }
+
+ /**
+ * Interprets the vertex flags associated with a class implementing
+ * GeneralizedStripFlags, constructing and returning a StripArray.
+ *
+ * RESTART_CW and RESTART_CCW are treated as equivalent, as are
+ * REPLACE_MIDDLE and REPLACE_OLDEST.
+ *
+ * @param vertices an object implementing GeneralizedStripFlags
+ * @return a StripArray representing an array of line strips
+ */
+ static StripArray toLineStrips(GeneralizedStripFlags vertices) {
+ int v, size, stripStart, stripLength, flag ;
+
+ stripStart = 0 ;
+ stripLength = 2 ;
+ size = vertices.getFlagCount() ;
+
+ // Initialize IntLists to worst-case sizes.
+ IntList stripVerts = new IntList(size*2) ;
+ IntList stripCounts = new IntList(size) ;
+
+ // Vertex replace flags for the first two vertices are irrelevant.
+ v = 2 ;
+ while (v < size) {
+ flag = vertices.getFlag(v) ;
+
+ if ((flag != RESTART_CW) && (flag != RESTART_CCW)) {
+ // proceed to the next vertex.
+ stripLength++ ;
+ v++ ;
+
+ } else {
+ // Record the last strip.
+ stripCounts.add(stripLength) ;
+ for (int i = stripStart ; i < stripStart+stripLength ; i++)
+ stripVerts.add(i) ;
+
+ // Start a new strip and skip to its 3rd vertex.
+ stripStart = v ;
+ stripLength = 2 ;
+ v += 2 ;
+ }
+ }
+
+ // Finish off the last strip.
+ // If v > size then the strip is degenerate.
+ if (v == size) {
+ stripCounts.add(stripLength) ;
+ for (int i = stripStart ; i < stripStart+stripLength ; i++)
+ stripVerts.add(i) ;
+ } else
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeneralizedStrip0")) ;
+
+ if (debug) {
+ System.out.println("GeneralizedStrip.toLineStrips\n") ;
+ if (v > size)
+ System.out.println(" ended with a degenerate line") ;
+
+ System.out.println(" number of strips: " + stripCounts.count) ;
+ if (stripCounts.count > 0) {
+ System.out.println(" number of vertices: " + stripVerts.count) ;
+ System.out.println(" vertices/strip: " +
+ (float)stripVerts.count/stripCounts.count) ;
+ System.out.println(" strip counts: " + stripCounts.toString()) ;
+ // System.out.println(" indices: " + stripVerts.toString()) ;
+ }
+ System.out.println() ;
+ }
+
+ if (stripCounts.count > 0)
+ return new StripArray(stripVerts, stripCounts) ;
+ else
+ return null ;
+ }
+
+ /**
+ * Counts the number of lines defined by arrays of line strips.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return number of lines in the strips
+ */
+ static int getLineCount(int stripCounts[]) {
+ int count = 0 ;
+ for (int i = 0 ; i < stripCounts.length ; i++)
+ count += (stripCounts[i] - 1) ;
+ return count ;
+ }
+
+ /**
+ * Counts the number of triangles defined by arrays of
+ * triangle strips or fans.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return number of triangles in the strips or fans
+ */
+ static int getTriangleCount(int stripCounts[]) {
+ int count = 0 ;
+ for (int i = 0 ; i < stripCounts.length ; i++)
+ count += (stripCounts[i] - 2) ;
+ return count ;
+ }
+
+ /**
+ * Counts the number of triangles defined by arrays of
+ * triangle strips or fans.
+ *
+ * @param stripCounts IntList of strip counts
+ * @return number of triangles in the strips or fans
+ */
+ static int getTriangleCount(IntList stripCounts) {
+ int count = 0 ;
+ for (int i = 0 ; i < stripCounts.count ; i++)
+ count += (stripCounts.ints[i] - 2) ;
+ return count ;
+ }
+
+ /**
+ * Breaks up triangle strips into separate triangles.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return array of ints which index into the original vertex array; each
+ * set of three consecutive vertex indices defines a single triangle
+ */
+ static int[] stripsToTriangles(int stripCounts[]) {
+ int triangleCount = getTriangleCount(stripCounts) ;
+ int tbuff[] = new int[3*triangleCount] ;
+ IntList vertices = new IntList(triangleCount + 2*stripCounts.length) ;
+
+ vertices.fillAscending() ;
+ stripsToTriangles(0, tbuff,
+ 0, vertices.ints,
+ 0, stripCounts,
+ stripCounts.length) ;
+ return tbuff ;
+ }
+
+ /**
+ * Breaks up triangle fans into separate triangles.
+ *
+ * @param stripCounts array of strip counts, as used by the
+ * GeometryStripArray object
+ * @return array of ints which index into the original vertex array; each
+ * set of three consecutive vertex indices defines a single triangle
+ */
+ static int[] fansToTriangles(int stripCounts[]) {
+ int triangleCount = getTriangleCount(stripCounts) ;
+ int tbuff[] = new int[3*triangleCount] ;
+ IntList vertices = new IntList(triangleCount + 2*stripCounts.length) ;
+
+ vertices.fillAscending() ;
+ fansToTriangles(0, tbuff,
+ 0, vertices.ints,
+ 0, stripCounts,
+ stripCounts.length) ;
+ return tbuff ;
+ }
+
+ /**
+ * Takes a fan and converts it to one or more strips.
+ *
+ * @param v index into the fans array of the first vertex in the fan
+ * @param length number of vertices in the fan
+ * @param fans array of vertex indices representing one or more fans
+ * @param convexPlanar if true indicates that the fan is convex and
+ * planar; such fans will always be converted into a single strip
+ * @return a StripArray containing the converted strips
+ */
+ static StripArray fanToStrips(int v, int length, int fans[],
+ boolean convexPlanar) {
+
+ // Initialize IntLists to worst-case sizes.
+ IntList stripVerts = new IntList(length*3) ;
+ IntList stripCounts = new IntList(length) ;
+
+ fanToStrips(v, length, fans, stripVerts, stripCounts, convexPlanar) ;
+ return new StripArray(stripVerts, stripCounts) ;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GeneralizedStripFlags.java b/src/classes/share/javax/media/j3d/GeneralizedStripFlags.java
new file mode 100644
index 0000000..3728010
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeneralizedStripFlags.java
@@ -0,0 +1,73 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A class which implements GeneralizedStripFlags provides the means to access
+ * the vertex replace code flags associated with each vertex of a generalized
+ * strip. This allows a flexible representation of generalized strips for
+ * various classes and makes it possible to provide a common subset of static
+ * methods which operate only on their topology.
+ *
+ * @see GeneralizedStrip
+ * @see GeneralizedVertexList
+ */
+interface GeneralizedStripFlags {
+
+ /**
+ * This flag indicates that a vertex starts a new strip with clockwise
+ * winding.
+ */
+ static final int RESTART_CW = 0 ;
+
+ /**
+ * This flag indicates that a vertex starts a new strip with
+ * counter-clockwise winding.
+ */
+ static final int RESTART_CCW = 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.
+ */
+ 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.
+ */
+ static final int REPLACE_OLDEST = 3 ;
+
+ /**
+ * This constant is used to indicate that triangles with clockwise vertex
+ * winding are front facing.
+ */
+ static final int FRONTFACE_CW = 0 ;
+
+ /**
+ * This constant is used to indicate that triangles with counter-clockwise
+ * vertex winding are front facing.
+ */
+ static final int FRONTFACE_CCW = 1 ;
+
+ /**
+ * Return the number of flags. This should be the same as the number of
+ * vertices in the generalized strip.
+ */
+ int getFlagCount() ;
+
+ /**
+ * Return the flag associated with the vertex at the specified index.
+ */
+ int getFlag(int index) ;
+}
diff --git a/src/classes/share/javax/media/j3d/GeneralizedVertexList.java b/src/classes/share/javax/media/j3d/GeneralizedVertexList.java
new file mode 100644
index 0000000..2fc0971
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeneralizedVertexList.java
@@ -0,0 +1,387 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.* ;
+import java.util.* ;
+
+/**
+ * The GeneralizedVertexList class is a variable-size list used to
+ * collect the vertices for a generalized strip of points, lines, or
+ * triangles. This is used by the GeometryDecompressor. This class
+ * implements the GeneralizedStripFlags interface and provides methods
+ * for copying instance vertex data into various fixed-size
+ * GeometryArray representations.
+ *
+ * @see GeneralizedStrip
+ * @see GeometryDecompressor
+ */
+class GeneralizedVertexList implements GeneralizedStripFlags {
+
+ // The ArrayList containing the vertices.
+ private ArrayList vertices ;
+
+ // Booleans for individual vertex components.
+ private boolean hasColor3 = false ;
+ private boolean hasColor4 = false ;
+ private boolean hasNormals = false ;
+
+ // Indicates the vertex winding of front-facing triangles in this strip.
+ private int frontFace ;
+
+ /**
+ * Count of number of strips generated after conversion to GeometryArray.
+ */
+ int stripCount ;
+
+ /**
+ * Count of number of vertices generated after conversion to GeometryArray.
+ */
+ int vertexCount ;
+
+ /**
+ * Count of number of triangles generated after conversion to GeometryArray.
+ */
+ int triangleCount ;
+
+ /**
+ * Bits describing the data bundled with each vertex. This is specified
+ * using the GeometryArray mask components.
+ */
+ int vertexFormat ;
+
+ /**
+ * Creates a new GeneralizedVertexList for the specified vertex format.
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex, as used by GeometryArray.
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @param initSize initial number of elements
+ * @see GeometryArray
+ */
+ GeneralizedVertexList(int vertexFormat, int frontFace, int initSize) {
+ this.frontFace = frontFace ;
+ setVertexFormat(vertexFormat) ;
+
+ if (initSize == 0)
+ vertices = new ArrayList() ;
+ else
+ vertices = new ArrayList(initSize) ;
+
+ stripCount = 0 ;
+ vertexCount = 0 ;
+ triangleCount = 0 ;
+ }
+
+ /**
+ * Creates a new GeneralizedVertexList for the specified vertex format.
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex, as used by GeometryArray.
+ * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
+ * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
+ * @see GeometryArray
+ */
+ GeneralizedVertexList(int vertexFormat, int frontFace) {
+ this(vertexFormat, frontFace, 0) ;
+ }
+
+ /**
+ * Sets the vertex format for this vertex list.
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex, as used by GeometryArray.
+ */
+ void setVertexFormat(int vertexFormat) {
+ this.vertexFormat = vertexFormat ;
+
+ if ((vertexFormat & GeometryArray.NORMALS) != 0)
+ hasNormals = true ;
+
+ if ((vertexFormat & GeometryArray.COLOR) != 0)
+ if ((vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ hasColor4 = true ;
+ else
+ hasColor3 = true ;
+ }
+
+ /**
+ * A class with fields corresponding to all the data that can be bundled
+ * with the vertices of generalized strips.
+ */
+ class Vertex {
+ int flag ;
+ Point3f coord ;
+ Color3f color3 ;
+ Color4f color4 ;
+ Vector3f normal ;
+
+ Vertex(Point3f p, Vector3f n, Color4f c, int flag) {
+ this.flag = flag ;
+ coord = new Point3f(p) ;
+
+ if (hasNormals)
+ normal = new Vector3f(n) ;
+
+ if (hasColor3)
+ color3 = new Color3f(c.x, c.y, c.z) ;
+
+ else if (hasColor4)
+ color4 = new Color4f(c) ;
+ }
+ }
+
+ /**
+ * Copy vertex data to a new Vertex object and add it to this list.
+ */
+ void addVertex(Point3f pos, Vector3f norm, Color4f color, int flag) {
+ vertices.add(new Vertex(pos, norm, color, flag)) ;
+ }
+
+ /**
+ * Return the number of vertices in this list.
+ */
+ int size() {
+ return vertices.size() ;
+ }
+
+ // GeneralizedStripFlags interface implementation
+ public int getFlagCount() {
+ return vertices.size() ;
+ }
+
+ // GeneralizedStripFlags interface implementation
+ public int getFlag(int index) {
+ return ((Vertex)vertices.get(index)).flag ;
+ }
+
+ // Copy vertices in the given order to a fixed-length GeometryArray.
+ // Using the array versions of the GeometryArray set() methods results in
+ // a significant performance improvement despite needing to create
+ // fixed-length arrays to hold the vertex elements.
+ private void copyVertexData(GeometryArray ga,
+ GeneralizedStrip.IntList indices) {
+ Vertex v ;
+ Point3f p3f[] = new Point3f[indices.count] ;
+
+ if (hasNormals) {
+ Vector3f v3f[] = new Vector3f[indices.count] ;
+ if (hasColor3) {
+ Color3f c3f[] = new Color3f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ v3f[i] = v.normal ;
+ c3f[i] = v.color3 ;
+ }
+ ga.setColors(0, c3f) ;
+
+ } else if (hasColor4) {
+ Color4f c4f[] = new Color4f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ v3f[i] = v.normal ;
+ c4f[i] = v.color4 ;
+ }
+ ga.setColors(0, c4f) ;
+
+ } else {
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ v3f[i] = v.normal ;
+ }
+ }
+ ga.setNormals(0, v3f) ;
+
+ } else {
+ if (hasColor3) {
+ Color3f c3f[] = new Color3f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ c3f[i] = v.color3 ;
+ }
+ ga.setColors(0, c3f) ;
+
+ } else if (hasColor4) {
+ Color4f c4f[] = new Color4f[indices.count] ;
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ c4f[i] = v.color4 ;
+ }
+ ga.setColors(0, c4f) ;
+
+ } else {
+ for (int i = 0 ; i < indices.count ; i++) {
+ v = (Vertex)vertices.get(indices.ints[i]) ;
+ p3f[i] = v.coord ;
+ }
+ }
+ }
+ ga.setCoordinates(0, p3f) ;
+ }
+
+ /**
+ * Output a PointArray.
+ */
+ PointArray toPointArray() {
+ int size = vertices.size() ;
+
+ if (size > 0) {
+ PointArray pa = new PointArray(size, vertexFormat) ;
+ GeneralizedStrip.IntList il = new GeneralizedStrip.IntList(size) ;
+
+ il.fillAscending() ;
+ copyVertexData(pa, il) ;
+
+ vertexCount += size ;
+ return pa ;
+ }
+ else
+ return null ;
+ }
+
+ /**
+ * Output a TriangleArray.
+ */
+ TriangleArray toTriangleArray() {
+ int vertices[] = GeneralizedStrip.toTriangles(this, frontFace) ;
+
+ if (vertices != null) {
+ TriangleArray ta ;
+ GeneralizedStrip.IntList il ;
+
+ ta = new TriangleArray(vertices.length, vertexFormat) ;
+ il = new GeneralizedStrip.IntList(vertices) ;
+ copyVertexData(ta, il) ;
+
+ vertexCount += vertices.length ;
+ triangleCount += vertices.length/3 ;
+ return ta ;
+ } else
+ return null ;
+ }
+
+ /**
+ * Output a LineStripArray.
+ */
+ LineStripArray toLineStripArray() {
+ GeneralizedStrip.StripArray stripArray =
+ GeneralizedStrip.toLineStrips(this) ;
+
+ if (stripArray != null) {
+ LineStripArray lsa ;
+ lsa = new LineStripArray(stripArray.vertices.count,
+ vertexFormat,
+ stripArray.stripCounts.trim()) ;
+
+ copyVertexData(lsa, stripArray.vertices) ;
+
+ vertexCount += stripArray.vertices.count ;
+ stripCount += stripArray.stripCounts.count ;
+ return lsa ;
+ } else
+ return null ;
+ }
+
+ /**
+ * Output a TriangleStripArray.
+ */
+ TriangleStripArray toTriangleStripArray() {
+ GeneralizedStrip.StripArray stripArray =
+ GeneralizedStrip.toTriangleStrips(this, frontFace) ;
+
+ if (stripArray != null) {
+ TriangleStripArray tsa ;
+ tsa = new TriangleStripArray(stripArray.vertices.count,
+ vertexFormat,
+ stripArray.stripCounts.trim()) ;
+
+ copyVertexData(tsa, stripArray.vertices) ;
+
+ vertexCount += stripArray.vertices.count ;
+ stripCount += stripArray.stripCounts.count ;
+ return tsa ;
+ } else
+ return null ;
+ }
+
+ /**
+ * Output triangle strip and triangle fan arrays.
+ * @return a 2-element array of GeometryStripArray; element 0 if non-null
+ * will contain a TriangleStripArray, and element 1 if non-null will
+ * contain a TriangleFanArray.
+ */
+ GeometryStripArray[] toStripAndFanArrays() {
+ GeneralizedStrip.StripArray stripArray[] =
+ GeneralizedStrip.toStripsAndFans(this, frontFace) ;
+
+ GeometryStripArray gsa[] = new GeometryStripArray[2] ;
+
+ if (stripArray[0] != null) {
+ gsa[0] = new TriangleStripArray(stripArray[0].vertices.count,
+ vertexFormat,
+ stripArray[0].stripCounts.trim()) ;
+
+ copyVertexData(gsa[0], stripArray[0].vertices) ;
+
+ vertexCount += stripArray[0].vertices.count ;
+ stripCount += stripArray[0].stripCounts.count ;
+ }
+
+ if (stripArray[1] != null) {
+ gsa[1] = new TriangleFanArray(stripArray[1].vertices.count,
+ vertexFormat,
+ stripArray[1].stripCounts.trim()) ;
+
+ copyVertexData(gsa[1], stripArray[1].vertices) ;
+
+ vertexCount += stripArray[1].vertices.count ;
+ stripCount += stripArray[1].stripCounts.count ;
+ }
+ return gsa ;
+ }
+
+ /**
+ * Output triangle strip and and triangle arrays.
+ * @return a 2-element array of GeometryArray; element 0 if non-null
+ * will contain a TriangleStripArray, and element 1 if non-null will
+ * contain a TriangleArray.
+ */
+ GeometryArray[] toStripAndTriangleArrays() {
+ GeneralizedStrip.StripArray stripArray[] =
+ GeneralizedStrip.toStripsAndTriangles(this, frontFace, 4, 12) ;
+
+ GeometryArray ga[] = new GeometryArray[2] ;
+
+ if (stripArray[0] != null) {
+ ga[0] = new TriangleStripArray(stripArray[0].vertices.count,
+ vertexFormat,
+ stripArray[0].stripCounts.trim()) ;
+
+ copyVertexData(ga[0], stripArray[0].vertices) ;
+
+ vertexCount += stripArray[0].vertices.count ;
+ stripCount += stripArray[0].stripCounts.count ;
+ }
+
+ if (stripArray[1] != null) {
+ ga[1] = new TriangleArray(stripArray[1].vertices.count,
+ vertexFormat) ;
+
+ copyVertexData(ga[1], stripArray[1].vertices) ;
+ triangleCount += stripArray[1].vertices.count/3 ;
+ }
+ return ga ;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Geometry.java b/src/classes/share/javax/media/j3d/Geometry.java
new file mode 100644
index 0000000..7ffc858
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Geometry.java
@@ -0,0 +1,45 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Geometry is an abstract class that specifies the geometry
+ * component information required by a Shape3D node. Geometry objects
+ * describe both the geometry and topology of the Shape3D nodes that
+ * reference them. Geometry objects consist of four generic geometric
+ * types:<P>
+ * <UL><LI>Compressed Geometry</LI>
+ * <LI>GeometryArray</LI>
+ * <LI>Raster</LI>
+ * <LI>Text3D</LI>
+ * </UL><P>
+ * Each of these geometric types defines a visible object or set of
+ * objects. A Geometry object is used as a component object of a Shape3D
+ * leaf node.
+ *
+ */
+
+public abstract class Geometry extends NodeComponent {
+
+ /**
+ * Specifies that this Geometry allows intersect operation.
+ */
+ public static final int
+ ALLOW_INTERSECT = CapabilityBits.GEOMETRY_ALLOW_INTERSECT;
+
+ /**
+ * Constructs a new Geometry object.
+ */
+ public Geometry() {
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryArray.java b/src/classes/share/javax/media/j3d/GeometryArray.java
new file mode 100644
index 0000000..82aa50b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryArray.java
@@ -0,0 +1,5763 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+
+/**
+ * The GeometryArray object contains separate arrays of positional
+ * coordinates, colors, normals, and texture coordinates that
+ * describe point, line, or polygon geometry. This class is extended
+ * to create the various primitive types (such as lines,
+ * triangle strips, etc.).
+ * Vertex data may be passed to this geometry array in one of two
+ * ways: by copying the data into the array using the existing
+ * methods, or by passing a reference to the data.
+ * <p>
+ * <ul>
+ * <li>
+ * <b>By Copying:</b>
+ * The existing methods for setting positional coordinates, colors,
+ * normals, and texture coordinates (such as <code>setCoordinate</code>,
+ * <code>setColors</code>, etc.) copy the data into this
+ * GeometryArray. This is appropriate for many applications and
+ * offers an application much flexibility in organizing its data.
+ * This is the default mode.
+ * </li>
+ * <li><b>By Reference:</b>
+ * A new set of methods in Java 3D version 1.2 allows data to be
+ * accessed by reference, directly from the user's arrays. To use
+ * this feature, set the <code>BY_REFERENCE</code> bit in the
+ * <code>vertexFormat</code> field of the constructor for this
+ * GeometryArray. In this mode, the various set methods for
+ * coordinates, normals, colors, and texture coordinates are not used.
+ * Instead, new methods are used to set a reference to user-supplied
+ * coordinate, color, normal, and texture coordinate arrays (such as
+ * <code>setCoordRefFloat</code>, <code>setColorRefFloat</code>,
+ * etc.). Data in any array that is referenced by a live or compiled
+ * GeometryArray object may only be modified via the
+ * <code>updateData</code> method (subject to the
+ * <code>ALLOW_REF_DATA_WRITE</code> capability bit). Applications
+ * must exercise care not to violate this rule. If any referenced
+ * geometry data is modified outside of the <code>updateData</code>
+ * method, the results are undefined.
+ * </li>
+ * </ul>
+ * <p>
+ * All colors used in the geometry array object must be in the range [0.0,1.0].
+ * Values outside this range will cause undefined results.
+ * All normals used in the geometry array object must be unit length
+ * vectors. That is their geometric length must be 1.0. Normals that
+ * are not unit length vectors will cause undefined results.
+ * <p>
+ * Note that the term <i>coordinate</i>, as used in the method names
+ * and method descriptions, actually refers to a set of <i>x</i>,
+ * <i>y</i>, and <i>z</i> coordinates representing the position of a
+ * single vertex. The term <i>coordinates</i> (plural) is used to
+ * indicate sets of <i>x</i>, <i>y</i>, and <i>z</i> coordinates for
+ * multiple vertices. This is somewhat at odds with the mathematical
+ * definition of a coordinate, but is used as a convenient shorthand.
+ * Similarly, the term <i>texture coordinate</i> is used to indicate a
+ * set of texture coordinates for a single vertex, while the term
+ * <i>texture coordinates</i> (plural) is used to indicate sets of
+ * texture coordinates for multiple vertices.
+ */
+
+public abstract class GeometryArray extends Geometry {
+
+ /**
+ * Specifies that this GeometryArray allows reading the array of
+ * coordinates.
+ */
+ public static final int
+ ALLOW_COORDINATE_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_COORDINATE_READ;
+
+ /**
+ * Specifies that this GeometryArray allows writing the array of
+ * coordinates.
+ */
+ public static final int
+ ALLOW_COORDINATE_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_COORDINATE_WRITE;
+
+ /**
+ * Specifies that this GeometryArray allows reading the array of
+ * colors.
+ */
+ public static final int
+ ALLOW_COLOR_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_COLOR_READ;
+
+ /**
+ * Specifies that this GeometryArray allows writing the array of
+ * colors.
+ */
+ public static final int
+ ALLOW_COLOR_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_COLOR_WRITE;
+
+ /**
+ * Specifies that this GeometryArray allows reading the array of
+ * normals.
+ */
+ public static final int
+ ALLOW_NORMAL_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_NORMAL_READ;
+
+ /**
+ * Specifies that this GeometryArray allows writing the array of
+ * normals.
+ */
+ public static final int
+ ALLOW_NORMAL_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_NORMAL_WRITE;
+
+ /**
+ * Specifies that this GeometryArray allows reading the array of
+ * texture coordinates.
+ */
+ public static final int
+ ALLOW_TEXCOORD_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_TEXCOORD_READ;
+
+ /**
+ * Specifies that this GeometryArray allows writing the array of
+ * texture coordinates.
+ */
+ public static final int
+ ALLOW_TEXCOORD_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_TEXCOORD_WRITE;
+
+ /**
+ * Specifies that this GeometryArray allows reading the count or
+ * initial index information for this object.
+ */
+ public static final int
+ ALLOW_COUNT_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_COUNT_READ;
+
+ /**
+ * Specifies that this GeometryArray allows writing the count or
+ * initial index information for this object.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int
+ ALLOW_COUNT_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_COUNT_WRITE;
+
+ /**
+ * Specifies that this GeometryArray allows reading the vertex format
+ * information for this object.
+ */
+ public static final int
+ ALLOW_FORMAT_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_FORMAT_READ;
+
+ /**
+ * Specifies that this GeometryArray allows reading the geometry
+ * data reference information for this object. This is only used in
+ * by-reference geometry mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int
+ ALLOW_REF_DATA_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_REF_DATA_READ;
+
+ private static final int J3D_1_2_ALLOW_REF_DATA_READ =
+ CapabilityBits.J3D_1_2_GEOMETRY_ARRAY_ALLOW_REF_DATA_READ;
+
+ /**
+ * Specifies that this GeometryArray allows writing the geometry
+ * data reference information for this object. It also enables
+ * writing the referenced data itself, via the GeometryUpdater
+ * interface. This is only used in by-reference geometry mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int
+ ALLOW_REF_DATA_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_REF_DATA_WRITE;
+
+ /**
+ * Specifies that this GeometryArray contains an array of coordinates.
+ * This bit must be set.
+ */
+ public static final int COORDINATES = 0x01;
+
+ /**
+ * Specifies that this GeometryArray contains an array of normals.
+ */
+ public static final int NORMALS = 0x02;
+
+ /**
+ * Specifies that this GeometryArray contains an array of colors.
+ */
+ static final int COLOR = 0x04;
+
+ /**
+ * Specifies that this GeometryArray's colors contain alpha.
+ */
+ static final int WITH_ALPHA = 0x08;
+
+ /**
+ * Specifies that this GeometryArray contains an array of colors without alpha.
+ */
+ public static final int COLOR_3 = COLOR;
+
+ /**
+ * Specifies that this GeometryArray contains an array of colors with alpha.
+ * This takes precedence over COLOR_3.
+ */
+ public static final int COLOR_4 = COLOR | WITH_ALPHA;
+
+ /**
+ * Specifies that this GeometryArray contains one or more arrays of
+ * 2D texture coordinates.
+ */
+ public static final int TEXTURE_COORDINATE_2 = 0x20;
+
+ /**
+ * Specifies that this GeometryArray contains one or more arrays of
+ * 3D texture coordinates.
+ * This takes precedence over TEXTURE_COORDINATE_2.
+ */
+ public static final int TEXTURE_COORDINATE_3 = 0x40;
+
+
+ /**
+ * Specifies that this GeometryArray contains one or more arrays of
+ * 4D texture coordinates.
+ * This takes precedence over TEXTURE_COORDINATE_2 and TEXTURE_COORDINATE_3.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int TEXTURE_COORDINATE_4 = 0x400;
+
+
+ static final int TEXTURE_COORDINATE = TEXTURE_COORDINATE_2 |
+ TEXTURE_COORDINATE_3 |
+ TEXTURE_COORDINATE_4;
+
+ /**
+ * Specifies that the position, color, normal, and texture coordinate
+ * data for this GeometryArray are accessed by reference.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int BY_REFERENCE = 0x80;
+
+
+ /**
+ * Specifies that the position, color, normal, and texture
+ * coordinate data for this GeometryArray are accessed via a single
+ * interleaved, floating-point array reference. All of the data
+ * values for each vertex are stored in consecutive memory
+ * locations. This flag is only valid in conjunction with the
+ * <code>BY_REFERENCE</code> flag.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int INTERLEAVED = 0x100;
+
+ /**
+ * Specifies that geometry by-reference data for this
+ * GeometryArray, whether interleaved or non-interleaved, is
+ * accessed via J3DBuffer objects that wrap NIO Buffer objects,
+ * rather than float, double, byte, or TupleXX arrays. This flag
+ * is only valid in conjunction with the <code>BY_REFERENCE</code>
+ * flag.
+ *
+ * <p>
+ * NOTE: Use of this class requires version 1.4 of the
+ * Java<sup><font size="-2">TM</font></sup>&nbsp;2 Platform.
+ *
+ * @see J3DBuffer
+ * @see #setCoordRefBuffer(J3DBuffer)
+ * @see #setColorRefBuffer(J3DBuffer)
+ * @see #setNormalRefBuffer(J3DBuffer)
+ * @see #setTexCoordRefBuffer(int,J3DBuffer)
+ * @see #setInterleavedVertexBuffer(J3DBuffer)
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int USE_NIO_BUFFER = 0x800;
+
+ /**
+ * Specifies that only the coordinate indices are used for indexed
+ * geometry arrays. In this mode, the values from the coordinate
+ * index array are used as a single set of index values to access
+ * the vertex data for all four vertex components (coord, color,
+ * normal, and texCoord). The color, normal, and texCoord index arrays
+ * are ignored. This flag is only valid for indexed geometry arrays
+ * (subclasses of IndexedGeometryArray).
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int USE_COORD_INDEX_ONLY = 0x200;
+
+ // Used to keep track of the last bit (for adding new bits only)
+ private static final int LAST_FORMAT_BIT = 0x800;
+
+
+ // Scratch arrays for converting Point[234]f to TexCoord[234]f
+ private TexCoord2f [] texCoord2fArray = null;
+ private TexCoord3f [] texCoord3fArray = null;
+ private TexCoord4f [] texCoord4fArray = null;
+ private TexCoord2f texCoord2fScratch = null;
+ private TexCoord3f texCoord3fScratch = null;
+
+
+
+ // non-public, no parameter constructor
+ GeometryArray() {
+ }
+
+
+ /**
+ * Constructs an empty GeometryArray object with the specified
+ * number of vertices and vertex format. Defaults are used
+ * for all other parameters. The default values are as follows:
+ * <ul>
+ * texCoordSetCount : 1<br>
+ * texCoordSetMap : { 0 }<br>
+ * validVertexCount : vertexCount<br>
+ * initialVertexIndex : 0<br>
+ * initialCoordIndex : 0<br>
+ * initialColorIndex : 0<br>
+ * initialNormalIndex : 0<br>
+ * initialTexCoordIndex : 0<br>
+ * all data array values : 0.0<br>
+ * all data array references : null<br>
+ * </ul>
+ *
+ * @param vertexCount the number of vertex elements in this GeometryArray
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: <code>COORDINATES</code>, to signal the inclusion of
+ * vertex positions--always present; <code>NORMALS</code>, to signal
+ * the inclusion of per vertex normals; one of <code>COLOR_3</code> or
+ * <code>COLOR_4</code>, to signal the inclusion of per vertex
+ * colors (without or with alpha information); one of
+ * <code>TEXTURE_COORDINATE_2</code>, <code>TEXTURE_COORDINATE_3</code>
+ * or <code>TEXTURE_COORDINATE_4</code>,
+ * to signal the
+ * inclusion of per-vertex texture coordinates (2D, 3D or 4D);
+ * <code>BY_REFERENCE</code>, to indicate that the data is passed
+ * by reference
+ * rather than by copying; <code>INTERLEAVED</code>, to indicate
+ * that the referenced
+ * data is interleaved in a single array;
+ * <code>USE_NIO_BUFFER</code>, to indicate that the referenced data
+ * is accessed via a J3DBuffer object that wraps an NIO buffer;
+ * <code>USE_COORD_INDEX_ONLY</code>,
+ * to indicate that only the coordinate indices are used for indexed
+ * geometry arrays.
+ * @exception IllegalArgumentException if vertexCount < 0, if
+ * vertexFormat does NOT include <code>COORDINATES</code>,
+ * if the <code>USE_COORD_INDEX_ONLY</code> bit is set for non-indexed
+ * geometry arrays (that is, GeometryArray objects that are not a
+ * subclass of IndexedGeometryArray),
+ * if the <code>INTERLEAVED</code> bit is set without the
+ * <code>BY_REFERENCE</code> bit being set,
+ * or if the <code>USE_NIO_BUFFER</code> bit is set without the
+ * <code>BY_REFERENCE</code> bit being set.
+ */
+ public GeometryArray(int vertexCount, int vertexFormat) {
+
+ if (vertexCount < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray96"));
+
+ if ((vertexFormat & COORDINATES) == 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray0"));
+
+ if ((vertexFormat & INTERLEAVED) != 0 &&
+ (vertexFormat & BY_REFERENCE) == 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray80"));
+
+ if ((vertexFormat & USE_NIO_BUFFER) != 0 &&
+ (vertexFormat & BY_REFERENCE) == 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray117"));
+
+ if ((vertexFormat & TEXTURE_COORDINATE) != 0) {
+ if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) {
+ texCoord2fArray = new TexCoord2f[1];
+ texCoord2fScratch = new TexCoord2f();
+ }
+ else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) {
+ texCoord3fArray = new TexCoord3f[1];
+ texCoord3fScratch = new TexCoord3f();
+ } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) {
+ texCoord4fArray = new TexCoord4f[1];
+ }
+ }
+
+ ((GeometryArrayRetained)this.retained).createGeometryArrayData(vertexCount, vertexFormat);
+ }
+
+
+ /**
+ * Constructs an empty GeometryArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, and texture coordinate mapping array. Defaults are used
+ * for all other parameters.
+ *
+ * @param vertexCount the number of vertex elements in this
+ * GeometryArray<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: <code>COORDINATES</code>, to signal the inclusion of
+ * vertex positions--always present; <code>NORMALS</code>, to signal
+ * the inclusion of per vertex normals; one of <code>COLOR_3</code> or
+ * <code>COLOR_4</code>, to signal the inclusion of per vertex
+ * colors (without or with alpha information); one of
+ * <code>TEXTURE_COORDINATE_2</code> or <code>TEXTURE_COORDINATE_3</code>
+ * or <code>TEXTURE_COORDINATE_4</code>,
+ * to signal the
+ * inclusion of per-vertex texture coordinates (2D , 3D or 4D);
+ * <code>BY_REFERENCE</code>, to indicate that the data is passed
+ * by reference
+ * rather than by copying; <code>INTERLEAVED</code>, to indicate
+ * that the referenced
+ * data is interleaved in a single array;
+ * <code>USE_NIO_BUFFER</code>, to indicate that the referenced data
+ * is accessed via a J3DBuffer object that wraps an NIO buffer;
+ * <code>USE_COORD_INDEX_ONLY</code>,
+ * to indicate that only the coordinate indices are used for indexed
+ * geometry arrays.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code> or
+ * <code>TEXTURE_COORDINATE_3</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * <a name="texCoordSetMap">
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code> or
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used. The following example
+ * illustrates the use of the <code>texCoordSetMap</code> array.
+ *
+ * <p>
+ * <ul>
+ * <table BORDER=1 CELLSPACING=1 CELLPADDING=1>
+ * <tr>
+ * <td><center><b>Index</b></center></td>
+ * <td><center><b>Element</b></center></td>
+ * <td><center><b>Description</b></center></td>
+ * </tr>
+ * <tr>
+ * <td><center>0</center></td>
+ * <td><center>1</center></td>
+ * <td><center>Use tex coord set 1 for tex unit 0</center></td>
+ * </tr>
+ * <tr>
+ * <td><center>1</center></td>
+ * <td><center>-1</center></td>
+ * <td><center>Use no tex coord set for tex unit 1</center></td>
+ * </tr>
+ * <tr>
+ * <td><center>2</center></td>
+ * <td><center>0</center></td>
+ * <td><center>Use tex coord set 0 for tex unit 2</center></td>
+ * </tr>
+ * <tr>
+ * <td><center>3</center></td>
+ * <td><center>1</center></td>
+ * <td><center>Reuse tex coord set 1 for tex unit 3</center></td>
+ * </tr>
+ * </table>
+ * </ul>
+ * <p>
+ *
+ * @exception IllegalArgumentException if
+ * <code>vertexCount&nbsp;<&nbsp;0</code>, if vertexFormat does
+ * NOT include <code>COORDINATES</code>, if the
+ * <code>INTERLEAVED</code> bit is set without the
+ * <code>BY_REFERENCE</code> bit being set, if the
+ * <code>USE_NIO_BUFFER</code> bit is set without the
+ * <code>BY_REFERENCE</code> bit being set, if
+ * the <code>USE_COORD_INDEX_ONLY</code> bit is set for non-indexed
+ * geometry arrays (that is, GeometryArray objects that are not a
+ * subclass of IndexedGeometryArray), if
+ * <code>texCoordSetCount&nbsp;<&nbsp;0</code>, or if any element
+ * in <code>texCoordSetMap[]&nbsp;>=&nbsp;texCoordSetCount</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public GeometryArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap) {
+
+ if (vertexCount < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray96"));
+
+ if ((vertexFormat & COORDINATES) == 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray0"
+));
+
+ if ((vertexFormat & INTERLEAVED) != 0 &&
+ (vertexFormat & BY_REFERENCE) == 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray80"));
+
+ if ((vertexFormat & USE_NIO_BUFFER) != 0 &&
+ (vertexFormat & BY_REFERENCE) == 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray117"));
+
+ if ((vertexFormat & TEXTURE_COORDINATE) != 0) {
+ if (texCoordSetMap == null)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray106"));
+
+ if (texCoordSetCount == 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray107"));
+
+ for (int i = 0; i < texCoordSetMap.length; i++) {
+ if (texCoordSetMap[i] >= texCoordSetCount)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray108"));
+ }
+
+ if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) {
+ texCoord2fArray = new TexCoord2f[1];
+ texCoord2fScratch = new TexCoord2f();
+ }
+ else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) {
+ texCoord3fArray = new TexCoord3f[1];
+ texCoord3fScratch = new TexCoord3f();
+ }
+ else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) {
+ texCoord4fArray = new TexCoord4f[1];
+ }
+ }
+
+ ((GeometryArrayRetained)this.retained).createGeometryArrayData(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap);
+
+ }
+
+
+ //------------------------------------------------------------------
+ // Common methods
+ //------------------------------------------------------------------
+
+ /**
+ * Retrieves the number of vertices in this GeometryArray
+ * @return number of vertices in this GeometryArray
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ */
+ public int getVertexCount() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray1"));
+
+ return ((GeometryArrayRetained)this.retained).getVertexCount();
+ }
+
+ /**
+ * Retrieves the vertexFormat of this GeometryArray
+ * @return format of vertices in this GeometryArray
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ */
+ public int getVertexFormat() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_FORMAT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray2"));
+
+ return ((GeometryArrayRetained)this.retained).getVertexFormat();
+ }
+
+
+ /**
+ * Retrieves the number of texture coordinate sets in this
+ * GeometryArray object.
+ *
+ * @return the number of texture coordinate sets
+ * in this GeometryArray object
+ *
+ * @since Java 3D 1.2
+ */
+ public int getTexCoordSetCount() {
+ return ((GeometryArrayRetained)this.retained).getTexCoordSetCount();
+ }
+
+
+ /**
+ * Retrieves the length of the texture coordinate set mapping
+ * array of this GeometryArray object.
+ *
+ * @return the length of the texture coordinate set mapping
+ * array of this GeometryArray object
+ *
+ * @since Java 3D 1.2
+ */
+ public int getTexCoordSetMapLength() {
+ return ((GeometryArrayRetained)this.retained).getTexCoordSetMapLength();
+ }
+
+
+ /**
+ * Retrieves the texture coordinate set mapping
+ * array from this GeometryArray object.
+ *
+ * @param texCoordSetMap an array that will receive a copy of the
+ * texture coordinate set mapping array. The array must be large
+ * enough to hold all entries of the texture coordinate set
+ * mapping array.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTexCoordSetMap(int[] texCoordSetMap) {
+ ((GeometryArrayRetained)this.retained).getTexCoordSetMap(texCoordSetMap);
+ return;
+ }
+
+
+ /**
+ * Updates geometry array data that is accessed by reference.
+ * This method calls the updateData method of the specified
+ * GeometryUpdater object to synchronize updates to vertex
+ * data that is referenced by this GeometryArray object.
+ * Applications that wish to modify such data must perform all
+ * updates via this method.
+ * <p>
+ * This method may also be used to atomically set multiple
+ * references (for example, to coordinate and color arrays)
+ * or to atomically
+ * change multiple data values through the geometry data copying
+ * methods.
+ *
+ * @param updater object whose updateData callback method will be
+ * called to update the data referenced by this GeometryArray.
+ * @exception CapabilityNotSetException if the appropriate capability
+ * is not set, the vertex data mode is <code>BY_REFERENCE</code>, and this
+ * object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void updateData(GeometryUpdater updater) {
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0 &&
+ isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_REF_DATA_WRITE)) {
+
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray81"));
+ }
+
+ ((GeometryArrayRetained)this.retained).updateData(updater);
+ }
+
+
+ /**
+ * Sets the valid vertex count for this GeometryArray object.
+ * This count specifies the number of vertices actually used in
+ * rendering or other operations such as picking and collision.
+ * This attribute is initialized to <code>vertexCount</code>.
+ *
+ * @param validVertexCount the new valid vertex count.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * <p>
+ * @exception IllegalArgumentException if any of the following are
+ * true:
+ * <ul>
+ * <code>validVertexCount < 0</code>,<br>
+ * <code>initialVertexIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialCoordIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialColorIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialNormalIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialTexCoordIndex + validVertexCount > vertexCount</code>
+ * </ul>
+ * <p>
+ * @exception ArrayIndexOutOfBoundsException if the geometry data format
+ * is <code>BY_REFERENCE</code> and any the following
+ * are true for non-null array references:
+ * <ul>
+ * <code>CoordRef.length</code> < <i>num_words</i> *
+ * (<code>initialCoordIndex + validVertexCount</code>),<br>
+ * <code>ColorRef.length</code> < <i>num_words</i> *
+ * (<code>initialColorIndex + validVertexCount</code>),<br>
+ * <code>NormalRef.length</code> < <i>num_words</i> *
+ * (<code>initialNormalIndex + validVertexCount</code>),<br>
+ * <code>TexCoordRef.length</code> < <i>num_words</i> *
+ * (<code>initialTexCoordIndex + validVertexCount</code>),<br>
+ * <code>InterleavedVertices.length</code> < <i>words_per_vertex</i> *
+ * (<code>initialVertexIndex + validVertexCount</code>)<br>
+ * </ul>
+ * where <i>num_words</i> depends on which variant of
+ * <code>set</code><i>Array</i><code>Ref</code> is used, and
+ * <i>words_per_vertex</i> depends on which vertex formats are enabled
+ * for interleaved arrays.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setValidVertexCount(int validVertexCount) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray88"));
+
+ if (validVertexCount < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray96"));
+
+ ((GeometryArrayRetained)this.retained).setValidVertexCount(validVertexCount);
+ // NOTE: the checks for initial*Index + validVertexCount >
+ // vertexCount need to be done in the retained method
+ }
+
+
+ /**
+ * Gets the valid vertex count for this GeometryArray object.
+ * For geometry strip primitives (subclasses of GeometryStripArray),
+ * the valid vertex count is defined to be the sum of the
+ * stripVertexCounts array.
+ * @return the current valid vertex count
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getValidVertexCount() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray89"));
+
+ return ((GeometryArrayRetained)this.retained).getValidVertexCount();
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code>
+ * into the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+ // vertexFormat and vertexCount are copied in subclass when constructor
+ // public GeometryArray(int vertexCount, int vertexFormat) is used
+ // in cloneNodeComponent()
+ GeometryArrayRetained src = (GeometryArrayRetained) originalNodeComponent.retained;
+ GeometryArrayRetained dst = (GeometryArrayRetained) retained;
+ int format = src.getVertexFormat();
+ if ((format & BY_REFERENCE) == 0) {
+ System.arraycopy(src.vertexData, 0, dst.vertexData, 0,
+ src.vertexData.length);
+ dst.setInitialVertexIndex(src.getInitialVertexIndex());
+
+ } else {
+ dst.setInitialCoordIndex(src.getInitialCoordIndex());
+ dst.setInitialColorIndex(src.getInitialColorIndex());
+ dst.setInitialNormalIndex(src.getInitialNormalIndex());
+ int setCount = src.getTexCoordSetCount();
+ for (int i=0; i < setCount; i++) {
+ dst.setInitialTexCoordIndex(i, src.getInitialTexCoordIndex(i));
+ }
+ if ((format & INTERLEAVED) == 0) {
+ dst.setCoordRefFloat(src.getCoordRefFloat());
+ dst.setCoordRefDouble(src.getCoordRefDouble());
+ dst.setCoordRef3f(src.getCoordRef3f());
+ dst.setCoordRef3d(src.getCoordRef3d());
+ dst.setColorRefFloat(src.getColorRefFloat());
+ dst.setColorRefByte(src.getColorRefByte());
+ if ((format & WITH_ALPHA) == 0) {
+ dst.setColorRef3f(src.getColorRef3f());
+ dst.setColorRef3b(src.getColorRef3b());
+ } else {
+ dst.setColorRef4f(src.getColorRef4f());
+ dst.setColorRef4b(src.getColorRef4b());
+ }
+ dst.setNormalRefFloat(src.getNormalRefFloat());
+ dst.setNormalRef3f(src.getNormalRef3f());
+ for (int i=0; i < setCount; i++) {
+ dst.setTexCoordRefFloat(i, src.getTexCoordRefFloat(i));
+ }
+ if ((format & TEXTURE_COORDINATE_2) != 0) {
+ for (int i=0; i < setCount; i++) {
+ dst.setTexCoordRef2f(i, src.getTexCoordRef2f(i));
+ }
+ }
+ if ((format & TEXTURE_COORDINATE_3) != 0) {
+ for (int i=0; i < setCount; i++) {
+ dst.setTexCoordRef3f(i, src.getTexCoordRef3f(i));
+ }
+ }
+ } else {
+ dst.setInterleavedVertices(src.getInterleavedVertices());
+ }
+ }
+ }
+
+
+ //------------------------------------------------------------------
+ // By-copying methods
+ //------------------------------------------------------------------
+
+ /**
+ * Sets the initial vertex index for this GeometryArray object.
+ * This index specifies the first vertex within this geometry
+ * array that is actually used in rendering or other operations
+ * such as picking and collision. This attribute is initialized
+ * to 0.
+ * This attribute is only used when the data mode for this
+ * geometry array object is not <code>BY_REFERENCE</code>
+ * or when the data mode is <code>INTERLEAVED</code>.
+ *
+ * @param initialVertexIndex the new initial vertex index.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalArgumentException if either of the following are
+ * true:
+ * <ul>
+ * <code>initialVertexIndex < 0</code> or<br>
+ * <code>initialVertexIndex + validVertexCount > vertexCount</code><br>
+ * </ul>
+ *
+ * @exception ArrayIndexOutOfBoundsException if the geometry data format
+ * is <code>INTERLEAVED</code>, the InterleavedVertices array is
+ * non-null, and:
+ * <ul>
+ * <code>InterleavedVertices.length</code> < <i>num_words</i> *
+ * (<code>initialVertexIndex + validVertexCount</code>)<br>
+ * </ul>
+ * where <i>num_words</i> depends on which vertex formats are enabled.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code> and is <i>not</i>
+ * <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setInitialVertexIndex(int initialVertexIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray90"));
+
+ if (initialVertexIndex < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray97"));
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0 && (format & INTERLEAVED) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray105"));
+
+ ((GeometryArrayRetained)this.retained).setInitialVertexIndex(initialVertexIndex);
+ // NOTE: the check for initialVertexIndex + validVertexCount >
+ // vertexCount is done in the retained method
+ }
+
+
+ /**
+ * Gets the initial vertex index for this GeometryArray object.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <i>not</i> <code>BY_REFERENCE</code>
+ * or when the data mode is <code>INTERLEAVED</code>.
+ * @return the current initial vertex index for this GeometryArray object.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getInitialVertexIndex() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray91"));
+
+ return ((GeometryArrayRetained)this.retained).getInitialVertexIndex();
+ }
+
+
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param coordinate source array of 3 values containing the new coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinate(int index, float coordinate[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray3"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinate(index, coordinate);
+ }
+
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index destination vertex index in this geometry array
+ * @param coordinate source array of 3 values containing the new coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinate(int index, double coordinate[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray3"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinate(index, coordinate);
+ }
+
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param coordinate a point containing the new coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinate(int index, Point3f coordinate) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray3"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinate(index, coordinate);
+ }
+
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param coordinate a point containing the new coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinate(int index, Point3d coordinate) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray3"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinate(index, coordinate);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of 3*n values containing n new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, float coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of 3*n values containing n new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, double coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of points containing new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, Point3f coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of points containing new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, Point3d coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of 3*n values containing n new coordinates
+ * @param start starting source vertex index in <code>coordinates</code> array.
+ * @param length number of vertices to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, float coordinates[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates,
+ start, length);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of 3*n values containing n new coordinates
+ * @param start starting source vertex index in <code>coordinates</code> array.
+ * @param length number of vertices to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, double coordinates[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates,
+ start, length);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of points containing new coordinates
+ * @param start starting source vertex index in <code>coordinates</code> array.
+ * @param length number of vertices to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, Point3f coordinates[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates,
+ start, length);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index starting destination vertex index in this geometry array
+ * @param coordinates source array of points containing new coordinates
+ * @param start starting source vertex index in <code>coordinates</code> array.
+ * @param length number of vertices to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setCoordinates(int index, Point3d coordinates[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray7"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).setCoordinates(index, coordinates,
+ start, length);
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param color source array of 3 or 4 values containing the new color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setColor(int index, float color[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray15"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).setColor(index, color);
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param color source array of 3 or 4 values containing the new color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setColor(int index, byte color[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray15"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).setColor(index, color);
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param color a Color3f containing the new color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in the vertex
+ * format
+ */
+ public void setColor(int index, Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray15"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColor(index, color);
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param color a Color4f containing the new color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in the vertex
+ * format
+ */
+ public void setColor(int index, Color4f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray15"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColor(index, color);
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param color a Color3b containing the new color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in the vertex
+ * format
+ */
+ public void setColor(int index, Color3b color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray15"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColor(index, color);
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param color a Color4b containing the new color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in the vertex
+ * format
+ */
+ public void setColor(int index, Color4b color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray15"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColor(index, color);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of 3*n or 4*n values containing n new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setColors(int index, float colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of 3*n or 4*n values containing n new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setColors(int index, byte colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color3f objects containing new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in vertex format
+ */
+ public void setColors(int index, Color3f colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color4f objects containing new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in vertex format
+ */
+ public void setColors(int index, Color4f colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color3b objects containing new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in vertex format
+ */
+ public void setColors(int index, Color3b colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color4b objects containing new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in vertex format
+ */
+ public void setColors(int index, Color4b colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>colors</code>
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of 3*n or 4*n values containing n new colors
+ * @param start starting source vertex index in <code>colors</code> array.
+ * @param length number of colors to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setColors(int index, float colors[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors, start,
+ length);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>colors</code>
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of 3*n or 4*n values containing n new colors
+ * @param start starting source vertex index in <code>colors</code> array.
+ * @param length number of colors to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setColors(int index, byte colors[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors, start,
+ length);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>colors</code>
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color3f objects containing new colors
+ * @param start starting source vertex index in <code>colors</code> array.
+ * @param length number of colors to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in vertex format
+ */
+ public void setColors(int index, Color3f colors[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors, start,
+ length);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>colors</code>
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color4f objects containing new colors
+ * @param start starting source vertex index in <code>colors</code> array.
+ * @param length number of colors to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in vertex format
+ */
+ public void setColors(int index, Color4f colors[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors, start,
+ length);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>colors</code>
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color3b objects containing new colors
+ * @param start starting source vertex index in <code>colors</code> array.
+ * @param length number of colors to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in vertex format
+ */
+ public void setColors(int index, Color3b colors[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors, start,
+ length);
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>colors</code>
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index starting destination vertex index in this geometry array
+ * @param colors source array of Color4b objects containing new colors
+ * @param start starting source vertex index in <code>colors</code> array.
+ * @param length number of colors to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if COLOR bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in vertex format
+ */
+ public void setColors(int index, Color4b colors[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray21"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColors(index, colors, start,
+ length);
+ }
+
+ /**
+ * Sets the normal associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param normal source array of 3 values containing the new normal
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if NORMALS bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setNormal(int index, float normal[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray33"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).setNormal(index, normal);
+ }
+
+ /**
+ * Sets the normal associated with the vertex at
+ * the specified index for this object.
+ * @param index destination vertex index in this geometry array
+ * @param normal the vector containing the new normal
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if NORMALS bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setNormal(int index, Vector3f normal) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray33"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).setNormal(index, normal);
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param normals source array of 3*n values containing n new normals
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if NORMALS bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setNormals(int index, float normals[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray35"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).setNormals(index, normals);
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index for this object. The entire source array is
+ * copied to this geometry array.
+ * @param index starting destination vertex index in this geometry array
+ * @param normals source array of vectors containing new normals
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if NORMALS bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setNormals(int index, Vector3f normals[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray35"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).setNormals(index, normals);
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index for this object using data in <code>normals</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index starting destination vertex index in this geometry array
+ * @param normals source array of 3*n values containing n new normals
+ * @param start starting source vertex index in <code>normals</code> array.
+ * @param length number of normals to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if NORMALS bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setNormals(int index, float normals[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray35"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).setNormals(index, normals, start, length);
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index for this object using data in <code>normals</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index starting destination vertex index in this geometry array
+ * @param normals source array of vectors containing new normals
+ * @param start starting source vertex index in <code>normals</code> array.
+ * @param length number of normals to be copied.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception ArrayIndexOutOfBoundsException if NORMALS bit NOT set in
+ * constructor <code>vertexFormat</code> or array index for element is out of bounds.
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void setNormals(int index, Vector3f normals[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray35"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).setNormals(index, normals, start, length);
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinate(int texCoordSet, ...)</code>
+ */
+ public void setTextureCoordinate(int index, float texCoord[]) {
+ setTextureCoordinate(0, index, texCoord);
+ }
+
+ /**
+ * Sets the texture coordinate associated with the vertex at the
+ * specified index in the specified texture coordinate set for
+ * this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index destination vertex index in this geometry array
+ * @param texCoord source array of 2, 3 or 4 values containing the new
+ * texture coordinate
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinate(int texCoordSet,
+ int index, float texCoord[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray39"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoord, 0, 1);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinate(int texCoordSet, TexCoord2f texCoord)</code>
+ */
+ public void setTextureCoordinate(int index, Point2f texCoord) {
+ texCoord2fScratch.set(texCoord);
+ setTextureCoordinate(0, index, texCoord2fScratch);
+ }
+
+ /**
+ * Sets the texture coordinate associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index destination vertex index in this geometry array
+ * @param texCoord the TexCoord2f containing the new texture coordinate
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_3 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinate(int texCoordSet,
+ int index, TexCoord2f texCoord) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray39"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_3 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray94"));
+
+ texCoord2fArray[0] = texCoord;
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoord2fArray, 0, 1);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinate(int texCoordSet, TexCoord3f texCoord)</code>
+ */
+ public void setTextureCoordinate(int index, Point3f texCoord) {
+ texCoord3fScratch.set(texCoord);
+ setTextureCoordinate(0, index, texCoord3fScratch);
+ }
+
+ /**
+ * Sets the texture coordinate associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index destination vertex index in this geometry array
+ * @param texCoord the TexCoord3f containing the new texture coordinate
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinate(int texCoordSet,
+ int index, TexCoord3f texCoord) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray39"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
+
+ texCoord3fArray[0] = texCoord;
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoord3fArray, 0, 1);
+ }
+
+ /**
+ * Sets the texture coordinate associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index destination vertex index in this geometry array
+ * @param texCoord the TexCoord4f containing the new texture coordinate
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_3 is specified in vertex format
+ *
+ * @since Java 3D 1.3
+ */
+ public void setTextureCoordinate(int texCoordSet,
+ int index, TexCoord4f texCoord) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray39"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray109"));
+
+ texCoord4fArray[0] = texCoord;
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoord4fArray, 0, 1);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinates(int texCoordSet, ...)</code>
+ */
+ public void setTextureCoordinates(int index, float texCoords[]) {
+ setTextureCoordinates(0, index, texCoords);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The entire source array is
+ * copied to this geometry array.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of 2*n, 3*n or 4*n values containing n new
+ * texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, float texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & GeometryArray.TEXTURE_COORDINATE_2) != 0)
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, 0, texCoords.length / 2);
+ else if ((format & GeometryArray.TEXTURE_COORDINATE_3) != 0)
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, 0, texCoords.length / 3);
+ else
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, 0, texCoords.length / 4);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinates(int texCoordSet, TexCoord2f texCoords[])</code>
+ */
+ public void setTextureCoordinates(int index, Point2f texCoords[]) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ 0, index, texCoords, 0, texCoords.length);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The entire source array is
+ * copied to this geometry array.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of TexCoord2f objects containing new
+ * texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_3 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, TexCoord2f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_3 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray94"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, 0, texCoords.length);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinates(int texCoordSet, TexCoord3f texCoords[])</code>
+ */
+ public void setTextureCoordinates(int index, Point3f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ 0, index, texCoords, 0, texCoords.length);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The entire source array is
+ * copied to this geometry array.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of TexCoord3f objects containing new
+ * texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, TexCoord3f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, 0, texCoords.length);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The entire source array is
+ * copied to this geometry array.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of TexCoord4f objects containing new
+ * texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_3 is specified in vertex format
+ *
+ * @since Java 3D 1.3
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, TexCoord4f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray109"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, 0, texCoords.length);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinates(int texCoordSet, ...)</code>
+ */
+ public void setTextureCoordinates(int index, float texCoords[],
+ int start, int length) {
+ setTextureCoordinates(0, index, texCoords, start, length);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices
+ * starting at the specified index in the specified texture
+ * coordinate set for this object using data in
+ * <code>texCoords</code> starting at index <code>start</code> and
+ * ending at index <code>start+length</code>.
+ *
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of 2*n , 3*n or 4*n values containing
+ * n new * texture coordinates
+ * @param start starting source vertex index in <code>texCoords</code>
+ * array.
+ * @param length number of texture Coordinates to be copied.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, float texCoords[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, start, length);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinates(int texCoordSet, TexCoord2f texCoords[], ...)</code>
+ */
+ public void setTextureCoordinates(int index, Point2f texCoords[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ 0, index, texCoords, start, length);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices
+ * starting at the specified index in the specified texture
+ * coordinate set for this object using data in
+ * <code>texCoords</code> starting at index <code>start</code> and
+ * ending at index <code>start+length</code>.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of TexCoord2f objects containing new
+ * texture coordinates
+ * @param start starting source vertex index in <code>texCoords</code>
+ * array.
+ * @param length number of texture Coordinates to be copied.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_3 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, TexCoord2f texCoords[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_3 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray94"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, start, length);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinates(int texCoordSet, TexCoord3f texCoords[], ...)</code>
+ */
+ public void setTextureCoordinates(int index, Point3f texCoords[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ 0, index, texCoords, start, length);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices
+ * starting at the specified index in the specified texture
+ * coordinate set for this object. starting at index
+ * <code>start</code> and ending at index <code>start+length</code>.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of TexCoord3f objects containing new
+ * texture coordinates
+ * @param start starting source vertex index in <code>texCoords</code>
+ * array.
+ * @param length number of texture Coordinates to be copied.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, TexCoord3f texCoords[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, start, length);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices
+ * starting at the specified index in the specified texture
+ * coordinate set for this object. starting at index
+ * <code>start</code> and ending at index <code>start+length</code>.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting destination vertex index in this geometry array
+ * @param texCoords source array of TexCoord4f objects containing new
+ * texture coordinates
+ * @param start starting source vertex index in <code>texCoords</code>
+ * array.
+ * @param length number of texture Coordinates to be copied.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_3 is specified in vertex format
+ *
+ * @since Java 3D 1.3
+ */
+ public void setTextureCoordinates(int texCoordSet,
+ int index, TexCoord4f texCoords[],
+ int start, int length) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray42"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray109"));
+
+ ((GeometryArrayRetained)this.retained).setTextureCoordinates(
+ texCoordSet, index, texCoords, start, length);
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index for this object using data in <code>texCoords</code>
+ * @param index source vertex index in this geometry array
+ * @param coordinate destination array of 3 values that will receive the coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinate(int index, float coordinate[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinate(index, coordinate);
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param coordinate destination array of 3 values that will receive the coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinate(int index, double coordinate[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinate(index, coordinate);
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param coordinate a vector that will receive the coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinate(int index, Point3f coordinate) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinate(index, coordinate);
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param coordinate a vector that will receive the coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinate(int index, Point3d coordinate) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinate(index, coordinate);
+ }
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index for this object. The length of the destination
+ * array determines the number of vertices copied.
+ * A maximum of <code>vertexCount-index</code> coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the coordinates, only as
+ * many coordinates as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param coordinates destination array of 3*n values that will receive new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinates(int index, float coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray52"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinates(index, coordinates);
+ }
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index for this object. The length of the destination
+ * array determines the number of vertices copied.
+ * A maximum of <code>vertexCount-index</code> coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the coordinates, only as
+ * many coordinates as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param coordinates destination array of 3*n values that will receive new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinates(int index, double coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray52"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinates(index, coordinates);
+ }
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index for this object. The length of the destination
+ * array determines the number of vertices copied.
+ * A maximum of <code>vertexCount-index</code> coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the coordinates, only as
+ * many coordinates as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param coordinates destination array of points that will receive new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinates(int index, Point3f coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray52"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinates(index, coordinates);
+ }
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index for this object. The length of the destination
+ * array determines the number of vertices copied.
+ * A maximum of <code>vertexCount-index</code> coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the coordinates, only as
+ * many coordinates as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param coordinates destination array of points that will receive new coordinates
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getCoordinates(int index, Point3d coordinates[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray52"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ ((GeometryArrayRetained)this.retained).getCoordinates(index, coordinates);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index for this object. The color is copied into the
+ * specified array. The array must be large enough to hold all
+ * of the colors.
+ * @param index source vertex index in this geometry array
+ * @param color destination array of 3 or 4 values that will receive the color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getColor(int index, float color[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray56"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).getColor(index, color);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index for this object. The color is copied into the
+ * specified array. The array must be large enough to hold all of
+ * the colors.
+ * @param index source vertex index in this geometry array
+ * @param color destination array of 3 or 4 values that will receive the color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getColor(int index, byte color[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray56"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).getColor(index, color);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param color a vector that will receive the color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in the vertex
+ * format
+ */
+ public void getColor(int index, Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray56"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).getColor(index, color);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param color a vector that will receive the color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in the vertex
+ * format
+ */
+ public void getColor(int index, Color4f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray56"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).getColor(index, color);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param color a vector that will receive the color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in the vertex
+ * format
+ */
+ public void getColor(int index, Color3b color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray56"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).getColor(index, color);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param color a vector that will receive the color
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in the vertex
+ * format
+ */
+ public void getColor(int index, Color4b color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray56"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).getColor(index, color);
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index for this object. The color is copied into the
+ * specified array. The length of the destination
+ * array determines the number of colors copied.
+ * A maximum of <code>vertexCount-index</code> colors
+ * are copied. If the destination array is larger than is needed
+ * to hold the colors, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the colors, only as
+ * many colors as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param colors destination array of 3*n or 4*n values that will
+ * receive n new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getColors(int index, float colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray62"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).getColors(index, colors);
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index for this object. The color is copied into the
+ * specified array. The length of the destination
+ * array determines the number of colors copied.
+ * A maximum of <code>vertexCount-index</code> colors
+ * are copied. If the destination array is larger than is needed
+ * to hold the colors, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the colors, only as
+ * many colors as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param colors destination array of 3*n or 4*n values that will
+ * receive new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getColors(int index, byte colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray62"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ ((GeometryArrayRetained)this.retained).getColors(index, colors);
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index for this object. The color is copied into the
+ * specified array. The length of the destination
+ * array determines the number of colors copied.
+ * A maximum of <code>vertexCount-index</code> colors
+ * are copied. If the destination array is larger than is needed
+ * to hold the colors, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the colors, only as
+ * many colors as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param colors destination array of Color3f objects that will receive new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in the vertex
+ * format
+ */
+ public void getColors(int index, Color3f colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray62"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).getColors(index, colors);
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index for this object. The color is copied into the
+ * specified array. The length of the destination
+ * array determines the number of colors copied.
+ * A maximum of <code>vertexCount-index</code> colors
+ * are copied. If the destination array is larger than is needed
+ * to hold the colors, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the colors, only as
+ * many colors as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param colors destination array of Color4f objects that will receive new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in the vertex
+ * format
+ */
+ public void getColors(int index, Color4f colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray62"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).getColors(index, colors);
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index for this object. The color is copied into the
+ * specified array. The length of the destination
+ * array determines the number of colors copied.
+ * A maximum of <code>vertexCount-index</code> colors
+ * are copied. If the destination array is larger than is needed
+ * to hold the colors, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the colors, only as
+ * many colors as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param colors destination array of Color3b objects that will receive new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_4 is specified in the vertex
+ * format
+ */
+ public void getColors(int index, Color3b colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray62"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).getColors(index, colors);
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index for this object. The color is copied into the
+ * specified array. The length of the destination
+ * array determines the number of colors copied.
+ * A maximum of <code>vertexCount-index</code> colors
+ * are copied. If the destination array is larger than is needed
+ * to hold the colors, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the colors, only as
+ * many colors as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param colors destination array of Color4b objects that will receive new colors
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ * @exception IllegalStateException if COLOR_3 is specified in the vertex
+ * format
+ */
+ public void getColors(int index, Color4b colors[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray62"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & COLOR ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray76"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).getColors(index, colors);
+ }
+
+ /**
+ * Gets the normal associated with the vertex at
+ * the specified index for this object. The normal is copied into
+ * the specified array.
+ * @param index source vertex index in this geometry array
+ * @param normal destination array of 3 values that will receive the normal
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getNormal(int index, float normal[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray68"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).getNormal(index, normal);
+ }
+
+ /**
+ * Gets the normal associated with the vertex at
+ * the specified index for this object.
+ * @param index source vertex index in this geometry array
+ * @param normal the vector that will receive the normal
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getNormal(int index, Vector3f normal) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray68"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).getNormal(index, normal);
+ }
+
+ /**
+ * Gets the normals associated with the vertices starting at
+ * the specified index for this object. The length of the destination
+ * array determines the number of normals copied.
+ * A maximum of <code>vertexCount-index</code> normals
+ * are copied. If the destination array is larger than is needed
+ * to hold the normals, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the normals, only as
+ * many normals as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param normals destination array of 3*n values that will receive the normal
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getNormals(int index, float normals[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray70"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).getNormals(index, normals);
+ }
+
+ /**
+ * Gets the normals associated with the vertices starting at
+ * the specified index for this object. The length of the destination
+ * array determines the number of normals copied.
+ * A maximum of <code>vertexCount-index</code> normals
+ * are copied. If the destination array is larger than is needed
+ * to hold the normals, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the normals, only as
+ * many normals as the array will hold are copied.
+ *
+ * @param index starting source vertex index in this geometry array
+ * @param normals destination array of vectors that will receive the normals
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ */
+ public void getNormals(int index, Vector3f normals[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray70"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & NORMALS ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray77"));
+
+ ((GeometryArrayRetained)this.retained).getNormals(index, normals);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinate(int texCoordSet, ...)</code>
+ */
+ public void getTextureCoordinate(int index, float texCoord[]) {
+ getTextureCoordinate(0, index, texCoord);
+ }
+
+ /**
+ * Gets the texture coordinate associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index source vertex index in this geometry array
+ * @param texCoord array of 2, 3 or 4 values that will receive the
+ * texture coordinate
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureCoordinate(int texCoordSet,
+ int index, float texCoord[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray72"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ ((GeometryArrayRetained)this.retained).getTextureCoordinate(
+ texCoordSet, index, texCoord);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinate(int texCoordSet, TexCoord2f texCoord)</code>
+ */
+ public void getTextureCoordinate(int index, Point2f texCoord) {
+ getTextureCoordinate(0, index, texCoord2fArray[0]);
+ texCoord.set(texCoord2fArray[0]);
+ }
+
+ /**
+ * Gets the texture coordinate associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index source vertex index in this geometry array
+ * @param texCoord the vector that will receive the texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_3 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureCoordinate(int texCoordSet,
+ int index, TexCoord2f texCoord) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray72"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_3 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray94"));
+
+ ((GeometryArrayRetained)this.retained).getTextureCoordinate(
+ texCoordSet, index, texCoord);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinate(int texCoordSet, TexCoord3f texCoord)</code>
+ */
+ public void getTextureCoordinate(int index, Point3f texCoord) {
+ getTextureCoordinate(0, index, texCoord3fArray[0]);
+ texCoord.set(texCoord3fArray[0]);
+ }
+
+ /**
+ * Gets the texture coordinate associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index source vertex index in this geometry array
+ * @param texCoord the vector that will receive the texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureCoordinate(int texCoordSet,
+ int index, TexCoord3f texCoord) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray72"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
+ ((GeometryArrayRetained)this.retained).getTextureCoordinate(
+ texCoordSet, index, texCoord);
+ }
+
+ /**
+ * Gets the texture coordinate associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index source vertex index in this geometry array
+ * @param texCoord the vector that will receive the texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_3 is specified in vertex format
+ *
+ * @since Java 3D 1.3
+ */
+ public void getTextureCoordinate(int texCoordSet,
+ int index, TexCoord4f texCoord) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray72"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray109"));
+ ((GeometryArrayRetained)this.retained).getTextureCoordinate(
+ texCoordSet, index, texCoord);
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinates(int texCoordSet, ...)</code>
+ */
+ public void getTextureCoordinates(int index, float texCoords[]) {
+ getTextureCoordinates(0, index, texCoords);
+ }
+
+ /**
+ * Gets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The length of the destination
+ * array determines the number of texture coordinates copied.
+ * A maximum of <code>vertexCount-index</code> texture coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the texture coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the texture coordinates, only as
+ * many texture coordinates as the array will hold are copied.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting source vertex index in this geometry array
+ * @param texCoords destination array of 2*n , 3*n or 4*n values that
+ * will receive n new texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureCoordinates(int texCoordSet,
+ int index, float texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ ((GeometryArrayRetained)this.retained).getTextureCoordinates(
+ texCoordSet, index, texCoords);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinates(int texCoordSet, TexCoord2f texCoords[])</code>
+ */
+ public void getTextureCoordinates(int index, Point2f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ ((GeometryArrayRetained)this.retained).getTextureCoordinates(
+ 0, index, texCoords);
+ }
+
+ /**
+ * Gets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The length of the destination
+ * array determines the number of texture coordinates copied.
+ * A maximum of <code>vertexCount-index</code> texture coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the texture coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the texture coordinates, only as
+ * many texture coordinates as the array will hold are copied.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting source vertex index in this geometry array
+ * @param texCoords destination array of TexCoord2f objects that will
+ * receive the texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_3 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureCoordinates(int texCoordSet,
+ int index, TexCoord2f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_3 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray94"));
+ ((GeometryArrayRetained)this.retained).getTextureCoordinates(
+ texCoordSet, index, texCoords);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinates(int texCoordSet, TexCoord3f texCoords[])</code>
+ */
+ public void getTextureCoordinates(int index, Point3f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ ((GeometryArrayRetained)this.retained).getTextureCoordinates(
+ 0, index, texCoords);
+ }
+
+ /**
+ * Gets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The length of the destination
+ * array determines the number of texture coordinates copied.
+ * A maximum of <code>vertexCount-index</code> texture coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the texture coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the texture coordinates, only as
+ * many texture coordinates as the array will hold are copied.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting source vertex index in this geometry array
+ * @param texCoords destination array of TexCoord3f objects that will
+ * receive the texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_4 is specified in vertex format
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureCoordinates(int texCoordSet,
+ int index, TexCoord3f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
+ ((GeometryArrayRetained)this.retained).getTextureCoordinates(
+ texCoordSet, index, texCoords);
+ }
+
+
+ /**
+ * Gets the texture coordinates associated with the vertices starting at
+ * the specified index in the specified texture coordinate set
+ * for this object. The length of the destination
+ * array determines the number of texture coordinates copied.
+ * A maximum of <code>vertexCount-index</code> texture coordinates
+ * are copied. If the destination array is larger than is needed
+ * to hold the texture coordinates, the excess locations in the
+ * array are not modified. If the destination array is smaller
+ * than is needed to hold the texture coordinates, only as
+ * many texture coordinates as the array will hold are copied.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index starting source vertex index in this geometry array
+ * @param texCoords destination array of TexCoord4f objects that will
+ * receive the texture coordinates
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is <code>BY_REFERENCE</code>.
+ *
+ * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+ * TEXTURE_COORDINATE_3 is specified in vertex format
+ *
+ * @since Java 3D 1.3
+ */
+ public void getTextureCoordinates(int texCoordSet,
+ int index, TexCoord4f texCoords[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TEXCOORD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((format & TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+ (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray109"));
+
+ ((GeometryArrayRetained)this.retained).getTextureCoordinates(
+ texCoordSet, index, texCoords);
+ }
+
+
+ //------------------------------------------------------------------
+ // By-reference methods
+ //------------------------------------------------------------------
+
+ /**
+ * Sets the initial coordinate index for this GeometryArray object.
+ * This index specifies the first coordinate within the array of
+ * coordinates referenced by this geometry
+ * array that is actually used in rendering or other operations
+ * such as picking and collision. This attribute is initialized
+ * to 0.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ *
+ * @param initialCoordIndex the new initial coordinate index.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * <p>
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code> or if the data mode
+ * is <code>INTERLEAVED</code>.
+ * <p>
+ * @exception IllegalArgumentException if either of the following are
+ * true:
+ * <ul>
+ * <code>initialCoordIndex < 0</code> or<br>
+ * <code>initialCoordIndex + validVertexCount > vertexCount</code><br>
+ * </ul>
+ * <p>
+ * @exception ArrayIndexOutOfBoundsException if
+ * the CoordRef array is non-null and:
+ * <ul>
+ * <code>CoordRef.length</code> < <i>num_words</i> *
+ * (<code>initialCoordIndex + validVertexCount</code>)<br>
+ * </ul>
+ * where <i>num_words</i> depends on which variant of
+ * <code>setCoordRef</code> is used.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setInitialCoordIndex(int initialCoordIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray90"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if (initialCoordIndex < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray97"));
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setInitialCoordIndex(initialCoordIndex);
+ // NOTE: the check for initialCoordIndex + validVertexCount >
+ // vertexCount needs to be done in the retained method
+ }
+
+
+ /**
+ * Gets the initial coordinate index for this GeometryArray object.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ * @return the current initial coordinate index for this
+ * GeometryArray object.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getInitialCoordIndex() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray91"));
+
+ return ((GeometryArrayRetained)this.retained).getInitialCoordIndex();
+ }
+
+
+ /**
+ * Sets the initial color index for this GeometryArray object.
+ * This index specifies the first color within the array of
+ * colors referenced by this geometry
+ * array that is actually used in rendering or other operations
+ * such as picking and collision. This attribute is initialized
+ * to 0.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ *
+ * @param initialColorIndex the new initial color index.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * <p>
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code> or if the data mode
+ * is <code>INTERLEAVED</code>.
+ * <p>
+ * @exception IllegalArgumentException if either of the following are
+ * true:
+ * <ul>
+ * <code>initialColorIndex < 0</code> or<br>
+ * <code>initialColorIndex + validVertexCount > vertexCount</code><br>
+ * </ul>
+ * <p>
+ * @exception ArrayIndexOutOfBoundsException if
+ * the ColorRef array is non-null and:
+ * <ul>
+ * <code>ColorRef.length</code> < <i>num_words</i> *
+ * (<code>initialColorIndex + validVertexCount</code>)<br>
+ * </ul>
+ * where <i>num_words</i> depends on which variant of
+ * <code>setColorRef</code> is used.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setInitialColorIndex(int initialColorIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray90"));
+
+ if (initialColorIndex < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray97"));
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setInitialColorIndex(initialColorIndex);
+ // NOTE: the check for initialColorIndex + validVertexCount >
+ // vertexCount needs to be done in the retained method
+ }
+
+
+ /**
+ * Gets the initial color index for this GeometryArray object.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ * @return the current initial color index for this
+ * GeometryArray object.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getInitialColorIndex() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray91"));
+
+ return ((GeometryArrayRetained)this.retained).getInitialColorIndex();
+ }
+
+
+ /**
+ * Sets the initial normal index for this GeometryArray object.
+ * This index specifies the first normal within the array of
+ * normals referenced by this geometry
+ * array that is actually used in rendering or other operations
+ * such as picking and collision. This attribute is initialized
+ * to 0.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ *
+ * @param initialNormalIndex the new initial normal index.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * <p>
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code> or if the data mode
+ * is <code>INTERLEAVED</code>.
+ * <p>
+ * @exception IllegalArgumentException if either of the following are
+ * true:
+ * <ul>
+ * <code>initialNormalIndex < 0</code> or<br>
+ * <code>initialNormalIndex + validVertexCount > vertexCount</code><br>
+ * </ul>
+ * <p>
+ * @exception ArrayIndexOutOfBoundsException if normals
+ * the NormalRef array is non-null and:
+ * <ul>
+ * <code>NormalRef.length</code> < <i>num_words</i> *
+ * (<code>initialNormalIndex + validVertexCount</code>)<br>
+ * </ul>
+ * where <i>num_words</i> depends on which variant of
+ * <code>setNormalRef</code> is used.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setInitialNormalIndex(int initialNormalIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray90"));
+
+ if (initialNormalIndex < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray97"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setInitialNormalIndex(initialNormalIndex);
+ // NOTE: the check for initialNormalIndex + validVertexCount >
+ // vertexCount needs to be done in the retained method
+ }
+
+
+ /**
+ * Gets the initial normal index for this GeometryArray object.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ * @return the current initial normal index for this
+ * GeometryArray object.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getInitialNormalIndex() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray91"));
+
+ return ((GeometryArrayRetained)this.retained).getInitialNormalIndex();
+ }
+
+
+ /**
+ * Sets the initial texture coordinate index for the specified
+ * texture coordinate set for this GeometryArray object. This
+ * index specifies the first texture coordinate within the array
+ * of texture coordinates referenced by this geometry array that
+ * is actually used in rendering or other operations such as
+ * picking and collision. This attribute is initialized to 0.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param initialTexCoordIndex the new initial texture coordinate index.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * <p>
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code> or if the data mode
+ * is <code>INTERLEAVED</code>.
+ * <p>
+ * @exception IllegalArgumentException if either of the following are
+ * true:
+ * <ul>
+ * <code>initialTexCoordIndex < 0</code> or<br>
+ * <code>initialTexCoordIndex + validVertexCount > vertexCount</code><br>
+ * </ul>
+ * <p>
+ * @exception ArrayIndexOutOfBoundsException if
+ * the TexCoordRef array is non-null and:
+ * <ul>
+ * <code>TexCoordRef.length</code> < <i>num_words</i> *
+ * (<code>initialTexCoordIndex + validVertexCount</code>)<br>
+ * </ul>
+ * where <i>num_words</i> depends on which variant of
+ * <code>setTexCoordRef</code> is used.
+ * <p>
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if texCoordSet is out of range.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setInitialTexCoordIndex(int texCoordSet,
+ int initialTexCoordIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray90"));
+
+ if (initialTexCoordIndex < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray97"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setInitialTexCoordIndex(
+ texCoordSet, initialTexCoordIndex);
+
+ // NOTE: the check for initialTexCoordIndex + validVertexCount >
+ // vertexCount needs to be done in the retained method
+ }
+
+
+ /**
+ * Gets the initial texture coordinate index for the specified
+ * texture coordinate set for this GeometryArray object.
+ * This attribute is only used when the data mode for this
+ * geometry array object is <code>BY_REFERENCE</code>
+ * and is <i>not</i> </code>INTERLEAVED</code>.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ *
+ * @return the current initial texture coordinate index for the specified
+ * texture coordinate set
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if texCoordSet is out of range.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getInitialTexCoordIndex(int texCoordSet) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray91"));
+
+ return ((GeometryArrayRetained)this.retained).getInitialTexCoordIndex(
+ texCoordSet);
+ }
+
+
+ /**
+ * Sets the coordinate buffer reference to the specified
+ * buffer object. The buffer contains either a java.nio.FloatBuffer
+ * or java.nio.DoubleBuffer object containing single or double
+ * precision floating-point <i>x</i>, <i>y</i>,
+ * and <i>z</i> values for each vertex (for a total of 3*<i>n</i>
+ * values, where <i>n</i> is the number of vertices).
+ * If the coordinate buffer
+ * reference is null, the entire geometry array object is
+ * treated as if it were null--any Shape3D or Morph node that uses
+ * this geometry array will not be drawn.
+ *
+ * @param coords a J3DBuffer object to which a reference will be set.
+ * The buffer contains an NIO buffer of 3*<i>n</i> float or
+ * double values.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @exception IllegalArgumentException if the java.nio.Buffer
+ * contained in the specified J3DBuffer is not a
+ * java.nio.FloatBuffer or a java.nio.DoubleBuffer object.
+ *
+ * @exception ArrayIndexOutOfBoundsException if
+ * <code>coords.getBuffer().limit() <
+ * 3 * (initialCoordIndex + validVertexCount)</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the coordinate index array is greater than or equal to the
+ * number of vertices defined by the coords object,
+ * <code>coords.getBuffer().limit() / 3</code>.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCoordRefBuffer(J3DBuffer coords) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setCoordRefBuffer(coords);
+ }
+
+
+ /**
+ * Gets the coordinate array buffer reference.
+ * @return the current coordinate array buffer reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.3
+ */
+ public J3DBuffer getCoordRefBuffer() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getCoordRefBuffer();
+ }
+
+
+ /**
+ * Sets the float coordinate array reference to the specified
+ * array. The array contains floating-point <i>x</i>, <i>y</i>,
+ * and <i>z</i> values for each vertex (for a total of 3*<i>n</i>
+ * values, where <i>n</i> is the number of vertices). Only one of
+ * <code>coordRefFloat</code>, <code>coordRefDouble</code>,
+ * <code>coordRef3f</code>, or <code>coordRef3d</code> may be
+ * non-null (or they may all be null). An attempt to set more
+ * than one of these attributes to a non-null reference will
+ * result in an exception being thrown. If all coordinate array
+ * references are null, the entire geometry array object is
+ * treated as if it were null--any Shape3D or Morph node that uses
+ * this geometry array will not be drawn.
+ *
+ * @param coords an array of 3*<i>n</i> values to which a
+ * reference will be set.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any other coordinate reference is also non-null.
+ * @exception ArrayIndexOutOfBoundsException if
+ * <code>coords.length < 3 * (initialCoordIndex + validVertexCount)</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the coordinate index array is greater than or equal to the
+ * number of vertices defined by the coords array,
+ * <code>coords.length / 3</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setCoordRefFloat(float[] coords) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setCoordRefFloat(coords);
+
+ }
+
+
+ /**
+ * Gets the float coordinate array reference.
+ * @return the current float coordinate array reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public float[] getCoordRefFloat() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getCoordRefFloat();
+ }
+
+
+ /**
+ * Sets the double coordinate array reference to the specified
+ * array. The array contains double-precision
+ * floating-point <i>x</i>, <i>y</i>,
+ * and <i>z</i> values for each vertex (for a total of 3*<i>n</i>
+ * values, where <i>n</i> is the number of vertices). Only one of
+ * <code>coordRefFloat</code>, <code>coordRefDouble</code>,
+ * <code>coordRef3f</code>, or <code>coordRef3d</code> may be
+ * non-null (or they may all be null). An attempt to set more
+ * than one of these attributes to a non-null reference will
+ * result in an exception being thrown. If all coordinate array
+ * references are null, the entire geometry array object is
+ * treated as if it were null--any Shape3D or Morph node that uses
+ * this geometry array will not be drawn.
+ *
+ * @param coords an array of 3*<i>n</i> values to which a
+ * reference will be set.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any other coordinate reference is also non-null.
+ * @exception ArrayIndexOutOfBoundsException if
+ * <code>coords.length < 3 * (initialCoordIndex + validVertexCount)</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the coordinate index array is greater than or equal to the
+ * number of vertices defined by the coords array,
+ * <code>coords.length / 3</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setCoordRefDouble(double[] coords) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setCoordRefDouble(coords);
+
+ }
+
+
+ /**
+ * Gets the double coordinate array reference.
+ * @return the current double coordinate array reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public double[] getCoordRefDouble() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getCoordRefDouble();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Point3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setCoordRef3f(Point3f[] coords) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setCoordRef3f(coords);
+
+
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Point3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public Point3f[] getCoordRef3f() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getCoordRef3f();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Point3d arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setCoordRef3d(Point3d[] coords) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setCoordRef3d(coords);
+
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Point3d arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public Point3d[] getCoordRef3d() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getCoordRef3d();
+ }
+
+
+ /**
+ * Sets the color buffer reference to the specified
+ * buffer object. The buffer contains either a java.nio.FloatBuffer
+ * or java.nio.ByteBuffer object containing floating-point
+ * or byte <i>red</i>, <i>green</i>,
+ * <i>blue</i>, and, optionally, <i>alpha</i> values for each
+ * vertex (for a total of 3*<i>n</i> or 4*<i>n</i> values, where
+ * <i>n</i> is the number of vertices).
+ * If the color buffer reference is null and colors are enabled
+ * (that is, the vertexFormat includes either <code>COLOR_3</code> or
+ * <code>COLOR_4</code>), the entire geometry array object is
+ * treated as if it were null--any Shape3D or Morph node that uses
+ * this geometry array will not be drawn.
+ *
+ * @param colors a J3DBuffer object to which a reference will be set.
+ * The buffer contains an NIO buffer of 3*<i>n</i> or 4*<i>n</i>
+ * float or byte values.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @exception IllegalArgumentException if the java.nio.Buffer
+ * contained in the specified J3DBuffer is not a
+ * java.nio.FloatBuffer or a java.nio.ByteBuffer object.
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>COLOR</code> bits are set in the
+ * <code>vertexFormat</code>, or if
+ * <code>colors.getBuffer().limit() < </code> <i>num_words</i> <code> *
+ * (initialColorIndex + validVertexCount)</code>,
+ * where <i>num_words</i> is 3 or 4 depending on the vertex color format.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the color index array is greater than or equal to the
+ * number of vertices defined by the colors object,
+ * <code>colors.getBuffer().limit() / </code> <i>num_words</i>.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setColorRefBuffer(J3DBuffer colors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setColorRefBuffer(colors);
+
+ }
+
+
+ /**
+ * Gets the color array buffer reference.
+ * @return the current color array buffer reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.3
+ */
+ public J3DBuffer getColorRefBuffer() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getColorRefBuffer();
+ }
+
+
+ /**
+ * Sets the float color array reference to the specified array.
+ * The array contains floating-point <i>red</i>, <i>green</i>,
+ * <i>blue</i>, and, optionally, <i>alpha</i> values for each
+ * vertex (for a total of 3*<i>n</i> or 4*<i>n</i> values, where
+ * <i>n</i> is the number of vertices). Only one of
+ * <code>colorRefFloat</code>, <code>colorRefByte</code>,
+ * <code>colorRef3f</code>, <code>colorRef4f</code>,
+ * <code>colorRef3b</code>, or <code>colorRef4b</code> may be
+ * non-null (or they may all be null). An attempt to set more
+ * than one of these attributes to a non-null reference will
+ * result in an exception being thrown. If all color array
+ * references are null and colors are enabled (that is, the
+ * vertexFormat includes either <code>COLOR_3</code> or
+ * <code>COLOR_4</code>), the entire geometry array object is
+ * treated as if it were null--any Shape3D or Morph node that uses
+ * this geometry array will not be drawn.
+ *
+ * @param colors an array of 3*<i>n</i> or 4*<i>n</i> values to which a
+ * reference will be set.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any other color reference is also non-null.
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>COLOR</code> bits are set in the
+ * <code>vertexFormat</code>, or if
+ * <code>colors.length < </code> <i>num_words</i> <code> *
+ * (initialColorIndex + validVertexCount)</code>,
+ * where <i>num_words</i> is 3 or 4 depending on the vertex color format.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the color index array is greater than or equal to the
+ * number of vertices defined by the colors array,
+ * <code>colors.length / </code> <i>num_words</i>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setColorRefFloat(float[] colors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setColorRefFloat(colors);
+
+ }
+
+
+ /**
+ * Gets the float color array reference.
+ * @return the current float color array reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public float[] getColorRefFloat() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getColorRefFloat();
+ }
+
+
+ /**
+ * Sets the byte color array reference to the specified array.
+ * The array contains <i>red</i>, <i>green</i>,
+ * <i>blue</i>, and, optionally, <i>alpha</i> values for each
+ * vertex (for a total of 3*<i>n</i> or 4*<i>n</i> values, where
+ * <i>n</i> is the number of vertices). Only one of
+ * <code>colorRefFloat</code>, <code>colorRefByte</code>,
+ * <code>colorRef3f</code>, <code>colorRef4f</code>,
+ * <code>colorRef3b</code>, or <code>colorRef4b</code> may be
+ * non-null (or they may all be null). An attempt to set more
+ * than one of these attributes to a non-null reference will
+ * result in an exception being thrown. If all color array
+ * references are null and colors are enabled (that is, the
+ * vertexFormat includes either <code>COLOR_3</code> or
+ * <code>COLOR_4</code>), the entire geometry array object is
+ * treated as if it were null--any Shape3D or Morph node that uses
+ * this geometry array will not be drawn.
+ *
+ * @param colors an array of 3*<i>n</i> or 4*<i>n</i> values to which a
+ * reference will be set.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any other color reference is also non-null.
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>COLOR</code> bits are set in the
+ * <code>vertexFormat</code>, or if
+ * <code>colors.length < </code> <i>num_words</i> <code> *
+ * (initialColorIndex + validVertexCount)</code>,
+ * where <i>num_words</i> is 3 or 4 depending on the vertex color format.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the color index array is greater than or equal to the
+ * number of vertices defined by the colors array,
+ * <code>colors.length / </code> <i>num_words</i>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setColorRefByte(byte[] colors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setColorRefByte(colors);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * Gets the byte color array reference.
+ * @return the current byte color array reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public byte[] getColorRefByte() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getColorRefByte();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setColorRef3f(Color3f[] colors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColorRef3f(colors);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public Color3f[] getColorRef3f() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getColorRef3f();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color4f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setColorRef4f(Color4f[] colors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColorRef4f(colors);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color4f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public Color4f[] getColorRef4f() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getColorRef4f();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color3b arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setColorRef3b(Color3b[] colors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ if ((format & WITH_ALPHA) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+
+ ((GeometryArrayRetained)this.retained).setColorRef3b(colors);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color3b arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public Color3b[] getColorRef3b() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getColorRef3b();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color4b arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setColorRef4b(Color4b[] colors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ if ((format & WITH_ALPHA) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+
+ ((GeometryArrayRetained)this.retained).setColorRef4b(colors);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Color4b arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public Color4b[] getColorRef4b() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getColorRef4b();
+ }
+
+
+ /**
+ * Sets the normal buffer reference to the specified
+ * buffer object. The buffer contains a java.nio.FloatBuffer
+ * object containing <i>nx</i>, <i>ny</i>,
+ * and <i>nz</i> values for each vertex (for a total of 3*<i>n</i>
+ * values, where <i>n</i> is the number of vertices).
+ * If the normal buffer reference is null and normals are enabled
+ * (that is, the vertexFormat includes <code>NORMAL</code>), the
+ * entire geometry array object is treated as if it were null--any
+ * Shape3D or Morph node that uses this geometry array will not be
+ * drawn.
+ *
+ * @param normals a J3DBuffer object to which a reference will be set.
+ * The buffer contains an NIO buffer of 3*<i>n</i> float values.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @exception IllegalArgumentException if the java.nio.Buffer
+ * contained in the specified J3DBuffer is not a
+ * java.nio.FloatBuffer object.
+ *
+ * @exception ArrayIndexOutOfBoundsException if
+ * <code>NORMALS</code> bit is not set in the
+ * <code>vertexFormat</code>, or if
+ * <code>normals.getBuffer().limit() <
+ * 3 * (initialNormalIndex + validVertexCount)</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the normal index array is greater than or equal to the
+ * number of vertices defined by the normals object,
+ * <code>normals.getBuffer().limit() / 3</code>.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setNormalRefBuffer(J3DBuffer normals) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setNormalRefBuffer(normals);
+ }
+
+
+ /**
+ * Gets the normal array buffer reference.
+ * @return the current normal array buffer reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.3
+ */
+ public J3DBuffer getNormalRefBuffer() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getNormalRefBuffer();
+ }
+
+
+ /**
+ * Sets the float normal array reference to the specified
+ * array. The array contains floating-point <i>nx</i>, <i>ny</i>,
+ * and <i>nz</i> values for each vertex (for a total of 3*<i>n</i>
+ * values, where <i>n</i> is the number of vertices). Only one of
+ * <code>normalRefFloat</code> or <code>normalRef3f</code> may be
+ * non-null (or they may all be null). An attempt to set more
+ * than one of these attributes to a non-null reference will
+ * result in an exception being thrown. If all normal array
+ * references are null and normals are enabled (that is, the
+ * vertexFormat includes
+ * <code>NORMAL</code>), the entire geometry array object is
+ * treated as if it were null--any Shape3D or Morph node that uses
+ * this geometry array will not be drawn.
+ *
+ * @param normals an array of 3*<i>n</i> values to which a
+ * reference will be set.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any other normal reference is also non-null.
+ * @exception ArrayIndexOutOfBoundsException if
+ * <code>NORMALS</code> bit is not set in the
+ * <code>vertexFormat</code>, or if
+ * <code>normals.length < 3 * (initialNormalIndex + validVertexCount)</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the normal index array is greater than or equal to the
+ * number of vertices defined by the normals array,
+ * <code>normals.length / 3</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setNormalRefFloat(float[] normals) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setNormalRefFloat(normals);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * Gets the float normal array reference.
+ * @return the current float normal array reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public float[] getNormalRefFloat() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getNormalRefFloat();
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Vector3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setNormalRef3f(Vector3f[] normals) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setNormalRef3f(normals);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for Vector3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public Vector3f[] getNormalRef3f() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getNormalRef3f();
+ }
+
+
+ /**
+ * Sets the texture coordinate array reference for the specified
+ * texture coordinate set to the
+ * specified buffer object. The buffer contains a java.nio.FloatBuffer
+ * object containing <i>s</i>,
+ * <i>t</i>, and, optionally, <i>r</i> and <i>q</i> values for each
+ * vertex (for
+ * a total of 2*<i>n</i> , 3*<i>n</i> or 4*<i>n</i> values,
+ * where <i>n</i> is
+ * the number of vertices).
+ * If the texCoord buffer reference is null and texture
+ * coordinates are enabled (that is, the vertexFormat includes
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code>, or
+ * <code>TEXTURE_COORDINATE_4</code>), the entire geometry
+ * array object is treated as if it were null--any Shape3D or
+ * Morph node that uses this geometry array will not be drawn.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param texCoords a J3DBuffer object to which a reference will be set.
+ * The buffer contains an NIO buffer of 2*<i>n</i>, 3*<i>n</i> or
+ * 4*<i>n</i> float values.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @exception IllegalArgumentException if the java.nio.Buffer
+ * contained in the specified J3DBuffer is not a
+ * java.nio.FloatBuffer object.
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code>, or if texCoordSet is out of range,
+ * or if
+ * <code>texCoords.getBuffer().limit() < </code> <i>num_words</i>
+ * <code> * (initialTexCoordIndex + validVertexCount)</code>,
+ * where <i>num_words</i> is 2, 3, or 4 depending on the vertex
+ * texture coordinate format.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the texture coordinate index array is greater than or equal to the
+ * number of vertices defined by the texCoords object,
+ * <code>texCoords.getBuffer().limit() / </code> <i>num_words</i>.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setTexCoordRefBuffer(int texCoordSet, J3DBuffer texCoords) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setTexCoordRefBuffer(
+ texCoordSet, texCoords);
+
+ }
+
+
+ /**
+ * Gets the texture coordinate array buffer reference for the specified
+ * texture coordinate set.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ *
+ * @return the current texture coordinate array buffer reference
+ * for the specified texture coordinate set
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or texCoordSet is out of range.
+ *
+ * @since Java 3D 1.3
+ */
+ public J3DBuffer getTexCoordRefBuffer(int texCoordSet) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getTexCoordRefBuffer(texCoordSet);
+ }
+
+
+ /**
+ * Sets the float texture coordinate array reference for the specified
+ * texture coordinate set to the
+ * specified array. The array contains floating-point <i>s</i>,
+ * <i>t</i>, and, optionally, <i>r</i> and <i>q</i> values for each
+ * vertex (for
+ * a total of 2*<i>n</i> , 3*<i>n</i> or 4*<i>n</i> values,
+ * where <i>n</i> is
+ * the number of vertices). Only one of
+ * <code>texCoordRefFloat</code>, <code>texCoordRef2f</code>, or
+ * <code>texCoordRef3f</code> may be non-null (or they may all be
+ * null). An attempt to set more than one of these attributes to
+ * a non-null reference will result in an exception being thrown.
+ * If all texCoord array references are null and texture
+ * coordinates are enabled (that is, the vertexFormat includes
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code>, or
+ * <code>TEXTURE_COORDINATE_4</code>), the entire geometry
+ * array object is treated as if it were null--any Shape3D or
+ * Morph node that uses this geometry array will not be drawn.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param texCoords an array of 2*<i>n</i>, 3*<i>n</i> or
+ * 4*<i>n</i> values to
+ * which a reference will be set.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any other texCoord reference is also non-null.
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code>, or if texCoordSet is out of range,
+ * or if
+ * <code>texCoords.length < </code> <i>num_words</i> <code> *
+ * (initialTexCoordIndex + validVertexCount)</code>,
+ * where <i>num_words</i> is 2, 3, or 4 depending on the vertex
+ * texture coordinate format.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the texture coordinate index array is greater than or equal to the
+ * number of vertices defined by the texCoords array,
+ * <code>texCoords.length / </code> <i>num_words</i>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTexCoordRefFloat(int texCoordSet, float[] texCoords) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ ((GeometryArrayRetained)this.retained).setTexCoordRefFloat(
+ texCoordSet, texCoords);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * Gets the float texture coordinate array reference for the specified
+ * texture coordinate set.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ *
+ * @return the current float texture coordinate array reference
+ * for the specified texture coordinate set
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>BY_REFERENCE</code>,
+ * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if none of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or texCoordSet is out of range.
+ *
+ * @since Java 3D 1.2
+ */
+ public float[] getTexCoordRefFloat(int texCoordSet) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getTexCoordRefFloat(
+ texCoordSet);
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for TexCoord2f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTexCoordRef2f(int texCoordSet, TexCoord2f[] texCoords) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ if ((format & (TEXTURE_COORDINATE_3 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray94"));
+
+ ((GeometryArrayRetained)this.retained).setTexCoordRef2f(
+ texCoordSet, texCoords);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for TexCoord2f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public TexCoord2f[] getTexCoordRef2f(int texCoordSet) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getTexCoordRef2f(
+ texCoordSet);
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for TexCoord3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTexCoordRef3f(int texCoordSet, TexCoord3f[] texCoords) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ if ((format & (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
+
+ ((GeometryArrayRetained)this.retained).setTexCoordRef3f(
+ texCoordSet, texCoords);
+
+ // NOTE: the checks for multiple non-null references, and the
+ // array length check need to be done in the retained method
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, use geometry by-copy
+ * for TexCoord3f arrays
+ *
+ * @since Java 3D 1.2
+ */
+ public TexCoord3f[] getTexCoordRef3f(int texCoordSet) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & BY_REFERENCE) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ if ((format & INTERLEAVED) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+
+ return ((GeometryArrayRetained)this.retained).getTexCoordRef3f(
+ texCoordSet);
+ }
+
+ /**
+ * Sets the interleaved vertex array reference to the specified
+ * array. The vertex components must be stored in a predetermined
+ * order in the array. The order is: texture coordinates, colors,
+ * normals, and positional coordinates. In the case of texture
+ * coordinates, the values for each texture coordinate set
+ * are stored in order from 0 through texCoordSetCount-1. Only those
+ * components that are enabled appear in the vertex. The number
+ * of words per vertex depends on which vertex components are
+ * enabled. Texture coordinates, if enabled, use 2 words per
+ * texture coordinate set per vertex for
+ * <code>TEXTURE_COORDINATE_2</code>, 3 words per texture
+ * coordinate set per vertex for
+ * <code>TEXTURE_COORDINATE_3</code> or 4 words per texture
+ * coordinate set per vertex for
+ * <code>TEXTURE_COORDINATE_4</code>. Colors, if enabled, use 3
+ * words per vertex for <code>COLOR_3</code> or 4 words per vertex
+ * for <code>COLOR_4</code>. Normals, if enabled, use 3 words per
+ * vertex. Positional coordinates, which are always enabled, use
+ * 3 words per vertex. For example, the format of interleaved
+ * data for a GeometryArray object whose vertexFormat includes
+ * <code>COORDINATES</code>, <code>COLOR_3</code>, and
+ * <code>NORMALS</code> would be: <i>red</i>, <i>green</i>,
+ * <i>blue</i>, <i>Nx</i>, <i>Ny</i>, <i>Nz</i>, <i>x</i>,
+ * <i>y</i>, <i>z</i>. All components of a vertex are stored in
+ * adjacent memory locations. The first component of vertex 0 is
+ * stored beginning at index 0 in the array. The first component
+ * of vertex 1 is stored beginning at index
+ * <i>words_per_vertex</i> in the array. The total number of
+ * words needed to store <i>n</i> vertices is
+ * <i>words_per_vertex</i>*<i>n</i>.
+ *
+ * @param vertexData an array of vertex values to which a
+ * reference will be set.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>INTERLEAVED</code>
+ * or is <code>USE_NIO_BUFFER</code>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if
+ * <code>vertexData.length</code> < <i>words_per_vertex</i> *
+ * (<code>initialVertexIndex + validVertexCount</code>),
+ * where <i>words_per_vertex</i> depends on which formats are enabled.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the index array associated with any of the enabled vertex
+ * components (coord, color, normal, texcoord) is greater than or
+ * equal to the number of vertices defined by the vertexData
+ * array,
+ * <code>vertexData.length / </code> <i>words_per_vertex</i>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setInterleavedVertices(float[] vertexData) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & INTERLEAVED) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray85"));
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ ((GeometryArrayRetained)this.retained).setInterleavedVertices(vertexData);
+
+ // NOTE: the array length check needs to be done in the retained method
+ }
+
+
+ /**
+ * Gets the interleaved vertices array reference.
+ * @return the current interleaved vertices array reference.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>INTERLEAVED</code>
+ * or is <code>USE_NIO_BUFFER</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public float[] getInterleavedVertices() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & INTERLEAVED) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray85"));
+
+
+ if ((format & USE_NIO_BUFFER) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+
+ return ((GeometryArrayRetained)this.retained).getInterleavedVertices();
+ }
+
+ /**
+ * Sets the interleaved vertex buffer reference to the specified
+ * buffer object. The buffer must contain a java.nio.FloatBuffer object.
+ * The vertex components must be stored in a predetermined
+ * order in the buffer. The order is: texture coordinates, colors,
+ * normals, and positional coordinates. In the case of texture
+ * coordinates, the values for each texture coordinate set
+ * are stored in order from 0 through texCoordSetCount-1. Only those
+ * components that are enabled appear in the vertex. The number
+ * of words per vertex depends on which vertex components are
+ * enabled. Texture coordinates, if enabled, use 2 words per
+ * texture coordinate set per vertex for
+ * <code>TEXTURE_COORDINATE_2</code>, 3 words per texture
+ * coordinate set per vertex for
+ * <code>TEXTURE_COORDINATE_3</code> or 4 words per texture
+ * coordinate set per vertex for
+ * <code>TEXTURE_COORDINATE_4</code>. Colors, if enabled, use 3
+ * words per vertex for <code>COLOR_3</code> or 4 words per vertex
+ * for <code>COLOR_4</code>. Normals, if enabled, use 3 words per
+ * vertex. Positional coordinates, which are always enabled, use
+ * 3 words per vertex. For example, the format of interleaved
+ * data for a GeometryArray object whose vertexFormat includes
+ * <code>COORDINATES</code>, <code>COLOR_3</code>, and
+ * <code>NORMALS</code> would be: <i>red</i>, <i>green</i>,
+ * <i>blue</i>, <i>Nx</i>, <i>Ny</i>, <i>Nz</i>, <i>x</i>,
+ * <i>y</i>, <i>z</i>. All components of a vertex are stored in
+ * adjacent memory locations. The first component of vertex 0 is
+ * stored beginning at index 0 in the buffer. The first component
+ * of vertex 1 is stored beginning at index
+ * <i>words_per_vertex</i> in the buffer. The total number of
+ * words needed to store <i>n</i> vertices is
+ * <i>words_per_vertex</i>*<i>n</i>.
+ *
+ * @param vertexData a J3DBuffer object to which a reference will be set.
+ * The buffer contains an NIO float buffer of
+ * <i>words_per_vertex</i>*<i>n</i> values.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>INTERLEAVED</code>
+ * or is not <code>USE_NIO_BUFFER</code>.
+ *
+ * @exception IllegalArgumentException if the java.nio.Buffer
+ * contained in the specified J3DBuffer is not a
+ * java.nio.FloatBuffer object.
+ *
+ * @exception ArrayIndexOutOfBoundsException if
+ * <code>vertexData.getBuffer().limit()</code> < <i>words_per_vertex</i> *
+ * (<code>initialVertexIndex + validVertexCount</code>),
+ * where <i>words_per_vertex</i> depends on which formats are enabled.
+ *
+ * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+ * object is a subclass of IndexedGeometryArray, and any element
+ * in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the index array associated with any of the enabled vertex
+ * components (coord, color, normal, texcoord) is greater than or
+ * equal to the number of vertices defined by the vertexData
+ * object,
+ * <code>vertexData.getBuffer().limit() / </code> <i>words_per_vertex</i>.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setInterleavedVertexBuffer(J3DBuffer vertexData) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & INTERLEAVED) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray85"));
+
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ ((GeometryArrayRetained)this.retained).setInterleavedVertexBuffer(vertexData);
+
+ }
+
+
+ /**
+ * Gets the interleaved vertex array buffer reference.
+ * @return the current interleaved vertex array buffer reference.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data mode for this geometry
+ * array object is not <code>INTERLEAVED</code>
+ * or is not <code>USE_NIO_BUFFER</code>.
+ *
+ * @since Java 3D 1.3
+ */
+ public J3DBuffer getInterleavedVertexBuffer() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+ !this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+ }
+
+ int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+ if ((format & INTERLEAVED) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray85"));
+
+ if ((format & USE_NIO_BUFFER) == 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+
+ return ((GeometryArrayRetained)this.retained).getInterleavedVertexBuffer();
+
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryArrayRetained.java b/src/classes/share/javax/media/j3d/GeometryArrayRetained.java
new file mode 100644
index 0000000..774371d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryArrayRetained.java
@@ -0,0 +1,10631 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import com.sun.j3d.internal.Distance;
+import javax.vecmath.*;
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.Vector;
+import java.util.Enumeration;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+
+
+/**
+ * The GeometryArray object contains arrays of positional coordinates,
+ * colors, normals and/or texture coordinates that describe
+ * point, line, or surface geometry. It is extended to create
+ * the various primitive types (e.g., lines, triangle_strips, etc.)
+ */
+
+abstract class GeometryArrayRetained extends GeometryRetained{
+
+
+
+ // TODO: Memory footprint reduction. Should have separate object to
+ // to contain specific data such as a ByRef object for
+ // all ByRef related data. So that incases where no
+ // ByRef is needed, the ByRef object reference is
+ // set to null. Hence saving memory!
+ // Need object such as Texture, D3d and ByRef ...
+ //
+
+
+ // Contains a bitset indicating which components are present
+ int vertexFormat;
+
+ // Whether this geometry was ever rendered as transparent
+ int c4fAllocated = 0;
+
+ // Total Number of vertices
+ int vertexCount;
+
+ // number of vertices used in rendering
+ int validVertexCount;
+
+ // The vertex data in packed format
+ float vertexData[];
+
+ // vertex data in packed format for each screen in multi-screen situation
+ // if alpha values of each vertex are to be updated
+ float mvertexData[][];
+
+ //
+ // The following offset/stride values are internally computed
+ // from the format
+ //
+
+ // Stride (in words) from one vertex to the next
+ int stride;
+
+ // Stride (in words) from one texture coordinate to the next
+ int texCoordStride;
+
+ // Offset (in words) within each vertex of the coordinate position
+ int coordinateOffset;
+
+ // Offset (in words) within each vertex of the normal
+ int normalOffset;
+
+ // Offset (in words) within each vertex of the color
+ int colorOffset;
+
+ // Offset (in words) within each vertex of the texture coordinate
+ int textureOffset;
+
+ // alpha value for transparency and texture blending
+ float[] lastAlpha = new float[1];
+ float lastScreenAlpha = -1;
+
+ int colorChanged = 0;
+
+ // true if alpha value from transparencyAttrubute has changed
+ boolean alphaChanged = false;
+
+ // byte to float scale factor
+ static final float ByteToFloatScale = 1.0f/255.0f;
+
+ // float to byte scale factor
+ static final float FloatToByteScale = 255.0f;
+
+ // Set flag indicating that we are in the updater. This flag
+ // can be used by the various setRef methods to inhibit any
+ // update messages
+ boolean inUpdater = false;
+
+ // Array List used for messages
+ ArrayList gaList = new ArrayList(1);
+
+
+ // Target threads to be notified when morph changes
+ static final int targetThreads = (J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_GEOMETRY);
+
+ // used for byReference geometry
+ float[] floatRefCoords = null;
+ double[] doubleRefCoords = null;
+ Point3d[] p3dRefCoords = null;
+ Point3f[] p3fRefCoords = null;
+
+ // Used for NIO buffer geometry
+ J3DBuffer coordRefBuffer = null;
+ FloatBufferWrapper floatBufferRefCoords = null;
+ DoubleBufferWrapper doubleBufferRefCoords = null;
+
+ // Initial index to use for rendering
+ int initialCoordIndex = 0;
+ int initialColorIndex = 0;
+ int initialNormalIndex = 0;
+ int initialTexCoordIndex[] = null;
+ int initialVertexIndex = 0;
+
+
+ // used for byReference colors
+ float[] floatRefColors = null;
+ byte[] byteRefColors = null;
+ Color3f[] c3fRefColors = null;
+ Color4f[] c4fRefColors = null;
+ Color3b[] c3bRefColors = null;
+ Color4b[] c4bRefColors = null;
+
+ // Used for NIO buffer colors
+ J3DBuffer colorRefBuffer = null;
+ FloatBufferWrapper floatBufferRefColors = null;
+ ByteBufferWrapper byteBufferRefColors = null;
+
+ // flag to indicate if the "by reference" component is already set
+ int vertexType = 0;
+ static final int PF = 0x1;
+ static final int PD = 0x2;
+ static final int P3F = 0x4;
+ static final int P3D = 0x8;
+ static final int VERTEX_DEFINED = PF | PD | P3F | P3D;
+
+
+ static final int CF = 0x10;
+ static final int CUB = 0x20;
+ static final int C3F = 0x40;
+ static final int C4F = 0x80;
+ static final int C3UB = 0x100;
+ static final int C4UB = 0x200;
+ static final int COLOR_DEFINED = CF | CUB | C3F | C4F| C3UB | C4UB;
+
+ static final int NF = 0x400;
+ static final int N3F = 0x800;
+ static final int NORMAL_DEFINED = NF | N3F;
+
+ static final int TF = 0x1000;
+ static final int T2F = 0x2000;
+ static final int T3F = 0x4000;
+ static final int TEXCOORD_DEFINED = TF | T2F | T3F;
+
+ // flag for execute geometry array when by reference
+ static final int COORD_FLOAT = 0x01;
+ static final int COORD_DOUBLE = 0x02;
+ static final int COLOR_FLOAT = 0x04;
+ static final int COLOR_BYTE = 0x08;
+ static final int NORMAL_FLOAT = 0x10;
+ static final int TEXCOORD_FLOAT = 0x20;
+
+
+ // used by "by reference" normals
+ float[] floatRefNormals = null;
+ Vector3f[] v3fRefNormals = null;
+
+ // Used for NIO buffer normals
+ J3DBuffer normalRefBuffer = null;
+ FloatBufferWrapper floatBufferRefNormals = null;
+
+
+ // used by "by reference" tex coords
+ Object[] refTexCoords = null;
+ TexCoord2f[] t2fRefTexCoords = null;
+ TexCoord3f[] t3fRefTexCoords = null;
+
+ // Used for NIO buffer tex coords
+ Object[] refTexCoordsBuffer = null;
+ //FloatBufferWrapper[] floatBufferRefTexCoords = null;
+
+
+ // used by interleaved array
+ float[] interLeavedVertexData = null;
+
+ // used by interleaved NIO buffer
+ J3DBuffer interleavedVertexBuffer = null;
+ FloatBufferWrapper interleavedFloatBufferImpl = null;
+
+ // pointers used, when transparency is turned on
+ // or when its an object such as C3F, P3F etc ..
+ // TODO: Update this for J3DBuffer
+ float[] mirrorFloatRefCoords = null;
+ double[] mirrorDoubleRefCoords = null;
+ float[] mirrorFloatRefNormals = null;
+ float[] mirrorFloatRefTexCoords = null;
+ Object[] mirrorRefTexCoords = null;
+
+ float[][] mirrorFloatRefColors = new float[1][];
+ byte[][] mirrorUnsignedByteRefColors= new byte[1][];
+ float[][] mirrorInterleavedColorPointer = null;
+
+
+ // This native method builds a native representation of this object, then
+ // returns the nativeId.
+ native int build(int geoType);
+
+ // boolean to determine if a mirror was allocated
+ int mirrorVertexAllocated = 0;
+ int mirrorColorAllocated = 0;
+ boolean mirrorTexCoordAllocated = false;
+ boolean mirrorNormalAllocated = false;
+
+ // Some dirty bits for GeometryArrays
+ static final int COORDINATE_CHANGED = 0x01;
+ static final int NORMAL_CHANGED = 0x02;
+ static final int COLOR_CHANGED = 0x04;
+ static final int TEXTURE_CHANGED = 0x08;
+ static final int BOUNDS_CHANGED = 0x10;
+ static final int INDEX_CHANGED = 0x20;
+ static final int STRIPCOUNT_CHANGED = 0x40;
+ static final int VERTEX_CHANGED = COORDINATE_CHANGED |
+ NORMAL_CHANGED |
+ COLOR_CHANGED |
+ TEXTURE_CHANGED;
+
+
+ static final int defaultTexCoordSetMap[] = {0};
+ int texCoordSetCount = 0;
+ int [] texCoordSetMap = null;
+
+ // this array contains offset to the texCoord data for each
+ // texture unit. -1 means no corresponding texCoord data offset
+ int [] texCoordSetMapOffset = null;
+
+ // This point to a list of VertexBuffers in a Vector structure
+ // Each element correspond to a D3D context that create this VB.
+ // Note that this GeometryArray can be used by multiple ctx.
+ long pVertexBuffers = 0;
+ int dirtyFlag;
+
+ // each bit corresponds to a unique renderer if shared context
+ // or a unique canvas otherwise
+ int resourceCreationMask = 0x0;
+
+
+ // Reference count of renderMolecules per dlist created, this is either
+ // per renderer for useSharedCtx or per Canvas for non-shared ctx
+ // note since
+ // renderer and canvasindex starts from 1, the first entry of this
+ // array will never be used.
+ int[] renderMolPerDlist = new int[2];
+
+ // timestamp used to create display list; same as above, this is either
+ // one per renderer for useSharedCtx, or one per Canvas for non-shared
+ // ctx
+ long[] timeStampPerDlist = new long[2];
+
+
+ // Unique display list Id, if this geometry is shared
+ int dlistId = -1;
+ Integer dlistObj = null;
+
+ // A list of pre-defined bits to indicate which component
+ // in this Texture object changed.
+ // static final int DLIST_CREATE_CHANGED = 0x01;
+ static final int INIT_MIRROR_GEOMETRY = 0x02;
+
+
+ // A list of Universes that this Geometry is referenced in Morph from
+ ArrayList morphUniverseList = null;
+
+ // A list of ArrayLists which contain all the MorphRetained objects
+ // refering to this geometry. Each list corresponds to the universe
+ // above.
+ ArrayList morphUserLists = null;
+
+ // The following variables are only used in compile mode
+
+ // Offset of a geometry array into the merged array
+ int[] geoOffset;
+
+ // vertexcount of a geometry array in a merge array
+ int[] compileVcount;
+
+ boolean isCompiled = false;
+
+ boolean isShared = false;
+
+ IndexedGeometryArrayRetained cloneSourceArray = null;
+
+// private MemoryFreeList pickVectorFreelist =
+// FreeListManager.getFreeList(FreeListManager.PICKVECTOR);
+
+ static final double EPS = 1.0e-13;
+
+ native void freeD3DArray(boolean deleteVB);
+
+ GeometryArrayRetained() {
+ dirtyFlag = INDEX_CHANGED|VERTEX_CHANGED;
+ lastAlpha[0] = 1.0f;
+ }
+
+
+ void setLive(boolean inBackgroundGroup, int refCount) {
+ dirtyFlag = VERTEX_CHANGED|INDEX_CHANGED;
+ isEditable = !isWriteStatic();
+ super.doSetLive(inBackgroundGroup, refCount);
+ super.markAsLive();
+ // Send message to RenderingAttribute structure to obtain a dlistId
+ // System.out.println("Geometry - "+this+"refCount = "+this.refCount);
+ if (this.refCount > 1) {
+ // Send to rendering attribute structure,
+ /*
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.GEOMETRYARRAY_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(DLIST_CREATE_CHANGED);
+ VirtualUniverse.mc.processMessage(createMessage);
+ */
+ isShared = true;
+ } // Clone geometry only for the first setLive
+ else {
+ // If geometry is indexed and use_index_coord is false, unindexify
+ // otherwise, set mirrorGeometry to null (from previous clearLive)
+ if (this instanceof IndexedGeometryArrayRetained) {
+ // Send to rendering attribute structure,
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.GEOMETRY_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = null;
+ createMessage.args[1]= this;
+ createMessage.args[2]= new Integer(INIT_MIRROR_GEOMETRY);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ }
+
+ void clearLive(int refCount) {
+ super.clearLive(refCount);
+
+ if (this.refCount <= 0) {
+ if (pVertexBuffers != 0) {
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = null;
+ renderMessage.args[1] = this;
+ // Any one renderer is fine since VB store the ctx
+ // where it is created.
+ Enumeration e = Screen3D.deviceRendererMap.elements();
+ Renderer rdr = (Renderer) e.nextElement();
+ rdr.rendererStructure.addMessage(renderMessage);
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+ isShared = false;
+ }
+ }
+
+ void computeBoundingBox() {
+
+ // System.out.println("computeBoundingBox ....");
+
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ // by copy
+ computeBoundingBox(initialVertexIndex, vertexData);
+
+ } else if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) { // USE_NIO_BUFFER
+ //System.out.println("vertexFormat & GeometryArray.USE_NIO_BUFFER");
+ if((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ computeBoundingBox(initialCoordIndex, interleavedFloatBufferImpl);
+ } else if((vertexType & PF) != 0) {
+ computeBoundingBox(floatBufferRefCoords);
+ } else if((vertexType & PD) != 0) {
+ computeBoundingBox(doubleBufferRefCoords);
+ }
+
+ } else if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ //System.out.println("vertexFormat & GeometryArray.INTERLEAVED");
+ computeBoundingBox(initialCoordIndex, interLeavedVertexData);
+ } else if ((vertexType & PF) != 0) {
+ //System.out.println("vertexType & PF");
+ computeBoundingBox(floatRefCoords);
+ } else if ((vertexType & P3F) != 0) {
+ //System.out.println("vertexType & P3F");
+ computeBoundingBox(p3fRefCoords);
+ } else if ((vertexType & P3D) != 0) {
+ //System.out.println("vertexType & P3D");
+ computeBoundingBox(p3dRefCoords);
+ } else if ((vertexType & PD) != 0) {
+ //System.out.println("vertexType & PD");
+ computeBoundingBox(doubleRefCoords);
+ }
+ }
+
+
+ // NullGeometry is true only for byRef case
+ void processCoordsChanged(boolean nullGeo) {
+
+ /*
+ System.out.println("processCoordsChanged : nullGeo " + nullGeo);
+ System.out.println("Before :processCoordsChanged : geoBounds ");
+ System.out.println(geoBounds);
+ */
+ if (nullGeo) {
+ synchronized(geoBounds) {
+ geoBounds.setLower(-1.0, -1.0, -1.0);
+ geoBounds.setUpper(1.0, 1.0, 1.0);
+ boundsDirty = false;
+ }
+ synchronized(centroid) {
+ recompCentroid = false;
+ this.centroid.set(geoBounds.getCenter());
+ }
+
+ }
+ else {
+ // re-compute centroid if used
+ synchronized(centroid) {
+ recompCentroid = true;
+ }
+
+ synchronized(geoBounds) {
+ boundsDirty = true;
+ computeBoundingBox();
+ }
+
+ /*
+ System.out.println("After :processCoordsChanged : geoBounds ");
+ System.out.println(geoBounds);
+ */
+ }
+ }
+
+
+ void computeBoundingBox(int vIndex, float[] vdata) {
+ int i, offset;
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+
+
+ //System.out.println("Before : computeBoundingBox : geoBounds ");
+ // System.out.println(geoBounds);
+
+ synchronized(geoBounds) {
+
+ // If autobounds compute is false then return
+ // It is possible that user call getBounds() before
+ // this Geometry add to live scene graph.
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+ if (!boundsDirty)
+ return;
+
+ // Initial offset
+ offset = vIndex * stride+coordinateOffset;
+ // Compute the bounding box
+ xmin = xmax = vdata[offset];
+ ymin = ymax = vdata[offset+1];
+ zmin = zmax = vdata[offset+2];
+ offset += stride;
+ for (i=1; i<validVertexCount; i++) {
+ if (vdata[offset] > xmax)
+ xmax = vdata[offset];
+ if (vdata[offset] < xmin)
+ xmin = vdata[offset];
+
+ if (vdata[offset+1] > ymax)
+ ymax = vdata[offset+1];
+ if (vdata[offset+1] < ymin)
+ ymin = vdata[offset+1];
+
+ if (vdata[offset+2] > zmax)
+ zmax = vdata[offset+2];
+ if (vdata[offset+2] < zmin)
+ zmin = vdata[offset+2];
+
+ offset += stride;
+ }
+
+ geoBounds.setUpper(xmax, ymax, zmax);
+ geoBounds.setLower(xmin, ymin, zmin);
+ boundsDirty = false;
+ }
+ /*
+ System.out.println("After : computeBoundingBox : geoBounds ");
+ System.out.println(geoBounds);
+ */
+ }
+
+ // Compute boundingbox for interleaved nio buffer
+ void computeBoundingBox(int vIndex, FloatBufferWrapper vdata) {
+ int i, offset;
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+
+
+ synchronized(geoBounds) {
+ // If autobounds compute is false then return
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+
+ if (!boundsDirty)
+ return;
+
+ // Initial offset
+ offset = vIndex * stride+coordinateOffset;
+ // Compute the bounding box
+ xmin = xmax = vdata.get(offset);
+ ymin = ymax = vdata.get(offset+1);
+ zmin = zmax = vdata.get(offset+2);
+ offset += stride;
+ for (i=1; i<validVertexCount; i++) {
+ if (vdata.get(offset) > xmax)
+ xmax = vdata.get(offset);
+ if (vdata.get(offset) < xmin)
+ xmin = vdata.get(offset);
+
+ if (vdata.get(offset+1) > ymax)
+ ymax = vdata.get(offset+1);
+ if (vdata.get(offset+1) < ymin)
+ ymin = vdata.get(offset+1);
+
+ if (vdata.get(offset+2) > zmax)
+ zmax = vdata.get(offset+2);
+ if (vdata.get(offset+2) < zmin)
+ zmin = vdata.get(offset+2);
+
+ offset += stride;
+ }
+
+ geoBounds.setUpper(xmax, ymax, zmax);
+ geoBounds.setLower(xmin, ymin, zmin);
+ boundsDirty = false;
+ }
+ }
+
+
+ // compute bounding box for coord with noi buffer
+ void computeBoundingBox( DoubleBufferWrapper buffer) {
+ int i, j, k, sIndex;
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+
+ synchronized(geoBounds) {
+ // If autobounds compute is false then return
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+
+ if (!boundsDirty)
+ return;
+
+ sIndex = initialCoordIndex;
+ int maxIndex = 3*validVertexCount;
+
+ // Compute the bounding box
+ xmin = xmax = buffer.get(sIndex++);
+ ymin = ymax = buffer.get(sIndex++);
+ zmin = zmax = buffer.get(sIndex++);
+
+ for (i=sIndex; i<maxIndex; i+=3) {
+ j = i + 1;
+ k = i + 2;
+
+ if (buffer.get(i) > xmax)
+ xmax = buffer.get(i);
+ if (buffer.get(i) < xmin)
+ xmin = buffer.get(i);
+
+ if (buffer.get(j) > ymax)
+ ymax = buffer.get(j);
+ if (buffer.get(j) < ymin)
+ ymin = buffer.get(j);
+
+ if (buffer.get(k) > zmax)
+ zmax = buffer.get(k);
+ if (buffer.get(k) < zmin)
+ zmin = buffer.get(k);
+
+ }
+ geoBounds.setUpper(xmax, ymax, zmax);
+ geoBounds.setLower(xmin, ymin, zmin);
+ boundsDirty = false;
+ }
+ }
+
+ // compute bounding box for coord with noi buffer
+ void computeBoundingBox( FloatBufferWrapper buffer) {
+ int i, j, k, sIndex;
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+
+
+ synchronized(geoBounds) {
+ // If autobounds compute is false then return
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+
+ if (!boundsDirty)
+ return;
+
+
+ sIndex = initialCoordIndex;
+ int maxIndex = 3*validVertexCount;
+
+ // Compute the bounding box
+ xmin = xmax = buffer.get(sIndex++);
+ ymin = ymax = buffer.get(sIndex++);
+ zmin = zmax = buffer.get(sIndex++);
+
+ for (i=sIndex; i<maxIndex; i+=3) {
+ j = i + 1;
+ k = i + 2;
+
+ if (buffer.get(i) > xmax)
+ xmax = buffer.get(i);
+ if (buffer.get(i) < xmin)
+ xmin = buffer.get(i);
+
+ if (buffer.get(j) > ymax)
+ ymax = buffer.get(j);
+ if (buffer.get(j) < ymin)
+ ymin = buffer.get(j);
+
+ if (buffer.get(k) > zmax)
+ zmax = buffer.get(k);
+ if (buffer.get(k) < zmin)
+ zmin = buffer.get(k);
+
+ }
+ geoBounds.setUpper(xmax, ymax, zmax);
+ geoBounds.setLower(xmin, ymin, zmin);
+ boundsDirty = false;
+ }
+ }
+
+ void computeBoundingBox(float[] coords) {
+ // System.out.println("GeometryArrayRetained : computeBoundingBox(float[] coords)");
+ int i, j, k, sIndex;
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+
+ synchronized(geoBounds) {
+ // If autobounds compute is false then return
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+
+ if (!boundsDirty)
+ return;
+
+ sIndex = initialCoordIndex;
+ int maxIndex = 3*validVertexCount;
+
+ // Compute the bounding box
+ xmin = xmax = coords[sIndex++];
+ ymin = ymax = coords[sIndex++];
+ zmin = zmax = coords[sIndex++];
+
+ for (i=sIndex; i<maxIndex; i+=3) {
+ j = i + 1;
+ k = i + 2;
+
+ if (coords[i] > xmax)
+ xmax = coords[i];
+ if (coords[i] < xmin)
+ xmin = coords[i];
+
+ if (coords[j] > ymax)
+ ymax = coords[j];
+ if (coords[j] < ymin)
+ ymin = coords[j];
+
+ if (coords[k] > zmax)
+ zmax = coords[k];
+ if (coords[k] < zmin)
+ zmin = coords[k];
+
+ }
+ geoBounds.setUpper(xmax, ymax, zmax);
+ // System.out.println("max(" + xmax + ", " + ymax + ", " + zmax + ")");
+ geoBounds.setLower(xmin, ymin, zmin);
+ // System.out.println("min(" + xmin + ", " + ymin + ", " + zmin + ")");
+
+ boundsDirty = false;
+ }
+
+ }
+
+ void computeBoundingBox(double[] coords) {
+ int i, j, k, sIndex;
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+
+ synchronized(geoBounds) {
+ // If autobounds compute is false then return
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+
+ if (!boundsDirty)
+ return;
+
+
+ sIndex = initialCoordIndex;
+ int maxIndex = 3*validVertexCount;
+
+ // Compute the bounding box
+ xmin = xmax = coords[sIndex++];
+ ymin = ymax = coords[sIndex++];
+ zmin = zmax = coords[sIndex++];
+
+ for (i=sIndex; i<maxIndex; i+=3) {
+ j = i + 1;
+ k = i + 2;
+
+ if (coords[i] > xmax)
+ xmax = coords[i];
+ if (coords[i] < xmin)
+ xmin = coords[i];
+
+ if (coords[j] > ymax)
+ ymax = coords[j];
+ if (coords[j] < ymin)
+ ymin = coords[j];
+
+ if (coords[k] > zmax)
+ zmax = coords[k];
+ if (coords[k] < zmin)
+ zmin = coords[k];
+
+ }
+ geoBounds.setUpper(xmax, ymax, zmax);
+ geoBounds.setLower(xmin, ymin, zmin);
+ boundsDirty = false;
+ }
+
+ }
+
+ void computeBoundingBox(Point3f[] coords) {
+
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+ Point3f p;
+
+ synchronized(geoBounds) {
+ // If autobounds compute is false then return
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+
+ if (!boundsDirty)
+ return;
+
+
+
+ // Compute the bounding box
+ xmin = xmax = coords[initialCoordIndex].x;
+ ymin = ymax = coords[initialCoordIndex].y;
+ zmin = zmax = coords[initialCoordIndex].z;
+
+ for (int i=initialCoordIndex+1; i<validVertexCount; i++) {
+ p = coords[i];
+ if (p.x > xmax) xmax = p.x;
+ if (p.x < xmin) xmin = p.x;
+
+ if (p.y > ymax) ymax = p.y;
+ if (p.y < ymin) ymin = p.y;
+
+ if (p.z > zmax) zmax = p.z;
+ if (p.z < zmin) zmin = p.z;
+
+ }
+ geoBounds.setUpper(xmax, ymax, zmax);
+ geoBounds.setLower(xmin, ymin, zmin);
+ boundsDirty = false;
+ }
+
+ }
+
+ void computeBoundingBox(Point3d[] coords) {
+
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+ Point3d p;
+
+ synchronized(geoBounds) {
+ // If autobounds compute is false then return
+ if ((computeGeoBounds == 0) && (refCount > 0)) {
+ return;
+ }
+
+ if (!boundsDirty)
+ return;
+
+
+ // Compute the bounding box
+ xmin = xmax = coords[initialCoordIndex].x;
+ ymin = ymax = coords[initialCoordIndex].y;
+ zmin = zmax = coords[initialCoordIndex].z;
+
+ for (int i=initialCoordIndex+1; i<validVertexCount; i++) {
+ p = coords[i];
+ if (p.x > xmax) xmax = p.x;
+ if (p.x < xmin) xmin = p.x;
+
+ if (p.y > ymax) ymax = p.y;
+ if (p.y < ymin) ymin = p.y;
+
+ if (p.z > zmax) zmax = p.z;
+ if (p.z < zmin) zmin = p.z;
+
+ }
+ geoBounds.setUpper(xmax, ymax, zmax);
+ geoBounds.setLower(xmin, ymin, zmin);
+ boundsDirty = false;
+ }
+
+ }
+
+
+ synchronized void update() {
+ }
+
+ void setupMirrorVertexPointer(int vType) {
+ int i, index;
+
+ switch (vType) {
+ case PF:
+ if (floatRefCoords == null) {
+ if ((vertexType & VERTEX_DEFINED) == PF) {
+ vertexType &= ~PF;
+ mirrorFloatRefCoords = null;
+ mirrorVertexAllocated &= ~PF;
+ }
+ }
+ else {
+ vertexType |= PF;
+ mirrorFloatRefCoords = floatRefCoords;
+ mirrorVertexAllocated &= ~PF;
+ }
+
+ break;
+ case PD:
+ if (doubleRefCoords == null) {
+ if ((vertexType & VERTEX_DEFINED) == PD) {
+ mirrorDoubleRefCoords = null;
+ mirrorVertexAllocated &= ~PD;
+ vertexType &= ~PD;
+ }
+ vertexType &= ~PD;
+ }
+ else {
+ vertexType |= PD;
+ mirrorDoubleRefCoords = doubleRefCoords;
+ mirrorVertexAllocated &= ~PD;
+ }
+
+ break;
+ case P3F:
+ if (p3fRefCoords == null) {
+ vertexType &= ~P3F;
+ // Don't set the mirrorFloatRefCoords to null,
+ // may be able to re-use
+ // mirrorFloatRefCoords = null;
+ }
+ else {
+ vertexType |= P3F;
+
+ if ((mirrorVertexAllocated & PF) == 0) {
+ mirrorFloatRefCoords = new float[vertexCount * 3];
+ mirrorVertexAllocated |= PF;
+ }
+
+ index = initialCoordIndex * 3;
+ for ( i=initialCoordIndex; i<validVertexCount; i++) {
+ mirrorFloatRefCoords[index++] = p3fRefCoords[i].x;
+ mirrorFloatRefCoords[index++] = p3fRefCoords[i].y;
+ mirrorFloatRefCoords[index++] = p3fRefCoords[i].z;
+ }
+ }
+ break;
+ case P3D:
+ if (p3dRefCoords == null) {
+ vertexType &= ~P3D;
+ // Don't set the mirrorDoubleRefCoords to null,
+ // may be able to re-use
+ // mirrorDoubleRefCoords = null;
+ }
+ else {
+ vertexType |= P3D;
+
+ if ((mirrorVertexAllocated & PD) == 0) {
+ mirrorDoubleRefCoords = new double[vertexCount * 3];
+ mirrorVertexAllocated |= PD;
+ }
+
+ index = initialCoordIndex * 3;
+ for ( i=initialCoordIndex; i<validVertexCount; i++) {
+ mirrorDoubleRefCoords[index++] = p3dRefCoords[i].x;
+ mirrorDoubleRefCoords[index++] = p3dRefCoords[i].y;
+ mirrorDoubleRefCoords[index++] = p3dRefCoords[i].z;
+ }
+ }
+ break;
+ default:
+ break;
+
+ }
+
+ }
+
+ // TODO: may not need this function in NIO buffer version
+ // setup mirror vertex pointers for J3DBuffer version
+ void setupMirrorVertexPointerNIOBuffer(int vType) {
+ int i, index = 0;
+ switch(vType) {
+ case PF:
+
+ break;
+ case PD:
+
+ break;
+
+ // do not need to handle P3F and P3D case in NIO buffer version
+ default:
+ break;
+
+ }
+
+ }
+
+ // If turned transparent the first time, then force it to allocate
+ void setupMirrorInterleavedColorPointer(boolean force) {
+ int index, length, offset;
+ int i;
+
+ if (force || (c4fAllocated != 0)) { // Color is present
+
+ length = 4 * vertexCount;
+
+ if (mirrorInterleavedColorPointer == null) {
+ mirrorInterleavedColorPointer = new float[1][length];
+ }
+
+ index = 4 * initialVertexIndex;
+ offset = stride * initialVertexIndex + colorOffset;
+
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0 &&
+ interLeavedVertexData != null ) { // java array
+ if ((vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+
+ for (i = initialVertexIndex; i < validVertexCount; i++) {
+ mirrorInterleavedColorPointer[0][index++] =
+ interLeavedVertexData[offset];
+ mirrorInterleavedColorPointer[0][index++] =
+ interLeavedVertexData[offset+1];
+ mirrorInterleavedColorPointer[0][index++] =
+ interLeavedVertexData[offset+2];
+ mirrorInterleavedColorPointer[0][index++] =
+ interLeavedVertexData[offset+3];
+ offset += stride;
+ }
+ }
+ else {
+ for (i = initialVertexIndex; i < validVertexCount; i++) {
+ mirrorInterleavedColorPointer[0][index++] =
+ interLeavedVertexData[offset];
+ mirrorInterleavedColorPointer[0][index++] =
+ interLeavedVertexData[offset+1];
+ mirrorInterleavedColorPointer[0][index++] =
+ interLeavedVertexData[offset+2];
+ mirrorInterleavedColorPointer[0][index++] = 1.0f;
+ offset += stride;
+ }
+ }
+
+ } else { // NIO BUFFER
+ if ((vertexFormat & GeometryArray.WITH_ALPHA) != 0 &&
+ interleavedFloatBufferImpl != null) {
+ for (i = initialVertexIndex; i < validVertexCount; i++) {
+ interleavedFloatBufferImpl.position(offset);
+ interleavedFloatBufferImpl.get(mirrorInterleavedColorPointer[0],
+ index , 4);
+ index += 4;
+ offset += stride;
+ }
+ }
+ else {
+ for (i = initialVertexIndex; i < validVertexCount; i++) {
+ interleavedFloatBufferImpl.position(offset);
+ interleavedFloatBufferImpl.get(mirrorInterleavedColorPointer[0],
+ index, 3);
+ mirrorInterleavedColorPointer[0][index+3] = 1.0f;
+ index += 4;
+ offset += stride;
+
+ }
+ }
+ }
+ c4fAllocated = GeometryArray.WITH_ALPHA;
+ }
+ }
+
+ // If turned transparent the first time, then force it to allocate
+ void setupMirrorColorPointer(int ctype, boolean force) {
+ int i, srcIndex = 0, dstIndex = 0;
+ int multiplier;
+
+ if (c4fAllocated == 0 && !force) {
+ multiplier = 3;
+ } else {
+
+ // If the first time, we are forced to allocate 4f, then
+ // we need to force the allocation of the colors again
+ // for the case when allocation has previously occurred
+ // only for RGB
+ if (force && (c4fAllocated == 0) &&
+ (vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+ mirrorColorAllocated = 0;
+ }
+ c4fAllocated = GeometryArray.WITH_ALPHA;
+ multiplier = 4;
+ }
+
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) { // java array
+ switch (ctype) {
+ case CF:
+ if (floatRefColors == null) {
+ if ((c4fAllocated == 0) && !force &&
+ (vertexType & COLOR_DEFINED) == CF) {
+ mirrorFloatRefColors[0] = null;
+ mirrorColorAllocated &= ~CF;
+ }
+ vertexType &= ~CF;
+ return;
+ }
+
+ vertexType |= CF;
+ if (c4fAllocated == 0 && !force) {
+ mirrorFloatRefColors[0] = floatRefColors;
+ mirrorColorAllocated &= ~CF;
+ }
+ else {
+ if ((mirrorColorAllocated & CF) == 0) {
+ mirrorFloatRefColors[0] = new float[4 * vertexCount];
+ mirrorColorAllocated |= CF;
+ }
+
+ if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+
+ srcIndex = initialColorIndex * 3;
+ dstIndex = initialColorIndex * 4;
+
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorFloatRefColors[0][dstIndex++] =
+ floatRefColors[srcIndex++];
+ mirrorFloatRefColors[0][dstIndex++] =
+ floatRefColors[srcIndex++];
+ mirrorFloatRefColors[0][dstIndex++] =
+ floatRefColors[srcIndex++];
+ mirrorFloatRefColors[0][dstIndex++] = 1.0f;
+ }
+
+ }
+ else {
+ srcIndex = initialColorIndex * 4;
+ System.arraycopy(floatRefColors, srcIndex,
+ mirrorFloatRefColors[0], srcIndex,
+ (4*validVertexCount));
+ }
+ }
+ break;
+ case CUB:
+ if (byteRefColors == null) {
+ if (c4fAllocated == 0 && !force &&
+ ((vertexType & COLOR_DEFINED) == CUB) ) {
+ mirrorUnsignedByteRefColors[0] = null;
+ mirrorColorAllocated &= ~CUB;
+ }
+ vertexType &= ~CUB;
+ return;
+ }
+ vertexType |= CUB;
+ if (c4fAllocated == 0 && !force) {
+ mirrorUnsignedByteRefColors[0] = byteRefColors;
+ mirrorColorAllocated &= ~CUB;;
+ }
+ else {
+ if ((mirrorColorAllocated & CUB) == 0) {
+ mirrorUnsignedByteRefColors[0] = new byte[4 * vertexCount];
+ mirrorColorAllocated |= CUB;
+ }
+ if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+
+ srcIndex = initialColorIndex * 3;
+ dstIndex = initialColorIndex * 4;
+
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorUnsignedByteRefColors[0][dstIndex++] =
+ byteRefColors[srcIndex++];
+ mirrorUnsignedByteRefColors[0][dstIndex++] =
+ byteRefColors[srcIndex++];
+ mirrorUnsignedByteRefColors[0][dstIndex++] =
+ byteRefColors[srcIndex++];
+ mirrorUnsignedByteRefColors[0][dstIndex++] =
+ (byte)(255.0);
+ }
+ }
+ else {
+ srcIndex = initialColorIndex * 4;
+ System.arraycopy(byteRefColors, srcIndex,
+ mirrorUnsignedByteRefColors[0], srcIndex,
+ (4*validVertexCount));
+ }
+ }
+
+ break;
+ case C3F:
+ if (c3fRefColors == null) {
+ vertexType &= ~C3F;
+ return;
+ }
+ vertexType |=C3F ;
+
+ if ((mirrorColorAllocated & CF) == 0) {
+ mirrorFloatRefColors[0] = new float[vertexCount * multiplier];
+ mirrorColorAllocated |= CF;
+ }
+ if ((c4fAllocated & GeometryArray.WITH_ALPHA) == 0) {
+
+ dstIndex = initialColorIndex * 3;
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].x;
+ mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].y;
+ mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].z;
+ }
+ } else {
+
+ dstIndex = initialColorIndex * 4;
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].x;
+ mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].y;
+ mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].z;
+ mirrorFloatRefColors[0][dstIndex++] = 1.0f;
+ }
+ }
+
+ break;
+ case C4F:
+ if (c4fRefColors == null) {
+ vertexType &= ~C4F;
+ return;
+ }
+ vertexType |=C4F ;
+
+ if ((mirrorColorAllocated & CF) == 0) {
+ mirrorFloatRefColors[0] = new float[vertexCount << 2];
+ mirrorColorAllocated |= CF;
+ }
+
+ dstIndex = initialColorIndex * 4;
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].x;
+ mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].y;
+ mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].z;
+ mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].w;
+ }
+ break;
+ case C3UB:
+ if (c3bRefColors == null) {
+ vertexType &= ~C3UB;
+ return;
+ }
+ vertexType |=C3UB ;
+
+ if ((mirrorColorAllocated & CUB) == 0) {
+ mirrorUnsignedByteRefColors[0] =
+ new byte[vertexCount * multiplier];
+ mirrorColorAllocated |= CUB;
+ }
+ if ((c4fAllocated & GeometryArray.WITH_ALPHA) == 0) {
+ dstIndex = initialColorIndex * 3;
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].x;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].y;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].z;
+ }
+ } else {
+ dstIndex = initialColorIndex * 4;
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].x;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].y;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].z;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = (byte)255;
+ }
+ }
+ break;
+ case C4UB:
+ if (c4bRefColors == null) {
+ vertexType &= ~C4UB;
+ return;
+ }
+ vertexType |=C4UB ;
+ if ((mirrorColorAllocated & CUB) == 0) {
+ mirrorUnsignedByteRefColors[0] = new byte[vertexCount << 2];
+ mirrorColorAllocated |= CUB;
+ }
+
+ dstIndex = initialColorIndex * 4;
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].x;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].y;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].z;
+ mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].w;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else { //USE_NIO_BUFFER is set
+ if( colorRefBuffer == null) {
+ if (c4fAllocated == 0 && !force &&
+ (vertexType & COLOR_DEFINED) == CF) {
+ mirrorFloatRefColors[0] = null;
+ mirrorColorAllocated &= ~CF;
+ }
+ vertexType &= ~CF;
+
+ if (c4fAllocated == 0 && !force &&
+ ((vertexType & COLOR_DEFINED) == CUB) ) {
+ mirrorUnsignedByteRefColors[0] = null;
+ mirrorColorAllocated &= ~CUB;
+ }
+ vertexType &= ~CUB;
+ return;
+
+ } else if( floatBufferRefColors != null) {
+ vertexType |= CF;
+ vertexType &= ~CUB;
+ if (c4fAllocated == 0 && !force) {
+ // TODO: make suren mirrorFloatRefColors[0] is set right
+ mirrorFloatRefColors[0] = null;
+ mirrorColorAllocated &= ~CF;
+ }
+ else {
+ if ((mirrorColorAllocated & CF) == 0) {
+ mirrorFloatRefColors[0] = new float[4 * vertexCount];
+ mirrorColorAllocated |= CF;
+ }
+ floatBufferRefColors.rewind();
+ if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+ srcIndex = initialColorIndex * 3;
+ dstIndex = initialColorIndex * 4;
+ floatBufferRefColors.position(srcIndex);
+
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ floatBufferRefColors.get(mirrorFloatRefColors[0], dstIndex, 3);
+ mirrorFloatRefColors[0][dstIndex+3] = 1.0f;
+ dstIndex += 4;
+ }
+ }
+ else {
+
+ srcIndex = initialColorIndex * 4;
+ dstIndex = initialColorIndex * 4;
+ floatBufferRefColors.position(srcIndex);
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ floatBufferRefColors.get(mirrorFloatRefColors[0], dstIndex, 4);
+ dstIndex+= 4;
+ }
+ }
+ }
+ } else if ( byteBufferRefColors != null) {
+ vertexType |= CUB;
+ vertexType &= ~CF;
+ if (c4fAllocated == 0 && !force) {
+ // TODO: make suren mirrorUnsignedByteRefColors[0] is set right
+ mirrorUnsignedByteRefColors[0] = null;
+ mirrorColorAllocated &= ~CUB;;
+ }
+ else {
+ if ((mirrorColorAllocated & CUB) == 0) {
+ mirrorUnsignedByteRefColors[0] = new byte[4 * vertexCount];
+ mirrorColorAllocated |= CUB;
+ }
+
+ byteBufferRefColors.rewind();
+ if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+ srcIndex = initialColorIndex * 3;
+ dstIndex = initialColorIndex * 4;
+ byteBufferRefColors.position(srcIndex);
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ byteBufferRefColors.get(mirrorUnsignedByteRefColors[0],
+ dstIndex, 3);
+ mirrorUnsignedByteRefColors[0][dstIndex+3] = (byte)(255.0);
+ dstIndex += 4;
+ }
+ }
+ else {
+ srcIndex = initialColorIndex * 4;
+ dstIndex = initialColorIndex * 4;
+ byteBufferRefColors.position(srcIndex);
+ for (i = initialColorIndex; i < validVertexCount; i++) {
+ byteBufferRefColors.get(mirrorUnsignedByteRefColors[0], dstIndex, 4);
+ dstIndex+= 4;
+ }
+ }
+ } // end of else
+ }//end of else if ( byteBufferRefColors != null)
+ }//end of NIO BUFFER case
+
+ colorChanged = 0xffff;
+ }
+
+
+ void setupMirrorNormalPointer(int ntype) {
+ int i, index;
+
+ switch (ntype) {
+ case NF:
+ if (floatRefNormals == null) {
+ if ((vertexType & NORMAL_DEFINED) == NF) {
+ vertexType &= ~NF;
+ mirrorFloatRefNormals = null;
+ mirrorNormalAllocated = false;
+ }
+ }
+ else {
+ vertexType |= NF;
+ mirrorFloatRefNormals = floatRefNormals;
+ mirrorNormalAllocated = false;
+ }
+ break;
+ case N3F:
+ if (v3fRefNormals == null) {
+ if ((vertexType & NORMAL_DEFINED) == N3F) {
+ vertexType &= ~N3F;
+ }
+ return;
+ }
+ else {
+ vertexType |= N3F;
+ }
+ if (!mirrorNormalAllocated) {
+ mirrorFloatRefNormals = new float[vertexCount * 3];
+ mirrorNormalAllocated = true;
+ }
+
+ index = initialNormalIndex * 3;
+ for (i = initialNormalIndex; i < validVertexCount; i++) {
+ mirrorFloatRefNormals[index++] = v3fRefNormals[i].x;
+ mirrorFloatRefNormals[index++] = v3fRefNormals[i].y;
+ mirrorFloatRefNormals[index++] = v3fRefNormals[i].z;
+ }
+ break;
+ default:
+ break; }
+ }
+
+ void setupMirrorTexCoordPointer(int type) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ setupMirrorTexCoordPointer(i, type);
+ }
+ }
+
+ void setupMirrorTexCoordPointer(int texCoordSet, int type) {
+ int i, index;
+
+ if (mirrorRefTexCoords == null)
+ mirrorRefTexCoords = new Object[texCoordSetCount];
+
+ switch (type) {
+ case TF:
+ if (refTexCoords[texCoordSet] == null) {
+ if ((vertexType & TEXCOORD_DEFINED) == TF) {
+ vertexType &= ~TF;
+ mirrorRefTexCoords[texCoordSet] = null;
+ mirrorTexCoordAllocated = false;
+ }
+ }
+ else {
+ vertexType |= TF;
+ mirrorRefTexCoords[texCoordSet] = refTexCoords[texCoordSet];
+ mirrorTexCoordAllocated = false;
+ }
+ break;
+ case T2F:
+ t2fRefTexCoords = (TexCoord2f[])refTexCoords[texCoordSet];
+
+ if (t2fRefTexCoords == null) {
+ if ((vertexType & TEXCOORD_DEFINED) == T2F) {
+ vertexType &= ~T2F;
+ }
+ return;
+ }
+ else {
+ vertexType |= T2F;
+ }
+
+ mirrorFloatRefTexCoords = (float[])mirrorRefTexCoords[texCoordSet];
+ if (mirrorFloatRefTexCoords != null) {
+ if (mirrorFloatRefTexCoords.length < (vertexCount * 2))
+ mirrorRefTexCoords[texCoordSet] =
+ mirrorFloatRefTexCoords = new float[vertexCount * 2];
+ }
+ else {
+ mirrorRefTexCoords[texCoordSet] =
+ mirrorFloatRefTexCoords = new float[vertexCount * 2];
+ }
+
+ index = initialTexCoordIndex[texCoordSet] * 2;
+ for (i = initialTexCoordIndex[texCoordSet]; i < validVertexCount; i++) {
+ mirrorFloatRefTexCoords[index++] = t2fRefTexCoords[i].x;
+ mirrorFloatRefTexCoords[index++] = t2fRefTexCoords[i].y;
+ }
+ mirrorTexCoordAllocated = true;
+ break;
+ case T3F:
+
+ t3fRefTexCoords = (TexCoord3f[])refTexCoords[texCoordSet];
+ if (t3fRefTexCoords == null) {
+ if ((vertexType & TEXCOORD_DEFINED) == T3F) {
+ vertexType &= ~T3F;
+ }
+ return;
+ }
+ else {
+ vertexType |= T3F;
+ }
+
+ mirrorFloatRefTexCoords = (float[])mirrorRefTexCoords[texCoordSet];
+ if (mirrorFloatRefTexCoords != null) {
+ if (mirrorFloatRefTexCoords.length < (vertexCount * 3))
+ mirrorRefTexCoords[texCoordSet] =
+ mirrorFloatRefTexCoords = new float[vertexCount * 3];
+ }
+ else {
+ mirrorRefTexCoords[texCoordSet] =
+ mirrorFloatRefTexCoords = new float[vertexCount * 3];
+ }
+
+ index = initialTexCoordIndex[texCoordSet] * 3;
+ for (i = initialTexCoordIndex[texCoordSet]; i < validVertexCount; i++) {
+ mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].x;
+ mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].y;
+ mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].z;
+ }
+ mirrorTexCoordAllocated = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+
+ void createGeometryArrayData(int vertexCount, int vertexFormat)
+ {
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ createGeometryArrayData(vertexCount, vertexFormat, 1,
+ defaultTexCoordSetMap);
+ } else {
+ createGeometryArrayData(vertexCount, vertexFormat, 0, null);
+ }
+ }
+
+ void createGeometryArrayData(int vertexCount, int vertexFormat,
+ int texCoordSetCount, int[] texCoordSetMap)
+ {
+ this.vertexFormat = vertexFormat;
+ this.vertexCount = vertexCount;
+ this.validVertexCount = vertexCount;
+ this.texCoordSetCount = texCoordSetCount;
+ this.texCoordSetMap = texCoordSetMap;
+ this.stride = this.stride();
+ this.texCoordSetMapOffset = this.texCoordSetMapOffset();
+ this.textureOffset = 0;
+ this.colorOffset = this.colorOffset();
+ this.normalOffset = this.normalOffset();
+ this.coordinateOffset = this.coordinateOffset();
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ this.vertexData = new float[this.vertexCount * this.stride];
+ }
+ else { // By reference geometry
+ this.vertexData = null;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ this.refTexCoords = new Object[texCoordSetCount]; // keep J3DBufferImp object in nio buffer case
+ if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 )
+ this.refTexCoordsBuffer = new Object[texCoordSetCount]; // keep J3DBuffer object
+ }
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ this.initialTexCoordIndex = new int[texCoordSetCount];
+ }
+ noAlpha = ((vertexFormat & GeometryArray.WITH_ALPHA) == 0);
+ lastAlpha[0] = 1.0f;
+
+ }
+
+ // used for GeometryArrays by Copy or interleaved
+ native void execute(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean useAlpha,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int startVIndex, int vcount, int vformat,
+ int texCoordSetCount, int texCoordSetMap[],
+ int texCoordSetMapLen,
+ int[] texCoordSetOffset,
+ int numActiveTexUnitState,
+ int[] texUnitStateMap,
+ float[] varray, float[] cdata, int texUnitIndex, int cdirty);
+
+
+
+
+ // used by GeometryArray by Reference with java arrays
+ native void executeVA(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int vcount,
+ int vformat,
+ int vdefined,
+ int coordIndex, float[] vfcoords, double[] vdcoords,
+ int colorIndex, float[] cfdata, byte[] cbdata,
+ int normalIndex, float[] ndata,
+ int pass, int texcoordmaplength,
+ int[] texcoordoffset,
+ int numActiveTexUnitState, int[] texunitstatemap,
+ int[] texIndex, int texstride, Object[] texCoords,
+ int cdirty);
+
+
+
+ // used by GeometryArray by Reference with NIO buffer
+ native void executeVABuffer(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int vcount,
+ int vformat,
+ int vdefined,
+ int coordIndex,
+ Object vcoords,
+ int colorIndex,
+ Object cdataBuffer,
+ float[] cfdata, byte[] cbdata,
+ int normalIndex, Object ndata,
+ int pass, int texcoordmaplength,
+ int[] texcoordoffset,
+ int numActiveTexUnitState, int[] texunitstatemap,
+ int[] texIndex, int texstride, Object[] texCoords,
+ int cdirty);
+
+ // used by GeometryArray by Reference in interleaved format with NIO buffer
+ native void executeInterleavedBuffer(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean useAlpha,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int startVIndex, int vcount, int vformat,
+ int texCoordSetCount, int texCoordSetMap[],
+ int texCoordSetMapLen,
+ int[] texCoordSetOffset,
+ int numActiveTexUnitState,
+ int[] texUnitStateMap,
+ Object varray, float[] cdata, int texUnitIndex, int cdirty);
+
+
+
+ native void setVertexFormat(int vformat, boolean useAlpha,
+ boolean ignoreVertexColors, long ctx);
+ native void disableGlobalAlpha(long ctx, int vformat,
+ boolean useAlpha,
+ boolean ignoreVertexColors);
+
+ void setVertexFormat(boolean useAlpha, boolean ignoreVC, long ctx) {
+ setVertexFormat(vertexFormat, useAlpha, ignoreVC, ctx);
+ }
+
+ void disableGlobalAlpha(long ctx, boolean useAlpha, boolean ignoreVC) {
+ // If global alpha was turned on, then disable it
+ disableGlobalAlpha(ctx, vertexFormat, useAlpha, ignoreVC);
+ }
+
+
+ float[] updateAlphaInFloatRefColors(Canvas3D cv, int screen, float alpha) {
+
+ //System.out.println("updateAlphaInFloatRefColors screen = " + screen +
+ // " alpha " + alpha );
+
+ // no need to update alpha values if canvas supports global alpha
+ if (cv.supportGlobalAlpha()) {
+ cv.setGlobalAlpha(cv.ctx, alpha);
+ return mirrorFloatRefColors[0];
+ }
+
+ // update alpha only if vertex format includes alpha
+ if (((vertexFormat | c4fAllocated) & GeometryArray.WITH_ALPHA) == 0)
+ return mirrorFloatRefColors[0];
+
+ // if alpha is smaller than EPSILON, set it to EPSILON, so that
+ // even if alpha is equal to 0, we will not completely lose
+ // the original alpha value
+ if (alpha <= EPSILON) {
+ alpha = (float)EPSILON;
+ }
+
+ // allocate an entry for the last alpha of the screen if needed
+ if (lastAlpha == null) {
+ lastAlpha = new float[screen + 1];
+ lastAlpha[screen] = 1.0f;
+ } else if (lastAlpha.length <= screen) {
+ float[] la = new float[screen + 1];
+ for (int i = 0; i < lastAlpha.length; i++) {
+ la[i] = lastAlpha[i];
+ }
+ lastAlpha = la;
+ lastAlpha[screen] = 1.0f;
+ }
+
+ //System.out.println("updateAlphaInFloatRefColors screen is " + screen
+ // + " mirrorFloatRefColors.length " +
+ // mirrorFloatRefColors.length);
+
+ // allocate a copy of the color data for the screen if needed.
+ // this piece of code is mainly for multi-screens case
+ if (mirrorFloatRefColors.length <= screen) {
+ float[][] cfData = new float[screen + 1][];
+ float[] cdata;
+ int refScreen = -1;
+
+ for (int i = 0; i < mirrorFloatRefColors.length; i++) {
+ cfData[i] = mirrorFloatRefColors[i];
+ if (Math.abs(lastAlpha[i] - alpha) < EPSILON) {
+ refScreen = i;
+ }
+ }
+ cdata = cfData[screen] = new float[4 * vertexCount];
+
+ // copy the data from a reference screen which has the closest
+ // alpha values
+ if (refScreen >= 0) {
+ System.arraycopy(cfData[refScreen], 0, cdata, 0,
+ 4 * vertexCount);
+ lastAlpha[screen] = lastAlpha[refScreen];
+ } else {
+ float m = alpha / lastAlpha[0];
+ float[] sdata = cfData[0];
+
+ int j = initialColorIndex * 4;
+ for (int i = initialColorIndex; i < validVertexCount; i++) {
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++] * m;
+ }
+ lastAlpha[screen] = alpha;
+ }
+ mirrorFloatRefColors = cfData;
+
+ // reset the colorChanged bit
+ colorChanged &= ~(1 << screen);
+ dirtyFlag |= COLOR_CHANGED;
+
+ return cdata;
+ }
+
+ /*
+ System.out.println("updateAlphaInFloatRefColors ** : lastAlpha[screen] " +
+ lastAlpha[screen]);
+
+ System.out.println("((colorChanged & (1<<screen)) == 0) " +
+ ((colorChanged & (1<<screen)) == 0));
+ */
+
+ if ((colorChanged & (1<<screen)) == 0) {
+ // color data is not modified
+ if (Math.abs(lastAlpha[screen] - alpha) < EPSILON) {
+ // and if alpha is the same as the last one,
+ // just return the data
+ //System.out.println("updateAlphaInFloatRefColors 0 : alpha is the same as the last one " + alpha);
+
+ return mirrorFloatRefColors[screen];
+ } else {
+
+ // if alpha is different, update the alpha values
+ //System.out.println("updateAlphaInFloatRefColors 1 : alpha is different, update the alpha values " + alpha);
+
+ float m = alpha / lastAlpha[screen];
+
+ float[] cdata = mirrorFloatRefColors[screen];
+
+ // We've to traverse the whole due to BugId : 4676483
+ for (int i = 0, j = 0; i < vertexCount; i++, j+=4) {
+ cdata[j+3] = cdata[j+3] * m;
+ }
+ }
+ } else {
+ // color data is modified
+ if (screen == 0) {
+
+ // just update alpha values since screen 0 data is
+ // already updated in setupMirrorColorPointer
+
+ //System.out.println("updateAlphaInFloatRefColors 2 : just update alpha = " + alpha);
+
+ float[] cdata = mirrorFloatRefColors[screen];
+
+
+ // This part is also incorrect due to BugId : 4676483
+ // But traversing the whole array doesn't help either, as there
+ // isn't a mechanism to indicate the the alpha component has
+ // not changed by user.
+ int j = initialColorIndex * 4;
+ for (int i = initialColorIndex; i < validVertexCount; i++, j+=4) {
+ cdata[j+3] = cdata[j+3] * alpha;
+ }
+ } else {
+ // update color values from screen 0 data
+ //System.out.println("updateAlphaInFloatRefColors 3 : update color values from screen 0 data " + alpha);
+
+ float m;
+
+ if ((colorChanged & 1) == 0) {
+ // alpha is up to date in screen 0
+ m = alpha / lastAlpha[0];
+ } else {
+ m = alpha;
+ }
+
+ float[] sdata = mirrorFloatRefColors[0];
+ float[] cdata = mirrorFloatRefColors[screen];
+
+ int j = initialColorIndex * 4;
+ for (int i = initialColorIndex; i < validVertexCount; i++) {
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++] * m;
+ }
+ }
+ }
+
+ lastAlpha[screen] = alpha;
+ colorChanged &= ~(1 << screen);
+ dirtyFlag |= COLOR_CHANGED;
+ return mirrorFloatRefColors[screen];
+ }
+
+
+ byte[] updateAlphaInByteRefColors(Canvas3D cv, int screen, float alpha) {
+
+ /*
+ System.out.println("updateAlphaInByteRefColors screen = " + screen +
+ " alpha " + alpha );
+ */
+
+ // no need to update alpha values if canvas supports global alpha
+
+ if (cv.supportGlobalAlpha()) {
+ cv.setGlobalAlpha(cv.ctx, alpha);
+ return mirrorUnsignedByteRefColors[0];
+ }
+
+ // update alpha only if vertex format includes alpha
+ if (((vertexFormat | c4fAllocated) & GeometryArray.WITH_ALPHA) == 0)
+ return mirrorUnsignedByteRefColors[0];
+
+ // if alpha is smaller than EPSILON, set it to EPSILON, so that
+ // even if alpha is equal to 0, we will not completely lose
+ // the original alpha value
+ if (alpha <= EPSILON) {
+ alpha = (float)EPSILON;
+ }
+
+ // allocate an entry for the last alpha of the screen if needed
+ if (lastAlpha == null) {
+ lastAlpha = new float[screen + 1];
+ lastAlpha[screen] = -1.0f;
+ } else if (lastAlpha.length <= screen) {
+ float[] la = new float[screen + 1];
+ for (int i = 0; i < lastAlpha.length; i++) {
+ la[i] = lastAlpha[i];
+ }
+ lastAlpha = la;
+ lastAlpha[screen] = -1.0f;
+ }
+
+ // allocate a copy of the color data for the screen if needed.
+ // this piece of code is mainly for multi-screens case
+ if (mirrorUnsignedByteRefColors.length <= screen) {
+ byte[][] cfData = new byte[screen + 1][];
+ byte[] cdata;
+ int refScreen = -1;
+ for (int i = 0; i < mirrorUnsignedByteRefColors.length; i++) {
+ cfData[i] = mirrorUnsignedByteRefColors[i];
+ if (Math.abs(lastAlpha[i] - alpha) < EPSILON) {
+ refScreen = i;
+ }
+ }
+ cdata = cfData[screen] = new byte[4 * vertexCount];
+
+ // copy the data from a reference screen which has the closest
+ // alpha values
+ if (refScreen >= 0) {
+ System.arraycopy(cfData[refScreen], 0, cdata, 0,
+ 4 * vertexCount);
+ lastAlpha[screen] = lastAlpha[refScreen];
+ } else {
+ float m = alpha / lastAlpha[0];
+ byte[] sdata = cfData[0];
+
+ int j = initialColorIndex * 4;
+ for (int i = initialColorIndex; i < validVertexCount; i++) {
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = (byte)(((int)sdata[j++]& 0xff) * m);
+ }
+ lastAlpha[screen] = alpha;
+ }
+ mirrorUnsignedByteRefColors = cfData;
+ colorChanged &= ~(1 << screen);
+ dirtyFlag |= COLOR_CHANGED;
+ return cdata;
+ }
+
+ /*
+ System.out.println("updateAlphaInByteRefColors ## : lastAlpha[screen] " +
+ lastAlpha[screen]);
+
+ System.out.println("((colorChanged & (1<<screen)) == 0) " +
+ ((colorChanged & (1<<screen)) == 0));
+ */
+
+ if ((colorChanged & (1<<screen)) == 0) {
+ // color data is not modified
+ if (Math.abs(lastAlpha[screen] - alpha) < EPSILON) {
+ // and if alpha is the same as the last one,
+ // just return the data
+ //System.out.println("updateAlphaInByteRefColors 0 : alpha is the same as the last one " + alpha);
+
+ return mirrorUnsignedByteRefColors[screen];
+ } else {
+ // if alpha is different, update the alpha values
+
+ //System.out.println("updateAlphaInByteRefColors 1 : alpha is different, update the alpha values " + alpha);
+
+ float m = alpha / lastAlpha[screen];
+
+ byte[] cdata = mirrorUnsignedByteRefColors[screen];
+
+ // We've to traverse the whole due to BugId : 4676483
+ for (int i = 0, j = 0; i < vertexCount; i++, j+=4) {
+ cdata[j+3] = (byte)( ((int)cdata[j+3] & 0xff) * m);
+ }
+ }
+ } else {
+ // color data is modified
+ if (screen == 0) {
+ //System.out.println("updateAlphaInByteRefColors 2 : just update alpha =" + alpha);
+
+ // just update alpha values since screen 0 data is
+ // already updated in setupMirrorColorPointer
+
+ byte[] cdata = mirrorUnsignedByteRefColors[screen];
+
+ // This part is also incorrect due to BugId : 4676483
+ // But traversing the whole array doesn't help either, as there
+ // isn't a mechanism to indicate the the alpha component has
+ // not changed by user.
+ int j = initialColorIndex * 4;
+ for (int i = initialColorIndex; i < validVertexCount; i++, j+=4) {
+ cdata[j+3] = (byte)(((int)cdata[j+3] & 0xff) * alpha);
+ }
+ } else {
+ // update color values from screen 0 data
+ float m;
+
+ //System.out.println("updateAlphaInByteRefColors 3 : update color values from screen 0 data " + alpha);
+
+ if ((colorChanged & 1) == 0) {
+ // alpha is up to date in screen 0
+ m = alpha / lastAlpha[0];
+ } else {
+ m = alpha;
+ }
+ byte[] sdata = mirrorUnsignedByteRefColors[0];
+ byte[] cdata = mirrorUnsignedByteRefColors[screen];
+
+ int j = initialColorIndex * 4;
+ for (int i = initialColorIndex; i < validVertexCount; i++) {
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = sdata[j++];
+ cdata[j] = (byte)(((int)sdata[j++]& 0xff) * m);
+ }
+ }
+ }
+
+ lastAlpha[screen] = alpha;
+ colorChanged &= ~(1 << screen);
+ dirtyFlag |= COLOR_CHANGED;
+ return mirrorUnsignedByteRefColors[screen];
+ }
+
+
+ Object[] updateAlphaInVertexData(Canvas3D cv, int screen, float alpha) {
+
+ Object[] retVal = new Object[2];
+ retVal[0] = Boolean.FALSE;
+
+ // no need to update alpha values if canvas supports global alpha
+ if (cv.supportGlobalAlpha()) {
+ cv.setGlobalAlpha(cv.ctx, alpha);
+ retVal[1] = vertexData;
+ return retVal;
+ }
+
+ // update alpha only if vertex format includes alpha
+ if ((vertexFormat & GeometryArray.COLOR) == 0) {
+ retVal[1] = vertexData;
+ return retVal;
+ }
+
+ // if alpha is smaller than EPSILON, set it to EPSILON, so that
+ // even if alpha is equal to 0, we will not completely lose
+ // the original alpha value
+ if (alpha <= EPSILON) {
+ alpha = (float)EPSILON;
+ }
+ retVal[0] = Boolean.TRUE;
+
+ // allocate an entry for the last alpha of the screen if needed
+ if (lastAlpha == null) {
+ lastAlpha = new float[screen + 1];
+ lastAlpha[screen] = 1.0f;
+ } else if (lastAlpha.length <= screen) {
+ float[] la = new float[screen + 1];
+ for (int i = 0; i < lastAlpha.length; i++) {
+ la[i] = lastAlpha[i];
+ }
+ lastAlpha = la;
+ lastAlpha[screen] = 1.0f;
+ }
+
+ // allocate a copy of the vertex data for the screen if needed.
+ // this piece of code is mainly for multi-screens case
+ // TODO: this might not too much data for just to update alpha
+ if (mvertexData == null || mvertexData.length <= screen) {
+
+ float[][] cfData = new float[screen + 1][];
+ float[] cdata;
+ int refScreen = -1;
+
+ if (mvertexData != null) {
+ for (int i = 0; i < mvertexData.length; i++) {
+ cfData[i] = mvertexData[i];
+ if (Math.abs(lastAlpha[i] - alpha) < EPSILON) {
+ refScreen = i;
+ }
+ }
+ }
+
+ if (cfData[0] == null) {
+ cfData[screen] = vertexData;
+ }
+
+ if (screen > 0)
+ cfData[screen] = new float[stride * vertexCount];
+
+ cdata = cfData[screen];
+
+ // copy the data from a reference screen which has the closest
+ // alpha values
+ if (refScreen >= 0) {
+ System.arraycopy(cfData[refScreen], 0, cdata, 0,
+ stride * vertexCount);
+ lastAlpha[screen] = lastAlpha[refScreen];
+ } else {
+ float m = alpha / lastAlpha[0];
+ float[] sdata = cfData[0];
+
+ /*
+ // screen 0 data is always up-to-date
+ if (screen > 0) {
+ System.arraycopy(cfData[0], 0, cdata, 0,
+ stride * vertexCount);
+ }
+ */
+
+ for (int i = 0, j = colorOffset; i < vertexCount;
+ i++, j+=stride) {
+ cdata[j+3] = sdata[j+3] * m;
+ }
+ lastAlpha[screen] = alpha;
+ }
+ mvertexData = cfData;
+ dirtyFlag |= COLOR_CHANGED;
+ // reset the colorChanged bit
+ colorChanged &= ~(1 << screen);
+ retVal[1] = cdata;
+ return retVal;
+ }
+
+ if ((colorChanged & (1<<screen)) == 0) {
+ // color data is not modified
+ if (Math.abs(lastAlpha[screen] - alpha) < EPSILON) {
+ // and if alpha is the same as the last one,
+ // just return the data
+ retVal[1] = mvertexData[screen];
+ return retVal;
+ } else {
+ // if alpha is different, update the alpha values
+ float m = alpha / lastAlpha[screen];
+
+ float[] cdata = mvertexData[screen];
+ for (int i = 0, j = colorOffset; i < vertexCount;
+ i++, j+=stride) {
+ cdata[j+3] *= m;
+ }
+ }
+ } else {
+ // color data is modified
+ if (screen == 0) {
+ // just update alpha values since screen 0 data is
+ // already updated in setupMirrorColorPointer
+
+ float[] cdata = mvertexData[screen];
+ double m = alpha / lastAlpha[0];
+
+ for (int i = 0, j = colorOffset; i < vertexCount;
+ i++, j+=stride) {
+ cdata[j+3] *= m;
+ }
+ } else {
+ // update color values from screen 0 data
+
+ float m = alpha / lastAlpha[0];
+ float[] sdata = mvertexData[0];
+ float[] cdata = mvertexData[screen];
+
+ for (int i = 0, j = colorOffset; i < vertexCount;
+ i++, j+=stride) {
+ System.arraycopy(sdata, j, cdata, j, 3);
+ cdata[j+3] = sdata[j+3] * m;
+ }
+ }
+ }
+
+ lastAlpha[screen] = alpha;
+ colorChanged &= ~(1 << screen);
+ dirtyFlag |= COLOR_CHANGED;
+ retVal[1] = mvertexData[screen];
+ return retVal;
+ }
+
+ Object[] updateAlphaInInterLeavedData(Canvas3D cv, int screen, float alpha) {
+
+ Object[] retVal = new Object[2];
+ retVal[0] = Boolean.FALSE;
+
+ // no need to update alpha values if canvas supports global alpha
+ if (cv.supportGlobalAlpha()) {
+ cv.setGlobalAlpha(cv.ctx, alpha);
+ retVal[1] = null;
+ return retVal;
+ }
+
+ // update alpha only if vertex format includes alpha
+ if (((vertexFormat | c4fAllocated) & GeometryArray.COLOR) == 0) {
+ retVal[1] = mirrorInterleavedColorPointer[0];
+ return retVal;
+ }
+ int coffset = initialColorIndex << 2; // Each color is 4 floats
+
+ // if alpha is smaller than EPSILON, set it to EPSILON, so that
+ // even if alpha is equal to 0, we will not completely lose
+ // the original alpha value
+ if (alpha <= EPSILON) {
+ alpha = (float)EPSILON;
+ }
+ retVal[0] = Boolean.TRUE;
+
+ // allocate an entry for the last alpha of the screen if needed
+ if (lastAlpha == null) {
+ lastAlpha = new float[screen + 1];
+ lastAlpha[screen] = 1.0f;
+ } else if (lastAlpha.length <= screen) {
+ float[] la = new float[screen + 1];
+ for (int i = 0; i < lastAlpha.length; i++) {
+ la[i] = lastAlpha[i];
+ }
+ lastAlpha = la;
+ lastAlpha[screen] = 1.0f;
+ }
+
+ // allocate a copy of the vertex data for the screen if needed.
+ // this piece of code is mainly for multi-screens case
+ // TODO: this might not too much data for just to update alpha
+ if (mirrorInterleavedColorPointer.length <= screen) {
+
+ float[][] cfData = new float[screen + 1][];
+ float[] cdata;
+ int refScreen = -1;
+
+ for (int i = 0; i < mirrorInterleavedColorPointer.length; i++) {
+ cfData[i] = mirrorInterleavedColorPointer[i];
+ if (Math.abs(lastAlpha[i] - alpha) < EPSILON) {
+ refScreen = i;
+ }
+ }
+
+ //cdata = cfData[screen] = new float[stride * vertexCount];
+ cdata = cfData[screen] = new float[4 * vertexCount];
+
+ // copy the data from a reference screen which has the closest
+ // alpha values
+ if (refScreen >= 0) {
+ System.arraycopy(cfData[refScreen], 0, cdata, 0,
+ 4 * vertexCount);
+ lastAlpha[screen] = lastAlpha[refScreen];
+ } else {
+ float m = alpha / lastAlpha[0];
+ float[] sdata = cfData[0];
+
+ for (int i = coffset; i < coffset + (vertexCount << 2); i+=4) {
+ cdata[i+3] = sdata[i+3] * m;
+ }
+
+ lastAlpha[screen] = alpha;
+ }
+ mirrorInterleavedColorPointer = cfData;
+
+ // reset the colorChanged bit
+ colorChanged &= ~(1 << screen);
+ dirtyFlag |= COLOR_CHANGED;
+ retVal[1] = cdata;
+ return retVal;
+ }
+
+ if ((colorChanged & (1<<screen)) == 0) {
+ // color data is not modified
+ if (Math.abs(lastAlpha[screen] - alpha) < EPSILON) {
+ // and if alpha is the same as the last one,
+ // just return the data
+ retVal[1] = mirrorInterleavedColorPointer[screen];
+ return retVal;
+ } else {
+
+ // if alpha is different, update the alpha values
+
+ float m = alpha / lastAlpha[screen];
+
+ float[] cdata = mirrorInterleavedColorPointer[screen];
+
+ coffset = initialColorIndex << 2;
+ for (int i = coffset; i < coffset + (vertexCount << 2); i+=4) {
+ cdata[i+3] = cdata[i+3] * m;
+ }
+ }
+ } else {
+ // color data is modified
+ if (screen == 0) {
+
+ // just update alpha values since screen 0 data is
+ // already updated in setupMirrorInterleavedColorPointer
+
+ float[] cdata = mirrorInterleavedColorPointer[screen];
+
+ for (int i = coffset; i < coffset + (vertexCount << 2); i+=4) {
+ cdata[i+3] = cdata[i+3] * alpha;
+ }
+ } else {
+ // update color values from screen 0 data
+
+ float m;
+
+ if ((colorChanged & 1) == 0) {
+ // alpha is up to date in screen 0
+ m = alpha / lastAlpha[0];
+ } else {
+ m = alpha;
+ }
+
+ float[] sdata = mirrorInterleavedColorPointer[0];
+ float[] cdata = mirrorInterleavedColorPointer[screen];
+
+ for (int i = coffset; i < coffset + (vertexCount << 2);) {
+ // System.arraycopy(sdata, i, cdata, i, 3);
+ cdata[i] = sdata[i++];
+ cdata[i] = sdata[i++];
+ cdata[i] = sdata[i++];
+ cdata[i] = sdata[i++] * m;
+ }
+ }
+ }
+
+ lastAlpha[screen] = alpha;
+ colorChanged &= ~(1 << screen);
+ dirtyFlag |= COLOR_CHANGED;
+ retVal[1] = mirrorInterleavedColorPointer[screen];
+ return retVal;
+ }
+
+
+ // pass < 0 implies underlying library supports multiTexture, so
+ // use the multiTexture extension to send all texture units
+ // data in one pass
+ // pass >= 0 implies one pass for one texture unit state
+
+ void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha,
+ boolean multiScreen, int screen,
+ boolean ignoreVertexColors, int pass) {
+
+ int cdirty;
+ boolean useAlpha = false;
+ Object[] retVal;
+
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ float[] vdata;
+
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ // update the alpha values
+ retVal = updateAlphaInVertexData(cv, screen, alpha);
+ useAlpha = (retVal[0] == Boolean.TRUE);
+ vdata = (float[])retVal[1];
+
+ // D3D only
+ if (alpha != lastScreenAlpha) {
+ // handle multiple screen case
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ vdata = vertexData;
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ // geomLock is get in MasterControl when
+ // RenderBin render the geometry. So it is safe
+ // just to set the dirty flag here
+ dirtyFlag = 0;
+ }
+
+ execute(cv.ctx, this, geoType, isNonUniformScale,
+ useAlpha,
+ multiScreen,
+ ignoreVertexColors,
+ initialVertexIndex,
+ validVertexCount,
+ ((vertexFormat & GeometryArray.COLOR) != 0)?(vertexFormat|GeometryArray.COLOR_4):vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ cv.numActiveTexUnit, cv.texUnitStateMap,
+ vdata, null,
+ pass, cdirty);
+ }
+
+ //By reference with java array
+ else if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ // interleaved data
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ if(interLeavedVertexData == null)
+ return;
+
+ float[] cdata = null;
+
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ // update the alpha values
+ retVal = updateAlphaInInterLeavedData(cv, screen, alpha);
+ useAlpha = (retVal[0] == Boolean.TRUE);
+ cdata = (float[])retVal[1];
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+
+ execute(cv.ctx, this, geoType, isNonUniformScale,
+ useAlpha,
+ multiScreen,
+ ignoreVertexColors,
+ initialVertexIndex,
+ validVertexCount,
+ vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ cv.numActiveTexUnit, cv.texUnitStateMap,
+ interLeavedVertexData, cdata,
+ pass, cdirty);
+
+ } // end of interleaved case
+
+ // non interleaved data
+ else {
+
+ // Check if a vertexformat is set, but the array is null
+ // if yes, don't draw anything
+ if ((vertexType == 0) ||
+ ((vertexType & VERTEX_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.COLOR) != 0) &&
+ (vertexType & COLOR_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.NORMALS) != 0) &&
+ (vertexType & NORMAL_DEFINED) == 0) ||
+ (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
+ (vertexType & TEXCOORD_DEFINED) == 0)) {
+ return;
+ } else {
+ byte[] cbdata = null;
+ float[] cfdata = null;
+
+ if ((vertexType & (CF | C3F | C4F )) != 0) {
+
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cfdata = updateAlphaInFloatRefColors(cv, screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ cfdata = mirrorFloatRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+
+ }
+ dirtyFlag = 0;
+ }
+ } // end of color in float format
+ else if ((vertexType & (CUB| C3UB | C4UB)) != 0) {
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cbdata = updateAlphaInByteRefColors(cv, screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ cbdata = mirrorUnsignedByteRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+ } // end of color in byte format
+ else {
+ cdirty = dirtyFlag;
+ }
+ // setup vdefined to passed to native code
+ int vdefined = 0;
+ if((vertexType & (PF | P3F)) != 0)
+ vdefined |= COORD_FLOAT;
+ if((vertexType & (PD | P3D)) != 0)
+ vdefined |= COORD_DOUBLE;
+ if((vertexType & (CF | C3F | C4F)) != 0)
+ vdefined |= COLOR_FLOAT;
+ if((vertexType & (CUB| C3UB | C4UB)) != 0)
+ vdefined |= COLOR_BYTE;
+ if((vertexType & NORMAL_DEFINED) != 0)
+ vdefined |= NORMAL_FLOAT;
+ if((vertexType & TEXCOORD_DEFINED) != 0)
+ vdefined |= TEXCOORD_FLOAT;
+
+ executeVA(cv.ctx, this, geoType, isNonUniformScale,
+ multiScreen,
+ ignoreVertexColors,
+ validVertexCount,
+ (vertexFormat | c4fAllocated),
+ vdefined,
+ initialCoordIndex,
+ mirrorFloatRefCoords, mirrorDoubleRefCoords,
+ initialColorIndex, cfdata, cbdata,
+ initialNormalIndex, mirrorFloatRefNormals,
+ pass,
+ ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+ texCoordSetMap,
+ cv.numActiveTexUnit,
+ cv.texUnitStateMap,
+ initialTexCoordIndex,texCoordStride,
+ mirrorRefTexCoords, cdirty);
+ }// end of all vertex data being set
+ }// end of non interleaved case
+ }// end of by reference with java array
+
+ //By reference with nio buffer
+ else {
+ // interleaved data
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+
+ if ( interleavedFloatBufferImpl == null)
+ return;
+
+ float[] cdata = null;
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ // update the alpha values
+ // TODO: to handle alpha case
+ retVal = updateAlphaInInterLeavedData(cv, screen, alpha);
+ useAlpha = (retVal[0] == Boolean.TRUE);
+ cdata = (float[])retVal[1];
+
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // TODO: to handle alpha case
+ cdata = null;
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+
+
+ executeInterleavedBuffer(cv.ctx, this, geoType, isNonUniformScale,
+ useAlpha,
+ multiScreen,
+ ignoreVertexColors,
+ initialVertexIndex,
+ validVertexCount,
+ vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ cv.numActiveTexUnit, cv.texUnitStateMap,
+ interleavedFloatBufferImpl.getBufferAsObject(), cdata,
+ pass, cdirty);
+
+ } // end of interleaved case
+
+ // non interleaved data
+ else {
+ // Check if a vertexformat is set, but the array is null
+ // if yes, don't draw anything
+ if ((vertexType == 0) ||
+ ((vertexType & VERTEX_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.COLOR) != 0) &&
+ (vertexType & COLOR_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.NORMALS) != 0) &&
+ (vertexType & NORMAL_DEFINED) == 0) ||
+ (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
+ (vertexType & TEXCOORD_DEFINED) == 0)) {
+ return;
+ } else {
+ byte[] cbdata = null;
+ float[] cfdata = null;
+
+
+ if ((vertexType & CF ) != 0) {
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cfdata = updateAlphaInFloatRefColors(cv,
+ screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // TODO: handle transparency case
+ //cfdata = null;
+ cfdata = mirrorFloatRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+
+ }
+ dirtyFlag = 0;
+ }
+ } // end of color in float format
+ else if ((vertexType & CUB) != 0) {
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cbdata = updateAlphaInByteRefColors(
+ cv, screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // TODO: handle transparency case
+ //cbdata = null;
+ cbdata = mirrorUnsignedByteRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+ } // end of color in byte format
+ else {
+ cdirty = dirtyFlag;
+ }
+
+ Object vcoord = null, cdataBuffer=null, normal=null;
+
+ int vdefined = 0;
+ if((vertexType & PF) != 0) {
+ vdefined |= COORD_FLOAT;
+ vcoord = floatBufferRefCoords.getBufferAsObject();
+ } else if((vertexType & PD ) != 0) {
+ vdefined |= COORD_DOUBLE;
+ vcoord = doubleBufferRefCoords.getBufferAsObject();
+ }
+
+ if((vertexType & CF ) != 0) {
+ vdefined |= COLOR_FLOAT;
+ cdataBuffer = floatBufferRefColors.getBufferAsObject();
+ } else if((vertexType & CUB) != 0) {
+ vdefined |= COLOR_BYTE;
+ cdataBuffer = byteBufferRefColors.getBufferAsObject();
+ }
+
+ if((vertexType & NORMAL_DEFINED) != 0) {
+ vdefined |= NORMAL_FLOAT;
+ normal = floatBufferRefNormals.getBufferAsObject();
+ }
+
+ if((vertexType & TEXCOORD_DEFINED) != 0)
+ vdefined |= TEXCOORD_FLOAT;
+
+ executeVABuffer(cv.ctx, this, geoType, isNonUniformScale,
+ multiScreen,
+ ignoreVertexColors,
+ validVertexCount,
+ (vertexFormat | c4fAllocated),
+ vdefined,
+ initialCoordIndex,
+ vcoord,
+ initialColorIndex,
+ cdataBuffer,
+ cfdata, cbdata,
+ initialNormalIndex,
+ normal,
+ pass,
+ ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+ texCoordSetMap,
+ cv.numActiveTexUnit,
+ cv.texUnitStateMap,
+ initialTexCoordIndex,texCoordStride,
+ refTexCoords, cdirty);
+ }// end of all vertex data being set
+ }// end of non interleaved case
+ }// end of by reference with nio-buffer case
+ }
+
+ // used for GeometryArrays
+ native void buildGA(long ctx, GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale, boolean updateAlpha,
+ float alpha,
+ boolean ignoreVertexColors,
+ int startVIndex,
+ int vcount, int vformat,
+ int texCoordSetCount, int texCoordSetMap[],
+ int texCoordSetMapLen,
+ int[] texCoordSetMapOffset,
+ double[] xform, double[] nxform,
+ float[] varray);
+
+ // used to Build Dlist GeometryArray by Reference with java arrays
+ native void buildGAForByRef(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale, boolean updateAlpha,
+ float alpha,
+ boolean ignoreVertexColors,
+ int vcount,
+ int vformat,
+ int vdefined,
+ int coordIndex, float[] vfcoords, double[] vdcoords,
+ int colorIndex, float[] cfdata, byte[] cbdata,
+ int normalIndex, float[] ndata,
+ int texcoordmaplength,
+ int[] texcoordoffset,
+ int[] texIndex, int texstride, Object[] texCoords,
+ double[] xform, double[] nxform);
+
+
+ // used to Build Dlist GeometryArray by Reference with java arrays
+ native void buildGAForBuffer(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale, boolean updateAlpha,
+ float alpha,
+ boolean ignoreVertexColors,
+ int vcount,
+ int vformat,
+ int vdefined,
+ int coordIndex, Object vcoords,
+ int colorIndex, Object cdata,
+ int normalIndex, Object ndata,
+ int texcoordmaplength,
+ int[] texcoordoffset,
+ int[] texIndex, int texstride, Object[] texCoords,
+ double[] xform, double[] nxform);
+
+
+
+
+ void buildGA(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha, boolean ignoreVertexColors,
+ Transform3D xform, Transform3D nxform) {
+ float[] vdata = null;
+
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ vdata = vertexData;
+ }
+ else if ((vertexFormat & GeometryArray.INTERLEAVED) != 0 &&
+ ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0)) {
+ vdata = interLeavedVertexData;
+ }
+ if (vdata != null) {
+ /*
+ System.out.println("calling native buildGA()");
+ System.out.println("geoType = "+geoType+" initialVertexIndex = "+initialVertexIndex+" validVertexCount = "+validVertexCount+" vertexFormat = "+vertexFormat+" vertexData = "+vertexData);
+ */
+ buildGA(cv.ctx, this, geoType, isNonUniformScale,
+ updateAlpha, alpha, ignoreVertexColors,
+ initialVertexIndex,
+ validVertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ (xform == null) ? null : xform.mat,
+ (nxform == null) ? null : nxform.mat,
+ vdata);
+ }
+ else {
+ // Either non-interleaved, by-ref or nio buffer
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ // setup vdefined to passed to native code
+ int vdefined = 0;
+ if((vertexType & (PF | P3F)) != 0)
+ vdefined |= COORD_FLOAT;
+ if((vertexType & (PD | P3D)) != 0)
+ vdefined |= COORD_DOUBLE;
+ if((vertexType & (CF | C3F | C4F)) != 0)
+ vdefined |= COLOR_FLOAT;
+ if((vertexType & (CUB| C3UB | C4UB)) != 0)
+ vdefined |= COLOR_BYTE;
+ if((vertexType & NORMAL_DEFINED) != 0)
+ vdefined |= NORMAL_FLOAT;
+ if((vertexType & TEXCOORD_DEFINED) != 0)
+ vdefined |= TEXCOORD_FLOAT;
+ buildGAForByRef(cv.ctx, this, geoType, isNonUniformScale,
+ updateAlpha, alpha,
+ ignoreVertexColors,
+ validVertexCount,
+ vertexFormat,
+ vdefined,
+ initialCoordIndex,
+ mirrorFloatRefCoords, mirrorDoubleRefCoords,
+ initialColorIndex, mirrorFloatRefColors[0], mirrorUnsignedByteRefColors[0],
+ initialNormalIndex, mirrorFloatRefNormals,
+ ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+ texCoordSetMap,
+ initialTexCoordIndex,texCoordStride,
+ mirrorRefTexCoords,
+ (xform == null) ? null : xform.mat,
+ (nxform == null) ? null : nxform.mat);
+ }
+ else {
+ Object vcoord = null, cdataBuffer=null, normal=null;
+
+ int vdefined = 0;
+ if((vertexType & PF) != 0) {
+ vdefined |= COORD_FLOAT;
+ vcoord = floatBufferRefCoords.getBufferAsObject();
+ } else if((vertexType & PD ) != 0) {
+ vdefined |= COORD_DOUBLE;
+ vcoord = doubleBufferRefCoords.getBufferAsObject();
+ }
+
+ if((vertexType & CF ) != 0) {
+ vdefined |= COLOR_FLOAT;
+ cdataBuffer = floatBufferRefColors.getBufferAsObject();
+ } else if((vertexType & CUB) != 0) {
+ vdefined |= COLOR_BYTE;
+ cdataBuffer = byteBufferRefColors.getBufferAsObject();
+ }
+
+ if((vertexType & NORMAL_DEFINED) != 0) {
+ vdefined |= NORMAL_FLOAT;
+ normal = floatBufferRefNormals.getBufferAsObject();
+ }
+
+ if((vertexType & TEXCOORD_DEFINED) != 0)
+ vdefined |= TEXCOORD_FLOAT;
+ buildGAForBuffer(cv.ctx, this, geoType, isNonUniformScale,
+ updateAlpha, alpha,
+ ignoreVertexColors,
+ validVertexCount,
+ vertexFormat,
+ vdefined,
+ initialCoordIndex,
+ vcoord,
+ initialColorIndex,cdataBuffer,
+ initialNormalIndex, normal,
+ ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+ texCoordSetMap,
+ initialTexCoordIndex,texCoordStride,
+ refTexCoords,
+ (xform == null) ? null : xform.mat,
+ (nxform == null) ? null : nxform.mat);
+ }
+
+ }
+
+ }
+
+ void unIndexify(IndexedGeometryArrayRetained src) {
+ if ((src.vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ unIndexifyJavaArray(src);
+ }
+ else {
+ unIndexifyNIOBuffer(src);
+ }
+ }
+
+ void unIndexifyJavaArray(IndexedGeometryArrayRetained src) {
+ int vOffset = 0, srcOffset, tOffset = 0;
+ int index, colorStride = 0;
+ float[] vdata = null;
+ int i;
+ int start, end;
+ start = src.initialIndexIndex;
+ end = src.initialIndexIndex + src.validIndexCount;
+ // If its either "normal" data or interleaved data then ..
+ if (((src.vertexFormat & GeometryArray.BY_REFERENCE) == 0) ||
+ ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0)) {
+
+ if ((src.vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ vdata = src.vertexData;
+ if ((src.vertexFormat & GeometryArray.COLOR) != 0)
+ colorStride = 4;
+ }
+ else if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ vdata = src.interLeavedVertexData;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ colorStride = 4;
+ else if ((src.vertexFormat & GeometryArray.COLOR) != 0)
+ colorStride = 3;
+ }
+
+ // System.out.println("===> start = "+start+" end = "+end);
+ for (index= start; index < end; index++) {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ System.arraycopy(vdata,
+ src.indexNormal[index]*src.stride + src.normalOffset,
+ vertexData, vOffset + normalOffset, 3);
+ }
+ if (colorStride == 4){
+ // System.out.println("===> copying color3");
+ System.arraycopy(vdata,
+ src.indexColor[index]*src.stride + src.colorOffset,
+ vertexData, vOffset + colorOffset, colorStride);
+ } else if (colorStride == 3) {
+ // System.out.println("===> copying color4");
+ System.arraycopy(vdata,
+ src.indexColor[index]*src.stride + src.colorOffset,
+ vertexData, vOffset + colorOffset, colorStride);
+ vertexData[vOffset + colorOffset + 3] = 1.0f;
+ }
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ int tcOffset = vOffset + textureOffset;
+ int interleavedOffset = 0;
+
+ for (i = 0; i < texCoordSetCount;
+ i++, tcOffset += texCoordStride) {
+
+ if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ interleavedOffset = i * texCoordStride;
+ }
+
+ System.arraycopy(vdata,
+ (((int[])src.indexTexCoord[i])[index])*src.stride + src.textureOffset + interleavedOffset,
+ vertexData, tcOffset, texCoordStride);
+ }
+ }
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ // System.out.println("===> copying coords");
+ System.arraycopy(vdata,
+ src.indexCoord[index]*src.stride
+ + src.coordinateOffset,
+ vertexData,
+ vOffset + coordinateOffset, 3);
+ }
+ vOffset += stride;
+ }
+
+ } else {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ vOffset = normalOffset;
+ switch ((src.vertexType & NORMAL_DEFINED)) {
+ case NF:
+ for (index=start; index < end; index++) {
+ System.arraycopy(src.floatRefNormals,
+ src.indexNormal[index]*3,
+ vertexData,
+ vOffset, 3);
+ vOffset += stride;
+ }
+ break;
+ case N3F:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexNormal[index];
+ vertexData[vOffset] = src.v3fRefNormals[srcOffset].x;
+ vertexData[vOffset+1] = src.v3fRefNormals[srcOffset].y;
+ vertexData[vOffset+2] = src.v3fRefNormals[srcOffset].z;
+ vOffset += stride;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.COLOR) != 0){
+ vOffset = colorOffset;
+ int multiplier = 3;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ multiplier = 4;
+
+ switch ((src.vertexType & COLOR_DEFINED)) {
+ case CF:
+ for (index=start; index < end; index++) {
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ System.arraycopy(src.floatRefColors,
+ src.indexColor[index]*multiplier,
+ vertexData,
+ vOffset, 4);
+ }
+ else {
+ System.arraycopy(src.floatRefColors,
+ src.indexColor[index]*multiplier,
+ vertexData,
+ vOffset, 3);
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ break;
+ case CUB:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexColor[index] * multiplier;
+ vertexData[vOffset] = (src.byteRefColors[srcOffset] & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.byteRefColors[srcOffset+1] & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+2] = (src.byteRefColors[srcOffset+2] & 0xff) * ByteToFloatScale;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ vertexData[vOffset+3] = (src.byteRefColors[srcOffset+3] & 0xff) * ByteToFloatScale;
+ }
+ else {
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ break;
+ case C3F:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexColor[index];
+ vertexData[vOffset] = src.c3fRefColors[srcOffset].x;
+ vertexData[vOffset+1] = src.c3fRefColors[srcOffset].y;
+ vertexData[vOffset+2] = src.c3fRefColors[srcOffset].z;
+ vertexData[vOffset+3] = 1.0f;
+ vOffset += stride;
+ }
+ break;
+ case C4F:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexColor[index];
+ vertexData[vOffset] = src.c4fRefColors[srcOffset].x;
+ vertexData[vOffset+1] = src.c4fRefColors[srcOffset].y;
+ vertexData[vOffset+2] = src.c4fRefColors[srcOffset].z;
+ vertexData[vOffset+3] = src.c4fRefColors[srcOffset].w;
+ vOffset += stride;
+ }
+ break;
+ case C3UB:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexColor[index];
+ vertexData[vOffset] = (src.c3bRefColors[srcOffset].x & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.c3bRefColors[srcOffset].y & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+2] = (src.c3bRefColors[srcOffset].z & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+3] = 1.0f;
+ vOffset += stride;
+ }
+ break;
+ case C4UB:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexColor[index];
+ vertexData[vOffset] = (src.c4bRefColors[srcOffset].x & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.c4bRefColors[srcOffset].y & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+2] = (src.c4bRefColors[srcOffset].z & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+3] = (src.c4bRefColors[srcOffset].w & 0xff) * ByteToFloatScale;
+ vOffset += stride;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ vOffset = textureOffset;
+ switch ((src.vertexType & TEXCOORD_DEFINED)) {
+ case TF:
+ for (index=start; index < end; index++) {
+ for (i = 0, tOffset = vOffset;
+ i < texCoordSetCount; i++) {
+ System.arraycopy(src.refTexCoords[i],
+ ((int[])src.indexTexCoord[i])[index]*texCoordStride,
+ vertexData, tOffset, texCoordStride);
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ break;
+ case T2F:
+ for (index=start; index < end; index++) {
+ for (i = 0, tOffset = vOffset;
+ i < texCoordSetCount; i++) {
+ srcOffset = ((int[])src.indexTexCoord[i])[index];
+ vertexData[tOffset] =
+ ((TexCoord2f[])src.refTexCoords[i])[srcOffset].x;
+ vertexData[tOffset+1] =
+ ((TexCoord2f[])src.refTexCoords[i])[srcOffset].y;
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ break;
+ case T3F:
+ for (index=start; index < end; index++) {
+ for (i = 0, tOffset = vOffset;
+ i < texCoordSetCount; i++) {
+ srcOffset = ((int[])src.indexTexCoord[i])[index];
+ vertexData[tOffset] =
+ ((TexCoord3f[])src.refTexCoords[i])[srcOffset].x;
+ vertexData[tOffset+1] =
+ ((TexCoord3f[])src.refTexCoords[i])[srcOffset].y;
+ vertexData[tOffset+2] =
+ ((TexCoord3f[])src.refTexCoords[i])[srcOffset].z;
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ vOffset = coordinateOffset;
+ switch ((src.vertexType & VERTEX_DEFINED)) {
+ case PF:
+ for (index=start; index < end; index++) {
+ System.arraycopy(src.floatRefCoords,
+ src.indexCoord[index]*3,
+ vertexData,
+ vOffset, 3);
+ vOffset += stride;
+ }
+ break;
+ case PD:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexCoord[index] * 3;
+ vertexData[vOffset] = (float)src.doubleRefCoords[srcOffset];
+ vertexData[vOffset+1] = (float)src.doubleRefCoords[srcOffset+1];
+ vertexData[vOffset+2] = (float)src.doubleRefCoords[srcOffset+2];
+ vOffset += stride;
+ }
+ break;
+ case P3F:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexCoord[index];
+ vertexData[vOffset] = src.p3fRefCoords[srcOffset].x;
+ vertexData[vOffset+1] = src.p3fRefCoords[srcOffset].y;
+ vertexData[vOffset+2] = src.p3fRefCoords[srcOffset].z;
+ vOffset += stride;
+ }
+ break;
+ case P3D:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexCoord[index];
+ vertexData[vOffset] = (float)src.p3dRefCoords[srcOffset].x;
+ vertexData[vOffset+1] = (float)src.p3dRefCoords[srcOffset].y;
+ vertexData[vOffset+2] = (float)src.p3dRefCoords[srcOffset].z;
+ vOffset += stride;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+ }
+
+
+ void unIndexifyNIOBuffer(IndexedGeometryArrayRetained src) {
+ int vOffset = 0, srcOffset, tOffset = 0;
+ int index, colorStride = 0;
+ float[] vdata = null;
+ int i;
+ int start, end;
+ start = src.initialIndexIndex;
+ end = src.initialIndexIndex + src.validIndexCount;
+ // If its interleaved data then ..
+ if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ colorStride = 4;
+ else if ((src.vertexFormat & GeometryArray.COLOR) != 0)
+ colorStride = 3;
+
+ // System.out.println("===> start = "+start+" end = "+end);
+ for (index= start; index < end; index++) {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ src.interleavedFloatBufferImpl.position(src.indexNormal[index]*src.stride + src.normalOffset);
+ src.interleavedFloatBufferImpl.get(vertexData, vOffset + normalOffset, 3);
+ }
+
+ if (colorStride == 4){
+ src.interleavedFloatBufferImpl.position(src.indexColor[index]*src.stride + src.colorOffset);
+ src.interleavedFloatBufferImpl.get(vertexData, vOffset + colorOffset, colorStride);
+ } else if (colorStride == 3) {
+ src.interleavedFloatBufferImpl.position(src.indexColor[index]*src.stride + src.colorOffset);
+ src.interleavedFloatBufferImpl.get(vertexData, vOffset + colorOffset, colorStride);
+ vertexData[vOffset + colorOffset + 3] = 1.0f;
+ }
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ int tcOffset = vOffset + textureOffset;
+ for (i = 0; i < texCoordSetCount;
+ i++, tcOffset += texCoordStride) {
+
+ src.interleavedFloatBufferImpl.position((((int[])src.indexTexCoord[i])[index])*src.stride +
+ src.textureOffset);
+ src.interleavedFloatBufferImpl.get(vertexData, tcOffset, texCoordStride);
+ }
+ }
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ src.interleavedFloatBufferImpl.position(src.indexCoord[index]*src.stride + src.coordinateOffset );
+ src.interleavedFloatBufferImpl.get(vertexData, vOffset + coordinateOffset, 3);
+ }
+ vOffset += stride;
+ }
+
+ } else {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ vOffset = normalOffset;
+ if ((src.vertexType & NORMAL_DEFINED) != 0) {
+ for (index=start; index < end; index++) {
+ src.floatBufferRefNormals.position(src.indexNormal[index]*3);
+ src.floatBufferRefNormals.get(vertexData, vOffset, 3);
+ vOffset += stride;
+ }
+ }
+ }
+ if ((vertexFormat & GeometryArray.COLOR) != 0){
+ vOffset = colorOffset;
+ int multiplier = 3;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ multiplier = 4;
+
+ switch ((src.vertexType & COLOR_DEFINED)) {
+ case CF:
+ for (index=start; index < end; index++) {
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ src.floatBufferRefColors.position(src.indexColor[index]*multiplier);
+ src.floatBufferRefColors.get(vertexData, vOffset, 4);
+ }
+ else {
+ src.floatBufferRefColors.position(src.indexColor[index]*multiplier);
+ src.floatBufferRefColors.get(vertexData, vOffset, 3);
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ break;
+ case CUB:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexColor[index] * multiplier;
+ vertexData[vOffset] = (src.byteBufferRefColors.get(srcOffset) & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.byteBufferRefColors.get(srcOffset+1) & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+2] = (src.byteBufferRefColors.get(srcOffset+2) & 0xff) * ByteToFloatScale;
+
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ vertexData[vOffset+3] = (src.byteBufferRefColors.get(srcOffset+3) & 0xff) * ByteToFloatScale;
+ }
+ else {
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ vOffset = textureOffset;
+ FloatBufferWrapper texBuffer;
+ if ((src.vertexType & TEXCOORD_DEFINED) != 0) {
+ for (index=start; index < end; index++) {
+ for (i = 0, tOffset = vOffset;
+ i < texCoordSetCount; i++) {
+ texBuffer = (FloatBufferWrapper)(((J3DBuffer) (src.refTexCoordsBuffer[i])).getBufferImpl());
+ texBuffer.position(((int[])src.indexTexCoord[i])[index]*texCoordStride);
+ texBuffer.get(vertexData, tOffset, texCoordStride);
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ }
+ }
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ vOffset = coordinateOffset;
+ switch ((src.vertexType & VERTEX_DEFINED)) {
+ case PF:
+ for (index=start; index < end; index++) {
+ src.floatBufferRefCoords.position(src.indexCoord[index]*3);
+ src.floatBufferRefCoords.get(vertexData, vOffset, 3);
+ vOffset += stride;
+ }
+ break;
+ case PD:
+ for (index=start; index < end; index++) {
+ srcOffset = src.indexCoord[index] * 3;
+ vertexData[vOffset] = (float)src.doubleBufferRefCoords.get(srcOffset);
+ vertexData[vOffset+1] = (float)src.doubleBufferRefCoords.get(srcOffset+1);
+ vertexData[vOffset+2] = (float)src.doubleBufferRefCoords.get(srcOffset+2);
+ vOffset += stride;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Returns the vertex stride in numbers of floats as a function
+ * of the vertexFormat.
+ * @return the stride in floats for this vertex array
+ */
+ int stride()
+ {
+ int stride = 0;
+
+ if((this.vertexFormat & GeometryArray.COORDINATES) != 0) stride += 3;
+ if((this.vertexFormat & GeometryArray.NORMALS) != 0) stride += 3;
+
+ if ((this.vertexFormat & GeometryArray.COLOR) != 0) {
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ // By copy
+ stride += 4;
+ } else {
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+ stride += 3;
+ }
+ else {
+ stride += 4;
+ }
+ }
+ }
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+
+ if ((this.vertexFormat &
+ GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ texCoordStride = 2;
+ } else if ((this.vertexFormat &
+ GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ texCoordStride = 3;
+ } else if ((this.vertexFormat &
+ GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ texCoordStride = 4;
+ }
+
+ stride += texCoordStride * texCoordSetCount;
+ }
+
+ return stride;
+ }
+
+ int[] texCoordSetMapOffset()
+ {
+ if (texCoordSetMap == null)
+ return null;
+
+ texCoordSetMapOffset = new int[texCoordSetMap.length];
+ for (int i = 0; i < texCoordSetMap.length; i++) {
+ if (texCoordSetMap[i] == -1) {
+ texCoordSetMapOffset[i] = -1;
+ } else {
+ texCoordSetMapOffset[i] = texCoordSetMap[i] * texCoordStride;
+ }
+ }
+ return texCoordSetMapOffset;
+ }
+
+ /**
+ * Returns the offset in number of floats from the start of a vertex to
+ * the per-vertex color data.
+ * color data always follows texture data
+ * @param vertexFormat the vertex format for this array
+ * @return the offset in floats vertex start to the color data
+ */
+ int colorOffset()
+ {
+ int offset = textureOffset;
+
+ if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0)
+ offset += 2 * texCoordSetCount;
+ else if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0)
+ offset += 3 * texCoordSetCount;
+ else if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0)
+ offset += 4 * texCoordSetCount;
+
+ return offset;
+ }
+
+ /**
+ * Returns the offset in number of floats from the start of a vertex to
+ * the per-vertex normal data.
+ * normal data always follows color data
+ * @return the offset in floats from the start of a vertex to the normal
+ */
+ int normalOffset()
+ {
+ int offset = colorOffset;
+
+ if ((this.vertexFormat & GeometryArray.COLOR) != 0) {
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ offset += 4;
+ } else {
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+ offset += 3;
+ }
+ else {
+ offset += 4;
+ }
+ }
+ }
+ return offset;
+ }
+
+ /**
+ * Returns the offset in number of floats from the start of a vertex to
+ * the per vertex coordinate data.
+ * @return the offset in floats vertex start to the coordinate data
+ */
+ int coordinateOffset()
+ {
+ int offset = normalOffset;
+
+ if ((this.vertexFormat & GeometryArray.NORMALS) != 0) offset += 3;
+ return offset;
+ }
+
+ /**
+ * Returns number of vertices in the GeometryArray
+ * @return vertexCount number of vertices in the GeometryArray
+ */
+ int getVertexCount(){
+ return vertexCount;
+ }
+
+ /**
+ * Returns vertexFormat in the GeometryArray
+ * @return vertexFormat format of vertices in the GeometryArray
+ */
+ int getVertexFormat(){
+ return vertexFormat;
+ }
+
+
+
+ void sendDataChangedMessage(boolean coordinatesChanged) {
+ J3dMessage[] m;
+ int i, j, k, index, numShapeMessages, numMorphMessages;
+ ArrayList shapeList;
+ Shape3DRetained s;
+ ArrayList morphList;
+ MorphRetained morph;
+
+ synchronized(liveStateLock) {
+ if (source != null && source.isLive()) {
+ // System.out.println("In GeometryArrayRetained - ");
+
+ // Send a message to renderBin to rebuild the display list or
+ // process the vertex array accordingly
+ // TODO: Should I send one per universe, isn't display list
+ // shared by all context/universes?
+ int threads = J3dThread.UPDATE_RENDER;
+ // If the geometry type is Indexed then we need to clone the geometry
+ // We also need to update the cachedChangedFrequent flag
+ threads |= J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+
+ synchronized (universeList) {
+ numShapeMessages = universeList.size();
+ m = new J3dMessage[numShapeMessages];
+
+ k = 0;
+
+ for (i = 0; i < numShapeMessages; i++, k++) {
+ gaList.clear();
+
+ shapeList = (ArrayList)userLists.get(i);
+ for (j=0; j<shapeList.size(); j++) {
+ s = (Shape3DRetained)shapeList.get(j);
+ LeafRetained src = (LeafRetained)s.sourceNode;
+ // Should only need to update distinct localBounds.
+ if (coordinatesChanged && src.boundsAutoCompute) {
+ src.boundsDirty = true;
+ }
+ }
+
+ for (j=0; j<shapeList.size(); j++) {
+ s = (Shape3DRetained)shapeList.get(j);
+ LeafRetained src = (LeafRetained)s.sourceNode;
+ if (src.boundsDirty) {
+ // update combine bounds of mirrorShape3Ds. So we need to
+ // use its bounds and not localBounds.
+ // bounds is actually a reference to
+ // mirrorShape3D.source.localBounds.
+ src.updateBounds();
+ src.boundsDirty = false;
+ }
+ gaList.add(Shape3DRetained.getGeomAtom(s));
+ }
+
+ m[k] = VirtualUniverse.mc.getMessage();
+
+ m[k].type = J3dMessage.GEOMETRY_CHANGED;
+ // Who to send this message to ?
+ m[k].threads = threads;
+ m[k].args[0] = gaList.toArray();
+ m[k].args[1] = this;
+ m[k].args[2]= null;
+ m[k].args[3] = new Integer(changedFrequent);
+ m[k].universe=(VirtualUniverse)universeList.get(i);
+ }
+ VirtualUniverse.mc.processMessage(m);
+ }
+
+ if (morphUniverseList != null) {
+ synchronized (morphUniverseList) {
+ numMorphMessages = morphUniverseList.size();
+
+ // take care of morph that is referencing this geometry
+ if (numMorphMessages > 0) {
+ synchronized (morphUniverseList) {
+ for (i = 0; i < numMorphMessages; i++, k++) {
+ morphList = (ArrayList)morphUserLists.get(i);
+ for (j=0; j<morphList.size(); j++) {
+ morph = (MorphRetained)morphList.get(j);
+ morph.updateMorphedGeometryArray(this, coordinatesChanged);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate an array of 3 values containing the new coordinate
+ */
+ void setCoordinate(int index, float coordinate[]) {
+ int offset = this.stride * index + coordinateOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ this.vertexData[offset] = coordinate[0];
+ this.vertexData[offset+1]= coordinate[1];
+ this.vertexData[offset+2]= coordinate[2];
+
+ geomLock.unLock();
+
+ if (inUpdater || (source == null)) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+ sendDataChangedMessage(true);
+
+ }
+
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate an array of 3 values containing the new coordinate
+ */
+ void setCoordinate(int index, double coordinate[]) {
+ int offset = this.stride * index + coordinateOffset;
+
+
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ this.vertexData[offset] = (float)coordinate[0];
+ this.vertexData[offset+1]= (float)coordinate[1];
+ this.vertexData[offset+2]= (float)coordinate[2];
+ geomLock.unLock();
+
+ if (inUpdater || (source == null)) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate a vector containing the new coordinate
+ */
+ void setCoordinate(int index, Point3f coordinate) {
+ int offset = this.stride * index + coordinateOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ this.vertexData[offset] = coordinate.x;
+ this.vertexData[offset+1]= coordinate.y;
+ this.vertexData[offset+2]= coordinate.z;
+
+ geomLock.unLock();
+
+ if (inUpdater || (source == null)) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate a vector containing the new coordinate
+ */
+ void setCoordinate(int index, Point3d coordinate) {
+ int offset = this.stride * index + coordinateOffset;
+
+ geomLock.getLock();
+
+ dirtyFlag |= COORDINATE_CHANGED;
+ this.vertexData[offset] = (float)coordinate.x;
+ this.vertexData[offset+1]= (float)coordinate.y;
+ this.vertexData[offset+2]= (float)coordinate.z;
+
+ geomLock.unLock();
+
+ if (inUpdater ||source == null ) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of 3*n values containing n new coordinates
+ */
+ void setCoordinates(int index, float coordinates[]) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ for (i=0, j= offset;i < num; i+=3, j+= this.stride)
+ {
+ this.vertexData[j] = coordinates[i];
+ this.vertexData[j+1]= coordinates[i+1];
+ this.vertexData[j+2]= coordinates[i+2];
+ }
+
+ geomLock.unLock();
+ if (inUpdater ||source == null ) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of 3*n values containing n new coordinates
+ */
+ void setCoordinates(int index, double coordinates[]) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ for (i=0, j= offset;i < num; i+=3, j+= this.stride)
+ {
+ this.vertexData[j] = (float)coordinates[i];
+ this.vertexData[j+1]= (float)coordinates[i+1];
+ this.vertexData[j+2]= (float)coordinates[i+2];
+ }
+
+ geomLock.unLock();
+
+ if (inUpdater ||source == null ) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of vectors containing new coordinates
+ */
+ void setCoordinates(int index, Point3f coordinates[]) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ this.vertexData[j] = coordinates[i].x;
+ this.vertexData[j+1]= coordinates[i].y;
+ this.vertexData[j+2]= coordinates[i].z;
+ }
+
+ geomLock.unLock();
+
+ if (inUpdater ||source == null ) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of vectors containing new coordinates
+ */
+ void setCoordinates(int index, Point3d coordinates[]) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ this.vertexData[j] = (float)coordinates[i].x;
+ this.vertexData[j+1]= (float)coordinates[i].y;
+ this.vertexData[j+2]= (float)coordinates[i].z;
+ }
+
+ geomLock.unLock();
+
+ if (inUpdater ||source == null ) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+ }
+
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index the vertex index
+ * @param coordinates an array of vectors containing new coordinates
+ * @param start starting vertex index of data in <code>coordinates</code> .
+ * @param length number of vertices to be copied.
+ */
+ void setCoordinates(int index, float coordinates[], int start, int length) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ for (i= start * 3, j= offset; i < (start+length) * 3;
+ i+=3, j+= this.stride) {
+ this.vertexData[j] = coordinates[i];
+ this.vertexData[j+1]= coordinates[i+1];
+ this.vertexData[j+2]= coordinates[i+2];
+ }
+
+ geomLock.unLock();
+ if (inUpdater ||source == null ) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index the vertex index
+ * @param coordinates an array of 3*n values containing n new coordinates
+ * @param start starting vertex index of data in <code>coordinates</code> .
+ * @param length number of vertices to be copied.
+ */
+ void setCoordinates(int index, double coordinates[], int start, int length) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ for (i= start*3, j= offset; i < (start+length)*3;
+ i+=3, j+= this.stride) {
+ this.vertexData[j] = (float)coordinates[i];
+ this.vertexData[j+1]= (float)coordinates[i+1];
+ this.vertexData[j+2]= (float)coordinates[i+2];
+ }
+
+ geomLock.unLock();
+
+ if (inUpdater || (source == null)) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index the vertex index
+ * @param coordinates an array of vectors containing new coordinates
+ * @param start starting vertex index of data in <code>coordinates</code> .
+ * @param length number of vertices to be copied.
+ */
+ void setCoordinates(int index, Point3f coordinates[], int start,
+ int length) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ for (i=start, j= offset;i < start + length; i++, j+= this.stride) {
+ this.vertexData[j] = coordinates[i].x;
+ this.vertexData[j+1]= coordinates[i].y;
+ this.vertexData[j+2]= coordinates[i].z;
+ }
+
+ geomLock.unLock();
+
+
+ if (inUpdater || (source == null)) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the coordinates associated with the vertices starting at
+ * the specified index for this object using coordinate data starting
+ * from vertex index <code>start</code> for <code>length</code> vertices.
+ * @param index the vertex index
+ * @param coordinates an array of vectors containing new coordinates
+ * @param start starting vertex index of data in <code>coordinates</code> .
+ * @param length number of vertices to be copied.
+ */
+ void setCoordinates(int index, Point3d coordinates[], int start,
+ int length) {
+ int offset = this.stride * index + coordinateOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ for (i=start, j= offset;i < start + length; i++, j+= this.stride) {
+ this.vertexData[j] = (float)coordinates[i].x;
+ this.vertexData[j+1]= (float)coordinates[i].y;
+ this.vertexData[j+2]= (float)coordinates[i].z;
+ }
+
+ geomLock.unLock();
+
+
+ if (inUpdater || (source == null)) {
+ return;
+ }
+ if (!source.isLive()) {
+ boundsDirty = true;
+ return;
+ }
+
+ // Compute geo's bounds
+ processCoordsChanged(false);
+
+ sendDataChangedMessage(true);
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color an array of 3 or 4 values containing the new color
+ */
+ void setColor(int index, float color[]) {
+ int offset = this.stride*index + colorOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ this.vertexData[offset] = color[0];
+ this.vertexData[offset+1] = color[1];
+ this.vertexData[offset+2] = color[2];
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ this.vertexData[offset+3] = color[3]*lastAlpha[0];
+ else
+ this.vertexData[offset+3] = lastAlpha[0];
+
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color an array of 3 or 4 values containing the new color
+ */
+ void setColor(int index, byte color[]) {
+ int offset = this.stride*index + colorOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ this.vertexData[offset] = (color[0] & 0xff) * ByteToFloatScale;
+ this.vertexData[offset+1] = (color[1] & 0xff) * ByteToFloatScale;
+ this.vertexData[offset+2] = (color[2] & 0xff) * ByteToFloatScale;
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ this.vertexData[offset+3] = ((color[3] & 0xff)* ByteToFloatScale)*lastAlpha[0];
+ else
+ this.vertexData[offset+3] = lastAlpha[0];
+
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector containing the new color
+ */
+ void setColor(int index, Color3f color) {
+ int offset = this.stride*index + colorOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ this.vertexData[offset] = color.x;
+ this.vertexData[offset+1] = color.y;
+ this.vertexData[offset+2] = color.z;
+ this.vertexData[offset+3] = lastAlpha[0];
+
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector containing the new color
+ */
+ void setColor(int index, Color4f color) {
+ int offset = this.stride*index + colorOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ this.vertexData[offset] = color.x;
+ this.vertexData[offset+1] = color.y;
+ this.vertexData[offset+2] = color.z;
+ this.vertexData[offset+3] = color.w*lastAlpha[0];
+
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector containing the new color
+ */
+ void setColor(int index, Color3b color) {
+ int offset = this.stride*index + colorOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ this.vertexData[offset] = (color.x & 0xff) * ByteToFloatScale;
+ this.vertexData[offset+1] = (color.y & 0xff) * ByteToFloatScale;
+ this.vertexData[offset+2] = (color.z & 0xff) * ByteToFloatScale;
+ this.vertexData[offset+3] = lastAlpha[0];
+
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector containing the new color
+ */
+ void setColor(int index, Color4b color) {
+ int offset = this.stride*index + colorOffset;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ this.vertexData[offset] = (color.x * 0xff) * ByteToFloatScale;
+ this.vertexData[offset+1] = (color.y * 0xff) * ByteToFloatScale;
+ this.vertexData[offset+2] = (color.z * 0xff) * ByteToFloatScale;
+ this.vertexData[offset+3] = ((color.w & 0xff) * ByteToFloatScale)*lastAlpha[0];
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ */
+ void setColors(int index, float colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ {
+ for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
+ {
+ this.vertexData[j] = colors[i];
+ this.vertexData[j+1] = colors[i+1];
+ this.vertexData[j+2] = colors[i+2];
+ this.vertexData[j+3] = colors[i+3]*lastAlpha[0];
+ }
+ }
+ else
+ {
+ for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
+ {
+ this.vertexData[j] = colors[i];
+ this.vertexData[j+1] = colors[i+1];
+ this.vertexData[j+2] = colors[i+2];
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ */
+ void setColors(int index, byte colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ {
+ for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
+ {
+ this.vertexData[j] = (colors[i] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = ((colors[i+3] & 0xff) * ByteToFloatScale)*lastAlpha[0];
+ }
+ }
+ else
+ {
+ for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
+ {
+ this.vertexData[j] = (colors[i] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors containing new colors
+ */
+ void setColors(int index, Color3f colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ this.vertexData[j] = colors[i].x;
+ this.vertexData[j+1] = colors[i].y;
+ this.vertexData[j+2] = colors[i].z;
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors containing new colors
+ */
+ void setColors(int index, Color4f colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ this.vertexData[j] = colors[i].x;
+ this.vertexData[j+1] = colors[i].y;
+ this.vertexData[j+2] = colors[i].z;
+ this.vertexData[j+3] = colors[i].w*lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors containing new colors
+ */
+ void setColors(int index, Color3b colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ this.vertexData[j] = (colors[i].x & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+ }
+
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors containing new colors
+ */
+ void setColors(int index, Color4b colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ this.vertexData[j] = (colors[i].x & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = ((colors[i].w & 0xff) * ByteToFloatScale)*lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>color</code>s
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ * @param start starting color index of data in <code>colors</code>.
+ * @param length number of colors to be copied.
+ */
+ void setColors(int index, float colors[], int start, int length) {
+ int offset = this.stride*index + colorOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ for (i = start * 4, j = offset; i < (start + length) * 4;
+ i += 4, j += this.stride) {
+ this.vertexData[j] = colors[i];
+ this.vertexData[j+1] = colors[i+1];
+ this.vertexData[j+2] = colors[i+2];
+ this.vertexData[j+3] = colors[i+3]*lastAlpha[0];
+ }
+ } else {
+ for (i = start * 3, j = offset; i < (start + length) * 3;
+ i += 3, j += this.stride) {
+ this.vertexData[j] = colors[i];
+ this.vertexData[j+1] = colors[i+1];
+ this.vertexData[j+2] = colors[i+2];
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>color</code>s
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ * @param start starting color index of data in <code>colors</code>.
+ * @param length number of colors to be copied.
+ */
+ void setColors(int index, byte colors[], int start, int length) {
+ int offset = this.stride*index + colorOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ for (i = start * 4, j = offset; i < (start + length) * 4;
+ i += 4, j += this.stride) {
+ this.vertexData[j] = (colors[i] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = ((colors[i+3] & 0xff) * ByteToFloatScale)*lastAlpha[0];
+ }
+ } else {
+ for (i = start * 3, j = offset; i < (start + length) * 3;
+ i += 3, j += this.stride) {
+ this.vertexData[j] = (colors[i] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>color</code>s
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ * @param start starting color index of data in <code>colors</code>.
+ * @param length number of colors to be copied.
+ */
+ void setColors(int index, Color3f colors[], int start, int length) {
+ int offset = this.stride*index + colorOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = colors[i].x;
+ this.vertexData[j+1] = colors[i].y;
+ this.vertexData[j+2] = colors[i].z;
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>color</code>s
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ * @param start starting color index of data in <code>colors</code>.
+ * @param length number of colors to be copied.
+ */
+ void setColors(int index, Color4f colors[], int start, int length) {
+ int offset = this.stride*index + colorOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = colors[i].x;
+ this.vertexData[j+1] = colors[i].y;
+ this.vertexData[j+2] = colors[i].z;
+ this.vertexData[j+3] = colors[i].w*lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>color</code>s
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ * @param start starting color index of data in <code>colors</code>.
+ * @param length number of colors to be copied.
+ */
+ void setColors(int index, Color3b colors[], int start, int length) {
+ int offset = this.stride*index + colorOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = (colors[i].x & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the colors associated with the vertices starting at
+ * the specified index for this object using data in <code>color</code>s
+ * starting at index <code>start</code> for <code>length</code> colors.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values containing n new colors
+ * @param start starting color index of data in <code>colors</code>.
+ * @param length number of colors to be copied.
+ */
+ void setColors(int index, Color4b colors[], int start, int length) {
+ int offset = this.stride*index + colorOffset;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = (colors[i].x & 0xff) * ByteToFloatScale;
+ this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
+ this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
+ this.vertexData[j+3] = ((colors[i].w & 0xff) * ByteToFloatScale)*lastAlpha[0];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the normal associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param normal the new normal
+ */
+ void setNormal(int index, float normal[]) {
+ int offset = this.stride*index + normalOffset;
+
+ geomLock.getLock();
+
+
+ this.vertexData[offset] = normal[0];
+ this.vertexData[offset+1] = normal[1];
+ this.vertexData[offset+2] = normal[2];
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the normal associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param normal the vector containing the new normal
+ */
+ void setNormal(int index, Vector3f normal) {
+ int offset = this.stride*index + normalOffset;
+
+ geomLock.getLock();
+
+ dirtyFlag |= NORMAL_CHANGED;
+ this.vertexData[offset] = normal.x;
+ this.vertexData[offset+1] = normal.y;
+ this.vertexData[offset+2] = normal.z;
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param normals the new normals
+ */
+ void setNormals(int index, float normals[]) {
+ int offset = this.stride*index + normalOffset;
+ int i, j, num = normals.length;
+
+ geomLock.getLock();
+
+ dirtyFlag |= NORMAL_CHANGED;
+ for (i=0, j= offset;i < num;i += 3, j+= this.stride)
+ {
+ this.vertexData[j] = normals[i];
+ this.vertexData[j+1] = normals[i+1];
+ this.vertexData[j+2] = normals[i+2];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param normals the vector containing the new normals
+ */
+ void setNormals(int index, Vector3f normals[]) {
+ int offset = this.stride*index + normalOffset;
+ int i, j, num = normals.length;
+
+ geomLock.getLock();
+
+ dirtyFlag |= NORMAL_CHANGED;
+ for (i=0, j= offset;i < num;i++, j+= this.stride)
+ {
+ this.vertexData[j] = normals[i].x;
+ this.vertexData[j+1] = normals[i].y;
+ this.vertexData[j+2] = normals[i].z;
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index for this object using data in <code>normals</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param normals the new normals
+ * @param start starting normal index of data in <code>colors</code> .
+ * @param length number of normals to be copied.
+ */
+ void setNormals(int index, float normals[], int start, int length) {
+ int offset = this.stride*index + normalOffset;
+ int i, j;
+
+ geomLock.getLock();
+
+ dirtyFlag |= NORMAL_CHANGED;
+ for (i = start * 3, j = offset; i < (start + length) * 3;
+ i+=3, j += this.stride) {
+ this.vertexData[j] = normals[i];
+ this.vertexData[j+1] = normals[i+1];
+ this.vertexData[j+2] = normals[i+2];
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the normals associated with the vertices starting at
+ * the specified index for this object using data in <code>normals</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param normals the new normals
+ * @param start starting normal index of data in <code>colors</code> .
+ * @param length number of normals to be copied.
+ */
+ void setNormals(int index, Vector3f normals[], int start, int length) {
+ int offset = this.stride*index + normalOffset;
+ int i, j;
+
+ geomLock.getLock();
+
+ dirtyFlag |= NORMAL_CHANGED;
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = normals[i].x;
+ this.vertexData[j+1] = normals[i].y;
+ this.vertexData[j+2] = normals[i].z;
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index for this object using data in <code>texCoords</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param texCoords the new texture coordinates
+ * @param start starting texture coordinate index of data in <code>texCoords</code> .
+ * @param length number of texture Coordinates to be copied.
+ */
+ void setTextureCoordinates(int texCoordSet, int index, float texCoords[],
+ int start, int length) {
+
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j, k;
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ for (i = start * 4, j = offset, k = 0; k < length;
+ j += this.stride, k++) {
+ this.vertexData[j] = texCoords[i++];
+ this.vertexData[j+1] = texCoords[i++];
+ this.vertexData[j+2] = texCoords[i++];
+ this.vertexData[j+3] = texCoords[i++];
+ }
+ } else if ((this.vertexFormat &
+ GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ for (i = start * 3, j = offset, k = 0; k < length;
+ j += this.stride, k++) {
+ this.vertexData[j] = texCoords[i++];
+ this.vertexData[j+1] = texCoords[i++];
+ this.vertexData[j+2] = texCoords[i++];
+ }
+ } else {
+ for (i = start * 2, j = offset, k = 0; k < length;
+ j += this.stride, k++) {
+ this.vertexData[j] = texCoords[i++];
+ this.vertexData[j+1] = texCoords[i++];
+ }
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index for this object using data in <code>texCoords</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param texCoords the new texture coordinates
+ * @param start starting texture coordinate index of data in <code>texCoords</code> .
+ * @param length number of texture Coordinates to be copied.
+ */
+ void setTextureCoordinates(int texCoordSet, int index, Point2f texCoords[],
+ int start, int length) {
+
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = texCoords[i].x;
+ this.vertexData[j+1] = texCoords[i].y;
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index for this object using data in <code>texCoords</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param texCoords the new texture coordinates
+ * @param start starting texture coordinate index of data in <code>texCoords</code> .
+ * @param length number of texture Coordinates to be copied.
+ */
+ void setTextureCoordinates(int texCoordSet, int index, Point3f texCoords[],
+ int start, int length) {
+
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j;
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = texCoords[i].x;
+ this.vertexData[j+1] = texCoords[i].y;
+ this.vertexData[j+2] = texCoords[i].z;
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index for this object using data in <code>texCoords</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param texCoords the new texture coordinates
+ * @param start starting texture coordinate index of data in <code>texCoords</code> .
+ * @param length number of texture Coordinates to be copied.
+ */
+ void setTextureCoordinates(int texCoordSet, int index, TexCoord2f texCoords[],
+ int start, int length) {
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = texCoords[i].x;
+ this.vertexData[j+1] = texCoords[i].y;
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index for this object using data in <code>texCoords</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param texCoords the new texture coordinates
+ * @param start starting texture coordinate index of data in <code>texCoords</code> .
+ * @param length number of texture Coordinates to be copied.
+ */
+ void setTextureCoordinates(int texCoordSet, int index,
+ TexCoord3f texCoords[],
+ int start, int length) {
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = texCoords[i].x;
+ this.vertexData[j+1] = texCoords[i].y;
+ this.vertexData[j+2] = texCoords[i].z;
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+ }
+
+ /**
+ * Sets the texture coordinates associated with the vertices starting at
+ * the specified index for this object using data in <code>texCoords</code>
+ * starting at index <code>start</code> and ending at index <code>start+length</code>.
+ * @param index the vertex index
+ * @param texCoords the new texture coordinates
+ * @param start starting texture coordinate index of data in <code>texCoords</code> .
+ * @param length number of texture Coordinates to be copied.
+ */
+ void setTextureCoordinates(int texCoordSet, int index,
+ TexCoord4f texCoords[],
+ int start, int length) {
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+
+ if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j;
+
+ for (i = start, j = offset; i < start+length; i++, j += this.stride) {
+ this.vertexData[j] = texCoords[i].x;
+ this.vertexData[j+1] = texCoords[i].y;
+ this.vertexData[j+2] = texCoords[i].z;
+ this.vertexData[j+3] = texCoords[i].w;
+ }
+ if (source == null || !source.isLive()) {
+ geomLock.unLock();
+ return;
+ }
+
+ geomLock.unLock();
+ sendDataChangedMessage(false);
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate an array of 3 values that will receive the new coordinate
+ */
+ void getCoordinate(int index, float coordinate[]) {
+ int offset = this.stride*index + coordinateOffset;
+
+ coordinate[0]= this.vertexData[offset];
+ coordinate[1]= this.vertexData[offset+1];
+ coordinate[2]= this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate an array of 3 values that will receive the new coordinate
+ */
+ void getCoordinate(int index, double coordinate[]) {
+ int offset = this.stride*index + coordinateOffset;
+
+ coordinate[0]= (double)this.vertexData[offset];
+ coordinate[1]= (double)this.vertexData[offset+1];
+ coordinate[2]= (double)this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate a vector that will receive the new coordinate
+ */
+ void getCoordinate(int index, Point3f coordinate) {
+ int offset = this.stride*index + coordinateOffset;
+
+ coordinate.x = this.vertexData[offset];
+ coordinate.y = this.vertexData[offset+1];
+ coordinate.z = this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the coordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinate a vector that will receive the new coordinate
+ */
+ void getCoordinate(int index, Point3d coordinate) {
+ int offset = this.stride*index + coordinateOffset;
+
+ coordinate.x = (double)this.vertexData[offset];
+ coordinate.y = (double)this.vertexData[offset+1];
+ coordinate.z = (double)this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of 3*n values that will receive new coordinates
+ */
+ void getCoordinates(int index, float coordinates[]) {
+ int offset = this.stride*index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ for (i=0,j= offset;i < num;i +=3, j += this.stride)
+ {
+ coordinates[i] = this.vertexData[j];
+ coordinates[i+1]= this.vertexData[j+1];
+ coordinates[i+2]= this.vertexData[j+2];
+ }
+ }
+
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of 3*n values that will receive new coordinates
+ */
+ void getCoordinates(int index, double coordinates[]) {
+ int offset = this.stride*index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ for (i=0,j= offset;i < num;i +=3, j += this.stride)
+ {
+ coordinates[i] = (double)this.vertexData[j];
+ coordinates[i+1]= (double)this.vertexData[j+1];
+ coordinates[i+2]= (double)this.vertexData[j+2];
+ }
+ }
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of vectors that will receive new coordinates
+ */
+ void getCoordinates(int index, Point3f coordinates[]) {
+ int offset = this.stride*index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ for (i=0,j= offset;i < num;i++, j += this.stride)
+ {
+ coordinates[i].x = this.vertexData[j];
+ coordinates[i].y = this.vertexData[j+1];
+ coordinates[i].z = this.vertexData[j+2];
+ }
+ }
+
+ /**
+ * Gets the coordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param coordinates an array of vectors that will receive new coordinates
+ */
+ void getCoordinates(int index, Point3d coordinates[]) {
+ int offset = this.stride*index + coordinateOffset;
+ int i, j, num = coordinates.length;
+
+ for (i=0,j= offset;i < num;i++, j += this.stride)
+ {
+ coordinates[i].x = (double)this.vertexData[j];
+ coordinates[i].y = (double)this.vertexData[j+1];
+ coordinates[i].z = (double)this.vertexData[j+2];
+ }
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color an array of 3 or 4 values that will receive the new color
+ */
+ void getColor(int index, float color[]) {
+ int offset = this.stride*index + colorOffset;
+
+ color[0]= this.vertexData[offset];
+ color[1]= this.vertexData[offset+1];
+ color[2]= this.vertexData[offset+2];
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ color[3]= this.vertexData[offset+3]/lastAlpha[0];
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color an array of 3 or 4 values that will receive the new color
+ */
+ void getColor(int index, byte color[]) {
+ int offset = this.stride*index + colorOffset;
+
+ color[0]= (byte)(this.vertexData[offset] * FloatToByteScale);
+ color[1]= (byte)(this.vertexData[offset+1] * FloatToByteScale);
+ color[2]= (byte)(this.vertexData[offset+2] * FloatToByteScale);
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ color[3]= (byte)((this.vertexData[offset+3]/lastAlpha[0]) * FloatToByteScale);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector that will receive the new color
+ */
+ void getColor(int index, Color3f color) {
+ int offset = this.stride*index + colorOffset;
+
+ color.x = this.vertexData[offset];
+ color.y = this.vertexData[offset+1];
+ color.z = this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector that will receive the new color
+ */
+ void getColor(int index, Color4f color) {
+ int offset = this.stride*index + colorOffset;
+
+ color.x = this.vertexData[offset];
+ color.y = this.vertexData[offset+1];
+ color.z = this.vertexData[offset+2];
+ color.w= this.vertexData[offset+3]/lastAlpha[0];
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector that will receive the new color
+ */
+ void getColor(int index, Color3b color) {
+ int offset = this.stride*index + colorOffset;
+
+ color.x = (byte)(this.vertexData[offset] * FloatToByteScale);
+ color.y = (byte)(this.vertexData[offset+1] * FloatToByteScale);
+ color.z = (byte)(this.vertexData[offset+2] * FloatToByteScale);
+ }
+
+ /**
+ * Gets the color associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param color a vector that will receive the new color
+ */
+ void getColor(int index, Color4b color) {
+ int offset = this.stride*index + colorOffset;
+
+ color.x = (byte)(this.vertexData[offset] * FloatToByteScale);
+ color.y = (byte)(this.vertexData[offset+1] * FloatToByteScale);
+ color.z = (byte)(this.vertexData[offset+2] * FloatToByteScale);
+ color.w = (byte)((this.vertexData[offset+3]/lastAlpha[0]) * FloatToByteScale);
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values that will receive n new colors
+ */
+ void getColors(int index, float colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+ float val = 1.0f/lastAlpha[0];
+
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ {
+ for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
+ {
+ colors[i] = this.vertexData[j];
+ colors[i+1]= this.vertexData[j+1];
+ colors[i+2]= this.vertexData[j+2];
+ colors[i+3]= this.vertexData[j+3] * val;
+ }
+ }
+ else
+ {
+ for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
+ {
+ colors[i] = this.vertexData[j];
+ colors[i+1]= this.vertexData[j+1];
+ colors[i+2]= this.vertexData[j+2];
+ }
+ }
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of 3*n or 4*n values that will receive new colors
+ */
+ void getColors(int index, byte colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+ float val = 1.0f/lastAlpha[0];
+
+
+ if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ {
+ for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
+ {
+ colors[i] = (byte)(this.vertexData[j] * FloatToByteScale);
+ colors[i+1]= (byte)(this.vertexData[j+1] * FloatToByteScale);
+ colors[i+2]= (byte)(this.vertexData[j+2] * FloatToByteScale);
+ colors[i+3]= (byte)((this.vertexData[j+3] * val) * FloatToByteScale);
+ }
+ }
+ else
+ {
+ for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
+ {
+ colors[i] = (byte)(this.vertexData[j] * FloatToByteScale);
+ colors[i+1]= (byte)(this.vertexData[j+1] * FloatToByteScale);
+ colors[i+2]= (byte)(this.vertexData[j+2] * FloatToByteScale);
+ }
+ }
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors that will receive new colors
+ */
+ void getColors(int index, Color3f colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ colors[i].x = this.vertexData[j];
+ colors[i].y = this.vertexData[j+1];
+ colors[i].z = this.vertexData[j+2];
+ }
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors that will receive new colors
+ */
+ void getColors(int index, Color4f colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+ float val = 1.0f/lastAlpha[0];
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ colors[i].x = this.vertexData[j];
+ colors[i].y = this.vertexData[j+1];
+ colors[i].z = this.vertexData[j+2];
+ colors[i].w = this.vertexData[j+3] * val;
+ }
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors that will receive new colors
+ */
+ void getColors(int index, Color3b colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ colors[i].x = (byte)(this.vertexData[j] * FloatToByteScale);
+ colors[i].y = (byte)(this.vertexData[j+1] * FloatToByteScale);
+ colors[i].z = (byte)(this.vertexData[j+2] * FloatToByteScale);
+ }
+ }
+
+ /**
+ * Gets the colors associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param colors an array of vectors that will receive new colors
+ */
+ void getColors(int index, Color4b colors[]) {
+ int offset = this.stride*index + colorOffset;
+ int i, j, num = colors.length;
+ float val = 1.0f/lastAlpha[0];
+
+ for (i=0, j= offset;i < num; i++, j+= this.stride)
+ {
+ colors[i].x = (byte)(this.vertexData[j] * FloatToByteScale);
+ colors[i].y = (byte)(this.vertexData[j+1] * FloatToByteScale);
+ colors[i].z = (byte)(this.vertexData[j+2] * FloatToByteScale);
+ colors[i].w = (byte)(this.vertexData[j+3] * val * FloatToByteScale);
+ }
+ }
+
+ /**
+ * Gets the normal associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param normal array that will receive the new normal
+ */
+ void getNormal(int index, float normal[]) {
+ int offset = this.stride*index + normalOffset;
+
+ normal[0]= this.vertexData[offset];
+ normal[1]= this.vertexData[offset+1];
+ normal[2]= this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the normal associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param normal the vector that will receive the new normal
+ */
+ void getNormal(int index, Vector3f normal) {
+ int offset = this.stride*index + normalOffset;
+
+ normal.x= this.vertexData[offset];
+ normal.y= this.vertexData[offset+1];
+ normal.z= this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the normals associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param normals array that will receive the new normals
+ */
+ void getNormals(int index, float normals[]) {
+ int offset = this.stride*index + normalOffset;
+ int i, j, num = normals.length;
+
+ for (i=0, j= offset;i < num;i+=3, j+= this.stride)
+ {
+ normals[i] = this.vertexData[j];
+ normals[i+1]= this.vertexData[j+1];
+ normals[i+2]= this.vertexData[j+2];
+ }
+ }
+
+ /**
+ * Gets the normals associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param normals the vector that will receive the new normals
+ */
+ void getNormals(int index, Vector3f normals[]) {
+ int offset = this.stride*index + normalOffset;
+ int i, j, num = normals.length;
+
+ for (i=0, j= offset;i < num;i++, j+= this.stride)
+ {
+ normals[i].x= this.vertexData[j];
+ normals[i].y= this.vertexData[j+1];
+ normals[i].z= this.vertexData[j+2];
+ }
+ }
+
+ /**
+ * Gets the texture co-ordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoord array that will receive the new texture co-ordinate
+ */
+ void getTextureCoordinate(int texCoordSet, int index, float texCoord[]) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+
+ texCoord[0]= this.vertexData[offset];
+ texCoord[1]= this.vertexData[offset+1];
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ texCoord[2]= this.vertexData[offset+2];
+
+ } else if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4)
+ != 0) {
+ texCoord[2]= this.vertexData[offset+2];
+ texCoord[3]= this.vertexData[offset+3];
+ }
+ }
+
+ /**
+ * Gets the texture co-ordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoord the vector that will receive the new texture co-ordinates
+ */
+ void getTextureCoordinate(int texCoordSet, int index, TexCoord2f texCoord) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+
+ texCoord.x= this.vertexData[offset];
+ texCoord.y= this.vertexData[offset+1];
+ }
+
+ /**
+ * Gets the texture co-ordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoord the vector that will receive the new texture co-ordinates
+ */
+ void getTextureCoordinate(int texCoordSet, int index, TexCoord3f texCoord) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+
+ texCoord.x= this.vertexData[offset];
+ texCoord.y= this.vertexData[offset+1];
+ texCoord.z= this.vertexData[offset+2];
+ }
+
+ /**
+ * Gets the texture co-ordinate associated with the vertex at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoord the vector that will receive the new texture co-ordinates
+ */
+ void getTextureCoordinate(int texCoordSet, int index, TexCoord4f texCoord) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+
+ texCoord.x= this.vertexData[offset];
+ texCoord.y= this.vertexData[offset+1];
+ texCoord.z= this.vertexData[offset+2];
+ texCoord.w= this.vertexData[offset+3];
+ }
+
+ /**
+ * Gets the texture co-ordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoords array that will receive the new texture co-ordinates
+ */
+ void getTextureCoordinates(int texCoordSet, int index, float texCoords[]) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j, num = texCoords.length;
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ for (i=0, j= offset;i < num;i+=4, j+= this.stride)
+ {
+ texCoords[i]= this.vertexData[j];
+ texCoords[i+1]= this.vertexData[j+1];
+ texCoords[i+2]= this.vertexData[j+2];
+ texCoords[i+3]= this.vertexData[j+3];
+ }
+ } else if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_3)
+ != 0) {
+ for (i=0, j= offset;i < num;i+=3, j+= this.stride)
+ {
+ texCoords[i]= this.vertexData[j];
+ texCoords[i+1]= this.vertexData[j+1];
+ texCoords[i+2]= this.vertexData[j+2];
+ }
+ } else {
+ for (i=0, j= offset;i < num;i+=2, j+= this.stride)
+ {
+ texCoords[i]= this.vertexData[j];
+ texCoords[i+1]= this.vertexData[j+1];
+ }
+ }
+ }
+
+ /**
+ * Gets the texture co-ordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoords the vector that will receive the new texture co-ordinates
+ */
+ void getTextureCoordinates(int texCoordSet, int index,
+ TexCoord2f texCoords[]) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j, num = texCoords.length;
+
+ for (i=0, j= offset;i < num;i++, j+= this.stride)
+ {
+ texCoords[i].x= this.vertexData[j];
+ texCoords[i].y= this.vertexData[j+1];
+ }
+ }
+
+ /**
+ * Gets the texture co-ordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoords the vector that will receive the new texture co-ordinates
+ */
+ void getTextureCoordinates(int texCoordSet, int index, TexCoord3f texCoords[]) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j, num = texCoords.length;
+
+ for (i=0, j= offset;i < num;i++, j+= this.stride)
+ {
+ texCoords[i].x= this.vertexData[j];
+ texCoords[i].y= this.vertexData[j+1];
+ texCoords[i].z= this.vertexData[j+2];
+ }
+ }
+
+ /**
+ * Gets the texture co-ordinates associated with the vertices starting at
+ * the specified index.
+ * @param index the vertex index
+ * @param texCoords the vector that will receive the new texture co-ordinates
+ */
+ void getTextureCoordinates(int texCoordSet, int index, TexCoord4f texCoords[]) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j, num = texCoords.length;
+
+ for (i=0, j= offset;i < num;i++, j+= this.stride)
+ {
+ texCoords[i].x= this.vertexData[j];
+ texCoords[i].y= this.vertexData[j+1];
+ texCoords[i].z= this.vertexData[j+2];
+ texCoords[i].w= this.vertexData[j+3];
+ }
+ }
+
+ void getTextureCoordinates(int texCoordSet, int index,
+ Point2f texCoords[]) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j, num = texCoords.length;
+
+ for (i=0, j= offset;i < num;i++, j+= this.stride)
+ {
+ texCoords[i].x= this.vertexData[j];
+ texCoords[i].y= this.vertexData[j+1];
+ }
+ }
+
+ void getTextureCoordinates(int texCoordSet, int index, Point3f texCoords[]) {
+ int offset = this.stride*index + textureOffset +
+ texCoordSet * texCoordStride;
+ int i, j, num = texCoords.length;
+
+ for (i=0, j= offset;i < num;i++, j+= this.stride)
+ {
+ texCoords[i].x= this.vertexData[j];
+ texCoords[i].y= this.vertexData[j+1];
+ texCoords[i].z= this.vertexData[j+2];
+ }
+ }
+
+
+ /**
+ * Updates geometry array data.
+ */
+ void updateData(GeometryUpdater updater) {
+ boolean nullGeo = false;
+
+ // Add yourself to obtain the geometry lock
+ // and Thread.currentThread().sleep until you get the lock
+ geomLock.getLock();
+
+ inUpdater = true;
+ updater.updateData((Geometry)source);
+ inUpdater = false;
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0) {
+ if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ // TODO: handle the nio buffer
+ if (!(this instanceof IndexedGeometryArrayRetained) ||
+ (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+ if (((vertexFormat & GeometryArray.INTERLEAVED) != 0)) {
+ setupMirrorInterleavedColorPointer(false);
+ nullGeo = (interleavedFloatBufferImpl == null);
+ }
+ else {
+ setupMirrorColorPointer((vertexType & COLOR_DEFINED), false);
+ nullGeo = ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
+ }
+ }
+ }
+ else {
+ if (!(this instanceof IndexedGeometryArrayRetained) ||
+ (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+ if (((vertexFormat & GeometryArray.INTERLEAVED) != 0)) {
+ setupMirrorInterleavedColorPointer(false);
+ nullGeo = (interLeavedVertexData == null);
+ }
+ else {
+ setupMirrorVertexPointer((vertexType & VERTEX_DEFINED));
+ setupMirrorColorPointer((vertexType & COLOR_DEFINED), false);
+ setupMirrorNormalPointer((vertexType & NORMAL_DEFINED));
+ setupMirrorTexCoordPointer((vertexType & TEXCOORD_DEFINED));
+ nullGeo = ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
+ }
+ }
+ }
+ }
+ dirtyFlag |= VERTEX_CHANGED;
+ colorChanged = 0xffff;
+ geomLock.unLock();
+
+ if (source != null && source.isLive()) {
+ processCoordsChanged(nullGeo);
+ sendDataChangedMessage(true);
+ }
+ }
+
+ boolean intersectBoundingBox( Point3d coordinates[],
+ BoundingBox box,
+ double dist[],
+ Point3d iPnt) {
+ int i, j;
+ int out[] = new int[6];
+
+ //Do trivial vertex test.
+ for(i=0; i<6; i++)
+ out[i] = 0;
+ for(i=0; i<coordinates.length; i++) {
+ if((coordinates[i].x >= box.lower.x) && (coordinates[i].x <= box.upper.x) &&
+ (coordinates[i].y >= box.lower.y) && (coordinates[i].y <= box.upper.y) &&
+ (coordinates[i].z >= box.lower.z) && (coordinates[i].z <= box.upper.z))
+ // We're done! It's inside the boundingbox.
+ return true;
+ else {
+ if(coordinates[i].x < box.lower.x)
+ out[0]++; // left
+ if(coordinates[i].y < box.lower.y)
+ out[1]++; // bottom
+ if(coordinates[i].z < box.lower.z)
+ out[2]++; // back
+ if(coordinates[i].x > box.upper.x)
+ out[3]++; // right
+ if(coordinates[i].y > box.upper.y)
+ out[4]++; // top
+ if(coordinates[i].z > box.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] = getPoint3d();
+
+ // left plane.
+ pCoor[0].set(box.lower.x, box.lower.y, box.lower.z);
+ pCoor[1].set(box.lower.x, box.lower.y, box.upper.z);
+ pCoor[2].set(box.lower.x, box.upper.y, box.upper.z);
+ pCoor[3].set(box.lower.x, box.upper.y, box.lower.z);
+
+
+ if (intersectPolygon(pCoor, coordinates)) {
+ if (dist != null) {
+ computeMinDistance(pCoor, box.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ // free points
+ for (i=0; i<4; i++) freePoint3d(pCoor[i]);
+ return true;
+ }
+
+ // right plane.
+ pCoor[0].set(box.upper.x, box.lower.y, box.lower.z);
+ pCoor[1].set(box.upper.x, box.upper.y, box.lower.z);
+ pCoor[2].set(box.upper.x, box.upper.y, box.upper.z);
+ pCoor[3].set(box.upper.x, box.lower.y, box.upper.z);
+ if (intersectPolygon(pCoor, coordinates)) {
+ if (dist != null) {
+ computeMinDistance(pCoor, box.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ for (i=0; i<4; i++) freePoint3d(pCoor[i]);
+ return true;
+ }
+
+ // bottom plane.
+ pCoor[0].set(box.upper.x, box.lower.y, box.upper.z);
+ pCoor[1].set(box.lower.x, box.lower.y, box.upper.z);
+ pCoor[2].set(box.lower.x, box.lower.y, box.lower.z);
+ pCoor[3].set(box.upper.x, box.lower.y, box.lower.z);
+ if (intersectPolygon(pCoor, coordinates)) {
+ if (dist != null) {
+ computeMinDistance(pCoor, box.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ for (i=0; i<4; i++) freePoint3d(pCoor[i]);
+ return true;
+ }
+ // top plane.
+ pCoor[0].set(box.upper.x, box.upper.y, box.upper.z);
+ pCoor[1].set(box.upper.x, box.upper.y, box.lower.z);
+ pCoor[2].set(box.lower.x, box.upper.y, box.lower.z);
+ pCoor[3].set(box.lower.x, box.upper.y, box.upper.z);
+ if (intersectPolygon(pCoor, coordinates)) {
+ if (dist != null) {
+ computeMinDistance(pCoor, box.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ for (i=0; i<4; i++) freePoint3d(pCoor[i]);
+ return true;
+ }
+
+ // front plane.
+ pCoor[0].set(box.upper.x, box.upper.y, box.upper.z);
+ pCoor[1].set(box.lower.x, box.upper.y, box.upper.z);
+ pCoor[2].set(box.lower.x, box.lower.y, box.upper.z);
+ pCoor[3].set(box.upper.x, box.lower.y, box.upper.z);
+ if (intersectPolygon(pCoor, coordinates)) {
+ if (dist != null) {
+ computeMinDistance(pCoor, box.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ for (i=0; i<4; i++) freePoint3d(pCoor[i]);
+ return true;
+ }
+
+ // back plane.
+ pCoor[0].set(box.upper.x, box.upper.y, box.lower.z);
+ pCoor[1].set(box.upper.x, box.lower.y, box.lower.z);
+ pCoor[2].set(box.lower.x, box.lower.y, box.lower.z);
+ pCoor[3].set(box.lower.x, box.upper.y, box.lower.z);
+ if (intersectPolygon(pCoor, coordinates)) {
+ if (dist != null) {
+ computeMinDistance(pCoor, box.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ for (i=0; i<4; i++) freePoint3d(pCoor[i]);
+ return true;
+ }
+
+ for (i=0; i<4; i++) freePoint3d(pCoor[i]);
+ return false;
+ }
+
+
+ boolean intersectBoundingSphere(Point3d coordinates[],
+ BoundingSphere sphere,
+ double dist[],
+ Point3d iPnt)
+ {
+ int i, j;
+ Vector3d tempV3D = getVector3d();
+ boolean esFlag;
+
+ //Do trivial vertex test.
+
+ for (i=0; i<coordinates.length; i++) {
+ tempV3D.x = coordinates[i].x - sphere.center.x;
+ tempV3D.y = coordinates[i].y - sphere.center.y;
+ tempV3D.z = coordinates[i].z - sphere.center.z;
+
+ if (tempV3D.length() <= sphere.radius) {
+ // We're done! It's inside the boundingSphere.
+ if (dist != null) {
+ computeMinDistance(coordinates,
+ sphere.getCenter(),
+ null, dist, iPnt);
+ }
+
+ freeVector3d(tempV3D);
+ 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) {
+ if (dist != null) {
+ computeMinDistance(coordinates,
+ sphere.getCenter(),
+ null,
+ dist, iPnt);
+ }
+
+ freeVector3d(tempV3D);
+ return true;
+ }
+ }
+
+
+ if (coordinates.length < 3) {
+
+ freeVector3d(tempV3D);
+ return false; // We're done with line.
+ }
+
+ // Find rho.
+ // Compute plane normal.
+ Vector3d vec0 = getVector3d(); // Edge vector from point 0 to point 1;
+ Vector3d vec1 = getVector3d(); // Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = getVector3d();
+ Vector3d pa = getVector3d();
+ Point3d q = getPoint3d();
+ 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) Degenerate polygon.");
+ freeVector3d(tempV3D);
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(pa);
+ freePoint3d(q);
+ return false; // Degenerate 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) Degenerate polygon.");
+ freeVector3d(tempV3D);
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(pa);
+ freePoint3d(q);
+ return false; // Degenerate polygon.
+ }
+
+ pa.x = coordinates[0].x - sphere.center.x;
+ pa.y = coordinates[0].y - sphere.center.y;
+ pa.z = coordinates[0].z - sphere.center.z;
+
+ pNrmDotPa = pNrm.dot(pa);
+
+ pqLen = Math.sqrt(pNrmDotPa * pNrmDotPa/ nLenSq);
+
+ if(pqLen > sphere.radius) {
+ freeVector3d(tempV3D);
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(pa);
+ freePoint3d(q);
+ return false;
+ }
+
+ tq = pNrmDotPa / nLenSq;
+
+ q.x = sphere.center.x + tq * pNrm.x;
+ q.y = sphere.center.y + tq * pNrm.y;
+ q.z = sphere.center.z + tq * pNrm.z;
+
+ // PolyPnt2D Test.
+ if (pointIntersectPolygon2D( pNrm, coordinates, q)) {
+ if (dist != null) {
+ computeMinDistance(coordinates,
+ sphere.getCenter(),
+ pNrm,
+ dist, iPnt);
+ }
+ freeVector3d(tempV3D);
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(pa);
+ freePoint3d(q);
+ return true;
+ }
+ freeVector3d(tempV3D);
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(pa);
+ freePoint3d(q);
+ return false;
+
+ }
+
+
+ boolean intersectBoundingPolytope(Point3d coordinates[],
+ BoundingPolytope polytope,
+ double dist[],
+ Point3d iPnt)
+ {
+ boolean debug = false;
+
+ Point4d tP4d = new Point4d();
+
+ // this is a multiplier to the halfplane distance coefficients
+ double distanceSign = -1.0;
+
+ if(coordinates.length == 2) {
+ // we'll handle line separately.
+ if (polytope.intersect( coordinates[0],
+ coordinates[1], tP4d)) {
+ if (dist != null) {
+ iPnt.x = tP4d.x;
+ iPnt.y = tP4d.y;
+ iPnt.z = tP4d.z;
+ Point3d pc = polytope.getCenter();
+ double x = iPnt.x - pc.x;
+ double y = iPnt.y - pc.y;
+ double z = iPnt.z - pc.z;
+ dist[0] = Math.sqrt(x*x + y*y + z*z);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // 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 < polytope.planes.length; i++) {
+ System.out.println("The " +i+ " th plane is: " + polytope.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 < polytope.planes.length; j++) {
+ if ( ( polytope.planes[j].x * coordinates[i].x +
+ polytope.planes[j].y * coordinates[i].y +
+ polytope.planes[j].z*coordinates[i].z) <=
+ (distanceSign)*polytope.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
+ if (dist != null) {
+ computeMinDistance(coordinates,
+ polytope.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ 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 = polytope.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 < polytope.planes.length; i++) {
+ for( int j=0; j < coordinates.length; j++) {
+ problemTableau[j][i] = (-1.0)* (polytope.planes[i].x*coordinates[j].x+
+ polytope.planes[i].y*coordinates[j].y+
+ polytope.planes[i].z*coordinates[j].z);
+ }
+ }
+
+ // add the other rows
+ for(int i = 0; i < coordinates.length; i++) {
+ problemTableau[i][polytope.planes.length] = -1.0;
+ problemTableau[i][polytope.planes.length + 1] = 1.0;
+
+ for(int j=0; j < coordinates.length; j++) {
+ if ( i==j ) {
+ problemTableau[i][j + polytope.planes.length + 2] = 1.0;
+ } else {
+ problemTableau[i][j + polytope.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 < polytope.planes.length; j++) {
+ problemTableau[numberRows - 1][j] =
+ (distanceSign)*polytope.planes[j].w;
+ }
+ problemTableau[numberRows - 1][polytope.planes.length] = 1.0;
+ problemTableau[numberRows - 1][polytope.planes.length+1] = -1.0;
+ for(int j = 0; j < coordinates.length; j++) {
+ problemTableau[numberRows - 1][polytope.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;
+ }
+ if (dist != null) {
+ computeMinDistance(coordinates,
+ polytope.getCenter(),
+ null,
+ dist, iPnt);
+ }
+ 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
+ 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]);
+ }
+
+
+
+ boolean edgeIntersectSphere(BoundingSphere sphere, Point3d start,
+ Point3d end)
+ {
+ double abLenSq, acLenSq, apLenSq, abDotAp, radiusSq;
+ Vector3d ab = getVector3d();
+ Vector3d ap = getVector3d();
+
+ ab.x = end.x - start.x;
+ ab.y = end.y - start.y;
+ ab.z = end.z - start.z;
+
+ ap.x = sphere.center.x - start.x;
+ ap.y = sphere.center.y - start.y;
+ ap.z = sphere.center.z - start.z;
+
+ abDotAp = ab.dot(ap);
+
+ if(abDotAp < 0.0) {
+ freeVector3d(ab);
+ freeVector3d(ap);
+ return false; // line segment points away from sphere.
+ }
+
+ abLenSq = ab.lengthSquared();
+ acLenSq = abDotAp * abDotAp / abLenSq;
+
+ if(acLenSq < abLenSq) {
+ freeVector3d(ab);
+ freeVector3d(ap);
+ return false; // C doesn't lies between end points of edge.
+ }
+
+ radiusSq = sphere.radius * sphere.radius;
+ apLenSq = ap.lengthSquared();
+
+ if((apLenSq - acLenSq) <= radiusSq) {
+ freeVector3d(ab);
+ freeVector3d(ap);
+ return true;
+ }
+
+ freeVector3d(ab);
+ freeVector3d(ap);
+ return false;
+
+ }
+
+
+ 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.
+ 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;
+
+ }
+
+
+ boolean edgeIntersectPlane(Vector3d normal, Point3d pnt, Point3d start,
+ Point3d end, Point3d iPnt)
+ {
+
+ Vector3d tempV3d = getVector3d();
+ Vector3d direction = getVector3d();
+ 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.");
+ freeVector3d(tempV3d);
+ freeVector3d(direction);
+ 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.");
+ freeVector3d(tempV3d);
+ freeVector3d(direction);
+ return false;
+ }
+
+ iPnt.x = start.x + tr * direction.x;
+ iPnt.y = start.y + tr * direction.y;
+ iPnt.z = start.z + tr * direction.z;
+
+ freeVector3d(tempV3d);
+ freeVector3d(direction);
+ return true;
+
+ }
+
+ // Assume coord is CCW.
+ 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;
+
+ }
+
+
+ // New stuffs .....
+ double getCompValue(Point3d v, int i) {
+ switch (i) {
+ case 0: return v.x;
+ case 1: return v.y;
+ }
+ // Has to return something, so set the default to z component.
+ return v.z;
+ }
+
+ double getCompValue(Point3d v0, Point3d v1, int i) {
+ switch (i) {
+ case 0: return (v0.x - v1.x);
+ case 1: return (v0.y - v1.y);
+ }
+ // Has to return some, so set the default to z component.
+ return (v0.z - v1.z);
+ }
+
+
+ boolean pointInTri(Point3d v0, Point3d u0, Point3d u1, Point3d u2,
+ Vector3d normal) {
+
+ double nAbsX, nAbsY, nAbsZ;
+ int i0, i1;
+
+ // first project onto an axis-aligned plane, that maximizes the area
+ // of the triangles, compute indices i0, i1.
+ nAbsX = Math.abs(normal.x);
+ nAbsY = Math.abs(normal.y);
+ nAbsZ = Math.abs(normal.z);
+
+ if (nAbsX > nAbsY) {
+ if(nAbsX > nAbsZ) {
+ i0 = 1; // nAbsX is greatest.
+ i1 = 2;
+ }
+ else {
+ i0 = 0; // nAbsZ is greatest.
+ i1 = 1;
+ }
+ } else { // nAbsX <= nAbsY
+ if(nAbsZ > nAbsY) {
+ i0 = 0; // nAbsZ is greatest.
+ i1 = 1;
+ }
+ else {
+ i0 = 0; // nAbsY is greatest.
+ i1 = 2;
+ }
+ }
+ return pointInTri(v0, u0, u1, u2, i0, i1);
+ }
+
+ boolean pointInTri(Point3d v0, Point3d u0, Point3d u1, Point3d u2,
+ int i0, int i1) {
+
+ double a, b, c, d0, d1, d2;
+ // is T1 completely inside T2 ?
+ // check if v0 is inside tri(u0,u1,u2)
+
+ a = getCompValue(u1, u0, i1);
+ b = -(getCompValue(u1, u0, i0));
+ c = -a * getCompValue(u0, i0) - b * getCompValue(u0, i1);
+ d0 = a * getCompValue(v0, i0) + b * getCompValue(v0, i1) + c;
+
+ a = getCompValue(u2, u1, i1);
+ b = -(getCompValue(u2, u1, i0));
+ c = -a * getCompValue(u1, i0) - b * getCompValue(u1, i1);
+ d1 = a * getCompValue(v0, i0) + b * getCompValue(v0, i1) + c;
+
+ a = getCompValue(u0, u2, i1);
+ b = -(getCompValue(u0, u2, i0));
+ c = -a * getCompValue(u2, i0) - b * getCompValue(u2, i1);
+ d2 = a * getCompValue(v0, i0) + b * getCompValue(v0, i1) + c;
+
+ if(d0*d1>0.0) {
+ if(d0*d2>0.0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ // this edge to edge test is based on Franlin Antonio's gem:
+ // "Faster line segment intersection", in Graphics Gems III, pp 199-202
+ boolean edgeAgainstEdge(Point3d v0, Point3d u0, Point3d u1, double aX, double aY,
+ int i0, int i1) {
+ double bX, bY, cX, cY, e, d, f;
+
+ bX = getCompValue(u0, u1,i0);
+ bY = getCompValue(u0, u1, i1);
+ cX = getCompValue(v0, u0, i0);
+ cY = getCompValue(v0, u0, i1);
+
+ f = aY * bX - aX * bY;
+ d = bY * cX - bX * cY;
+ if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) {
+ e = aX * cY - aY * cX;
+ if(f>0) {
+ if(e>=0 && e<=f)
+ return true;
+ }
+ else {
+ if(e<=0 && e>=f)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ boolean edgeAgainstTriEdges(Point3d v0, Point3d v1, Point3d u0,
+ Point3d u1, Point3d u2, int i0, int i1) {
+ double aX, aY;
+
+ // aX = v1[i0] - v0[i0];
+ // aY = v1[i1] - v0[i1];
+ aX = getCompValue(v1, v0, i0);
+ aY = getCompValue(v1, v0, i1);
+
+ // test edge u0, u1 against v0, v1
+ if(edgeAgainstEdge(v0, u0, u1, aX, aY, i0, i1))
+ return true;
+ // test edge u1, u2 against v0, v1
+ if(edgeAgainstEdge(v0, u1, u2, aX, aY, i0, i1))
+ return true;
+ // test edge u2, u0 against v0, v1
+ if(edgeAgainstEdge(v0, u2, u0, aX, aY, i0, i1))
+ return true;
+
+ return false;
+
+ }
+
+ boolean coplanarTriTri(Vector3d normal, Point3d v0, Point3d v1, Point3d v2,
+ Point3d u0, Point3d u1, Point3d u2) {
+
+ double nAbsX, nAbsY, nAbsZ;
+ int i0, i1;
+
+ // first project onto an axis-aligned plane, that maximizes the area
+ // of the triangles, compute indices i0, i1.
+ nAbsX = Math.abs(normal.x);
+ nAbsY = Math.abs(normal.y);
+ nAbsZ = Math.abs(normal.z);
+
+ if(nAbsX > nAbsY) {
+ if(nAbsX > nAbsZ) {
+ i0 = 1; // nAbsX is greatest.
+ i1 = 2;
+ }
+ else {
+ i0 = 0; // nAbsZ is greatest.
+ i1 = 1;
+ }
+ }
+ else { // nAbsX <= nAbsY
+ if(nAbsZ > nAbsY) {
+ i0 = 0; // nAbsZ is greatest.
+ i1 = 1;
+ }
+ else {
+ i0 = 0; // nAbsY is greatest.
+ i1 = 2;
+ }
+ }
+
+ // test all edges of triangle 1 against the edges of triangle 2
+ if(edgeAgainstTriEdges(v0, v1, u0, u1, u2, i0, i1))
+ return true;
+
+ if(edgeAgainstTriEdges(v1, v2, u0, u1, u2, i0, i1))
+ return true;
+
+ if(edgeAgainstTriEdges(v2, v0, u0, u1, u2, i0, i1))
+ return true;
+
+ // finally, test if tri1 is totally contained in tri2 or vice versa.
+ if(pointInTri(v0, u0, u1, u2, i0, i1))
+ return true;
+
+ if(pointInTri(u0, v0, v1, v2, i0, i1))
+ return true;
+
+ return false;
+ }
+
+
+
+
+
+ boolean intersectTriPnt(Point3d v0, Point3d v1, Point3d v2, Point3d u) {
+
+ Vector3d e1 = getVector3d();
+ Vector3d e2 = getVector3d();
+ Vector3d n1 = getVector3d();
+ Vector3d tempV3d = getVector3d();
+
+ double d1, du;
+
+ // compute plane equation of triange(coord1)
+ e1.x = v1.x - v0.x;
+ e1.y = v1.y - v0.y;
+ e1.z = v1.z - v0.z;
+
+ e2.x = v2.x - v0.x;
+ e2.y = v2.y - v0.y;
+ e2.z = v2.z - v0.z;
+
+ n1.cross(e1,e2);
+
+ if(n1.length() == 0.0) {
+ // System.out.println("(1) Degenerate triangle.");
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(tempV3d);
+ return false; // Degenerate triangle.
+ }
+
+ tempV3d.set((Tuple3d) v0);
+ d1 = - n1.dot(tempV3d); // plane equation 1: n1.x + d1 = 0
+
+ // put u to compute signed distance to the plane.
+ tempV3d.set((Tuple3d) u);
+ du = n1.dot(tempV3d) + d1;
+
+ // coplanarity robustness check
+ if(Math.abs(du)<EPS) du = 0.0;
+
+ // no intersection occurs
+ if(du != 0.0) {
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(tempV3d);
+ return false;
+ }
+
+ double nAbsX, nAbsY, nAbsZ;
+ int i0, i1;
+
+ // first project onto an axis-aligned plane, that maximizes the area
+ // of the triangles, compute indices i0, i1.
+ nAbsX = Math.abs(n1.x);
+ nAbsY = Math.abs(n1.y);
+ nAbsZ = Math.abs(n1.z);
+
+ if(nAbsX > nAbsY) {
+ if(nAbsX > nAbsZ) {
+ i0 = 1; // nAbsX is greatest.
+ i1 = 2;
+ }
+ else {
+ i0 = 0; // nAbsZ is greatest.
+ i1 = 1;
+ }
+ }
+ else { // nAbsX <= nAbsY
+ if(nAbsZ > nAbsY) {
+ i0 = 0; // nAbsZ is greatest.
+ i1 = 1;
+ }
+ else {
+ i0 = 0; // nAbsY is greatest.
+ i1 = 2;
+ }
+ }
+
+
+ // finally, test if u is totally contained in tri.
+ if(pointInTri(u, v0, v1, v2, i0, i1)) {
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(tempV3d);
+ return true;
+ }
+
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(tempV3d);
+ return false;
+ }
+
+
+ /**
+ * Return flag indicating whether two triangles intersect. This
+ * uses Tomas Moller's code for fast triangle-triangle
+ * intersection from his "Real-Time Rendering" book.
+ *
+ * The code is now divisionless. It tests for separating by planes
+ * parallel to either triangle. If neither separate the
+ * triangles, then two cases are considered. First case is if the
+ * normals to the triangles are parallel. In that case, the
+ * triangles are coplanar and a sequence of tests are made to see
+ * if edges of each triangle intersect the other triangle. If the
+ * normals are not parallel, then the two triangles can intersect
+ * only on the line of intersection of the two planes. The
+ * intervals of intersection of the triangles with the line of
+ * intersection of the two planes are computed and tested for
+ * overlap.
+ */
+ boolean intersectTriTri(Point3d v0, Point3d v1, Point3d v2,
+ Point3d u0, Point3d u1, Point3d u2) {
+
+ // System.out.println("In intersectTriTri ...");
+ Vector3d e1 = getVector3d();
+ Vector3d e2 = getVector3d();
+ Vector3d n1 = getVector3d();
+ Vector3d n2 = getVector3d();
+ Vector3d tempV3d = getVector3d();
+
+ double d1, d2;
+ double du0, du1, du2, dv0, dv1, dv2;
+ double du0du1, du0du2, dv0dv1, dv0dv2;
+ int index;
+ double vp0=0.0, vp1=0.0, vp2=0.0;
+ double up0=0.0, up1=0.0, up2=0.0;
+ double bb, cc, max;
+
+ // compute plane equation of triange(coord1)
+ e1.x = v1.x - v0.x;
+ e1.y = v1.y - v0.y;
+ e1.z = v1.z - v0.z;
+
+ e2.x = v2.x - v0.x;
+ e2.y = v2.y - v0.y;
+ e2.z = v2.z - v0.z;
+
+ n1.cross(e1,e2);
+
+ if(n1.length() == 0.0) {
+ // System.out.println("(1) Degenerate triangle.");
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return false; // Degenerate triangle.
+ }
+
+ tempV3d.set((Tuple3d) v0);
+ d1 = - n1.dot(tempV3d); // plane equation 1: n1.x + d1 = 0
+
+ // put u0, u1, and u2 into plane equation 1
+ // to compute signed distance to the plane.
+ tempV3d.set((Tuple3d) u0);
+ du0 = n1.dot(tempV3d) + d1;
+ tempV3d.set((Tuple3d) u1);
+ du1 = n1.dot(tempV3d) + d1;
+ tempV3d.set((Tuple3d) u2);
+ du2 = n1.dot(tempV3d) + d1;
+
+ // coplanarity robustness check
+ if(Math.abs(du0)<EPS) du0 = 0.0;
+ if(Math.abs(du1)<EPS) du1 = 0.0;
+ if(Math.abs(du2)<EPS) du2 = 0.0;
+
+ du0du1 = du0 * du1;
+ du0du2 = du0 * du2;
+
+ // same sign on all of them + not equal 0 ?
+ // no intersection occurs
+ if(du0du1>0.0 && du0du2>0.0) {
+ // System.out.println("In intersectTriTri : du0du1>0.0 && du0du2>0.0");
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return false;
+ }
+
+ // compute plane of triangle(coord2)
+ e1.x = u1.x - u0.x;
+ e1.y = u1.y - u0.y;
+ e1.z = u1.z - u0.z;
+
+ e2.x = u2.x - u0.x;
+ e2.y = u2.y - u0.y;
+ e2.z = u2.z - u0.z;
+
+ n2.cross(e1,e2);
+
+ if(n2.length() == 0.0) {
+ // System.out.println("(2) Degenerate triangle.");
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return false; // Degenerate triangle.
+ }
+
+ tempV3d.set((Tuple3d) u0);
+ d2 = - n2.dot(tempV3d); // plane equation 2: n2.x + d2 = 0
+
+ // put v0, v1, and v2 into plane equation 2
+ // to compute signed distance to the plane.
+ tempV3d.set((Tuple3d) v0);
+ dv0 = n2.dot(tempV3d) + d2;
+ tempV3d.set((Tuple3d) v1);
+ dv1 = n2.dot(tempV3d) + d2;
+ tempV3d.set((Tuple3d) v2);
+ dv2 = n2.dot(tempV3d) + d2;
+
+ // coplanarity robustness check
+ if(Math.abs(dv0)<EPS) dv0 = 0.0;
+ if(Math.abs(dv1)<EPS) dv1 = 0.0;
+ if(Math.abs(dv2)<EPS) dv2 = 0.0;
+
+ dv0dv1 = dv0 * dv1;
+ dv0dv2 = dv0 * dv2;
+
+ // same sign on all of them + not equal 0 ?
+ // no intersection occurs
+ if(dv0dv1>0.0 && dv0dv2>0.0) {
+ // System.out.println("In intersectTriTri : dv0dv1>0.0 && dv0dv2>0.0");
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return false;
+ }
+ // compute direction of intersection line.
+ tempV3d.cross(n1, n2);
+
+ // compute and index to the largest component of tempV3d.
+ max = Math.abs(tempV3d.x);
+ index = 0;
+ bb = Math.abs(tempV3d.y);
+ cc = Math.abs(tempV3d.z);
+ if(bb>max) {
+ max=bb;
+ index=1;
+ }
+ if(cc>max) {
+ max=cc;
+ index=2;
+ }
+
+ // this is the simplified projection onto L.
+
+ switch (index) {
+ case 0:
+ vp0 = v0.x;
+ vp1 = v1.x;
+ vp2 = v2.x;
+
+ up0 = u0.x;
+ up1 = u1.x;
+ up2 = u2.x;
+ break;
+ case 1:
+ vp0 = v0.y;
+ vp1 = v1.y;
+ vp2 = v2.y;
+
+ up0 = u0.y;
+ up1 = u1.y;
+ up2 = u2.y;
+ break;
+ case 2:
+ vp0 = v0.z;
+ vp1 = v1.z;
+ vp2 = v2.z;
+
+ up0 = u0.z;
+ up1 = u1.z;
+ up2 = u2.z;
+ break;
+ }
+
+ // compute intereval for triangle 1.
+ double a=0.0, b=0.0, c=0.0, x0=0.0, x1=0.0;
+ if(dv0dv1>0.0) {
+ // here we know that dv0dv2 <= 0.0 that is dv0 and dv1 are on the same side,
+ // dv2 on the other side or on the plane.
+ a = vp2; b = (vp0 - vp2) * dv2; c = (vp1 - vp2) * dv2;
+ x0 = dv2 - dv0; x1 = dv2 - dv1;
+ }
+ else if(dv0dv2>0.0) {
+ // here we know that dv0dv1<=0.0
+ a = vp1; b = (vp0 - vp1) * dv1; c = (vp2 - vp1) * dv1;
+ x0 = dv1 - dv0; x1 = dv1 - dv2;
+ }
+ else if((dv1*dv2>0.0) || (dv0 != 0.0)) {
+ // here we know that dv0vd1<=0.0 or that dv0!=0.0
+ a = vp0; b = (vp1 - vp0) * dv0; c = (vp2 - vp0) * dv0;
+ x0 = dv0 - dv1; x1 = dv0 - dv2;
+ }
+ else if(dv1 != 0.0) {
+ a = vp1; b = (vp0 - vp1) * dv1; c = (vp2 - vp1) * dv1;
+ x0 = dv1 - dv0; x1 = dv1 - dv2;
+ }
+ else if(dv2 != 0.0) {
+ a = vp2; b = (vp0 - vp2) * dv2; c = (vp1 - vp2) * dv2;
+ x0 = dv2 - dv0; x1 = dv2 - dv1;
+ }
+ else {
+ // triangles are coplanar
+ boolean toreturn = coplanarTriTri(n1, v0, v1, v2, u0, u1, u2);
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return toreturn;
+ }
+
+
+ // compute intereval for triangle 2.
+ double d=0.0, e=0.0, f=0.0, y0=0.0, y1=0.0;
+ if(du0du1>0.0) {
+ // here we know that du0du2 <= 0.0 that is du0 and du1 are on the same side,
+ // du2 on the other side or on the plane.
+ d = up2; e = (up0 - up2) * du2; f = (up1 - up2) * du2;
+ y0 = du2 - du0; y1 = du2 - du1;
+ }
+ else if(du0du2>0.0) {
+ // here we know that du0du1<=0.0
+ d = up1; e = (up0 - up1) * du1; f = (up2 - up1) * du1;
+ y0 = du1 - du0; y1 = du1 - du2;
+ }
+ else if((du1*du2>0.0) || (du0 != 0.0)) {
+ // here we know that du0du1<=0.0 or that D0!=0.0
+ d = up0; e = (up1 - up0) * du0; f = (up2 - up0) * du0;
+ y0 = du0 - du1; y1 = du0 - du2;
+ }
+ else if(du1 != 0.0) {
+ d = up1; e = (up0 - up1) * du1; f = (up2 - up1) * du1;
+ y0 = du1 - du0; y1 = du1 - du2;
+ }
+ else if(du2 != 0.0) {
+ d = up2; e = (up0 - up2) * du2; f = (up1 - up2) * du2;
+ y0 = du2 - du0; y1 = du2 - du1;
+ }
+ else {
+ // triangles are coplanar
+ // System.out.println("In intersectTriTri : coplanarTriTri test 2");
+ boolean toreturn = coplanarTriTri(n2, v0, v1, v2, u0, u1, u2);
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return toreturn;
+ }
+
+ double xx, yy, xxyy, tmp, isect1S, isect1E, isect2S, isect2E;
+ xx = x0 * x1;
+ yy = y0 * y1;
+ xxyy = xx * yy;
+
+ tmp = a * xxyy;
+ isect1S = tmp + b * x1 * yy;
+ isect1E = tmp + c * x0 * yy;
+
+ tmp = d * xxyy;
+ isect2S = tmp + e * y1 * xx;
+ isect2E = tmp + f * y0 * xx;
+
+ // sort so that isect1S <= isect1E
+ if(isect1S > isect1E) {
+ tmp = isect1S;
+ isect1S = isect1E;
+ isect1E = tmp;
+ }
+
+ // sort so that isect2S <= isect2E
+ if(isect2S > isect2E) {
+ tmp = isect2S;
+ isect2S = isect2E;
+ isect2E = tmp;
+ }
+
+ if(isect1E<isect2S || isect2E<isect1S) {
+ // System.out.println("In intersectTriTri :isect1E<isect2S || isect2E<isect1S");
+ // System.out.println("In intersectTriTri : return false");
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return false;
+ }
+
+ // System.out.println("In intersectTriTri : return true");
+ freeVector3d(e1);
+ freeVector3d(e2);
+ freeVector3d(n1);
+ freeVector3d(n2);
+ freeVector3d(tempV3d);
+ return true;
+
+ }
+
+
+
+ boolean intersectPolygon(Point3d coord1[], Point3d coord2[]) {
+ int i, j;
+ Vector3d vec0 = getVector3d(); // Edge vector from point 0 to point 1;
+ Vector3d vec1 = getVector3d(); // Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = getVector3d();
+ 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) Degenerate polygon.");
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ return false; // Degenerate 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) Degenerate polygon.");
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ return false; // Degenerate polygon.
+ }
+
+ j = 0;
+ Point3d seg[] = new Point3d[2];
+ seg[0] = getPoint3d();
+ seg[1] = getPoint3d();
+
+ 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) {
+ if(++j>1) {
+ break;
+ }
+ }
+ }
+
+ if (j==0) {
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freePoint3d(seg[0]);
+ freePoint3d(seg[1]);
+ return false;
+ }
+
+ if (coord2.length < 3) {
+ boolean toreturn = pointIntersectPolygon2D(pNrm, coord1, seg[0]);
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freePoint3d(seg[0]);
+ freePoint3d(seg[1]);
+ return toreturn;
+ } else {
+ boolean toreturn = edgeIntersectPolygon2D(pNrm, coord1, seg);
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freePoint3d(seg[0]);
+ freePoint3d(seg[1]);
+ return toreturn;
+ }
+ }
+
+
+ /**
+ * Return true if triangle or quad intersects with ray and the
+ * distance is stored in dist[0] and the intersect point in iPnt
+ * (if iPnt is not null).
+ */
+ boolean intersectRay(Point3d coordinates[], PickRay ray, double dist[],
+ Point3d iPnt) {
+
+ return intersectRayOrSegment(coordinates, ray.direction, ray.origin,
+ dist, iPnt, false);
+
+ }
+
+ /**
+ * Return true if triangle or quad intersects with segment and
+ * the distance is stored in dist[0].
+ */
+ boolean intersectSegment( Point3d coordinates[], Point3d start, Point3d end,
+ double dist[], Point3d iPnt ) {
+ boolean result;
+ Vector3d direction = getVector3d();
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+ result = intersectRayOrSegment(coordinates, direction, start, dist, iPnt, true);
+ freeVector3d(direction);
+ return result;
+ }
+
+
+
+ /**
+ * Return true if triangle or quad intersects with ray and the distance is
+ * stored in pr.
+ */
+ boolean intersectRayOrSegment(Point3d coordinates[],
+ Vector3d direction, Point3d origin,
+ double dist[], Point3d iPnt, boolean isSegment) {
+ Vector3d vec0, vec1, pNrm, tempV3d;
+ 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)) {
+ // degenerate 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,
+ dist,
+ iPnt);
+
+ // 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,
+ dist,
+ iPnt)) {
+ 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
+
+ dist[0] = (pD - pNrm.dot(tempV3d))/ pNrmDotrDir;
+
+ // Ray intersects the plane behind the ray's origin.
+ if ((dist[0] < -EPS ) ||
+ (isSegment && (dist[0] > 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.
+ if (iPnt == null) {
+ iPnt = new Point3d();
+ }
+ iPnt.x = origin.x + direction.x * dist[0];
+ iPnt.y = origin.y + direction.y * dist[0];
+ iPnt.z = origin.z + direction.z * dist[0];
+
+ // 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);
+
+ // 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 {
+ // Ignore degenerate line=>point happen when Quad => Triangle.
+ // Note that by next round sign*lastSign = 0 so it will
+ // not pass the interest test. This should only happen once in the
+ // loop because we already check for degenerate geometry before.
+ lastSign = 0;
+ }
+ }
+ }
+ }
+ } 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) {
+ dist[0] *= direction.length();
+ }
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(tempV3d);
+ return isIntersect;
+ }
+
+
+
+ static final boolean isNonZero(double v) {
+ return ((v > EPS) || (v < -EPS));
+
+ }
+
+ /**
+ * Return true if point is on the inside of halfspace test. The
+ * halfspace is partition by the plane of triangle or quad.
+ */
+ 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;
+
+ // 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) Degenerate polygon.");
+ return false; // Degenerate 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]);
+ */
+
+ if( ccw == 0x1)
+ pNrm.cross(vec0,vec1);
+ else
+ pNrm.cross(vec1,vec0);
+
+ if(pNrm.length() == 0.0) {
+ // System.out.println("(2) Degenerate polygon.");
+ return false; // Degenerate polygon.
+ }
+ // Compute plane D.
+ tempV3d.set((Tuple3d) coordinates[0]);
+ pD = pNrm.dot(tempV3d);
+ tempV3d.set((Tuple3d) point.location);
+
+ return ((pD - pNrm.dot(tempV3d)) <= 0);
+ }
+
+ boolean intersectPntAndPnt( Point3d pnt1, Point3d pnt2 ) {
+ return ((pnt1.x == pnt2.x) &&
+ (pnt1.y == pnt2.y) &&
+ (pnt1.z == pnt2.z));
+ }
+
+ boolean intersectPntAndRay(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 - EPS)) || (pnt.y > (temp + EPS)))
+ return false;
+ }
+
+ if(flag < 2) {
+ temp = ori.z + dist[0] * dir.z;
+ if((pnt.z < (temp - EPS)) || (pnt.z > (temp + EPS)))
+ return false;
+ }
+
+ return true;
+
+ }
+
+ boolean intersectLineAndRay(Point3d start, Point3d end,
+ Point3d ori, Vector3d dir, double dist[],
+ Point3d iPnt) {
+
+ double m00, m01, m10, m11;
+ double mInv00, mInv01, mInv10, mInv11;
+ double dmt, t, s, tmp1, tmp2;
+ Vector3d lDir;
+
+ // System.out.println("GeometryArrayRetained : intersectLineAndRay");
+ // System.out.println("start " + start + " end " + end );
+ // System.out.println("ori " + ori + " dir " + dir);
+
+ lDir = getVector3d();
+ lDir.x = (end.x - start.x);
+ lDir.y = (end.y - start.y);
+ lDir.z = (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, check degenerate line
+ boolean isIntersect = false;
+ if ((lDir.x == 0) && (lDir.y == 0) && (lDir.z == 0)) {
+ isIntersect = intersectPntAndRay(start, ori, dir, dist);
+ if (isIntersect && (iPnt != null)) {
+ iPnt.set(start);
+ }
+ }
+ 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);
+ freeVector3d(lDir);
+ 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);
+ freeVector3d(lDir);
+ return false;
+ }
+
+ tmp1 = ori.z + s * dir.z;
+ tmp2 = start.z + t * lDir.z;
+
+ if((tmp1 < (tmp2 - EPS)) || (tmp1 > (tmp2 + EPS))) {
+ // System.out.println("No intersection : tmp1 " + tmp1 + " tmp2 " + tmp2);
+ freeVector3d(lDir);
+ return false;
+ }
+
+ dist[0] = s;
+
+ if (iPnt != null) {
+ // compute point of intersection.
+ iPnt.x = ori.x + dir.x * dist[0];
+ iPnt.y = ori.y + dir.y * dist[0];
+ iPnt.z = ori.z + dir.z * dist[0];
+ }
+
+ // System.out.println("Intersected : tmp1 " + tmp1 + " tmp2 " + tmp2);
+ freeVector3d(lDir);
+ return true;
+ }
+
+ /**
+ Return true if triangle or quad intersects with cylinder. The
+ distance is stored in dist.
+ */
+ boolean intersectCylinder(Point3d coordinates[], PickCylinder cyl,
+ double dist[], Point3d iPnt) {
+
+ Point3d origin = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ Point3d iPnt1 = getPoint3d();
+ Vector3d originToIpnt = getVector3d();
+
+ if (iPnt == null) {
+ iPnt = new Point3d();
+ }
+
+ // 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),
+ dist, iPnt)) {
+
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freePoint3d(iPnt1);
+
+ return true;
+ }
+ }
+ else {
+ if (intersectSegment(coordinates, origin, end, dist, iPnt)) {
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freePoint3d(iPnt1);
+ return true;
+ }
+ }
+ }
+
+ // Ray doesn't intersect, check distance to edges
+ double sqDistToEdge;
+ int j;
+ for (int i=0; i<coordinates.length;i++) {
+ j = (i < coordinates.length-1 ? i+1: 0);
+ if (cyl instanceof PickCylinderSegment) {
+ sqDistToEdge =
+ Distance.segmentToSegment(origin, end,
+ coordinates[i], coordinates[j],
+ iPnt1, iPnt, null);
+ }
+ else {
+ sqDistToEdge =
+ Distance.rayToSegment(origin, direction,
+ coordinates[i], coordinates[j],
+ iPnt1, iPnt, null);
+ }
+ if (sqDistToEdge <= radius*radius) {
+ originToIpnt.sub (iPnt1, origin);
+ dist[0] = originToIpnt.length();
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freePoint3d(iPnt1);
+ return true;
+ }
+ }
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freePoint3d(iPnt1);
+ return false;
+ }
+
+ /**
+ Return true if triangle or quad intersects with cone. The
+ distance is stored in dist.
+ */
+ boolean intersectCone(Point3d coordinates[], PickCone cone,
+ double[] dist, Point3d iPnt) {
+
+ Point3d origin = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ Vector3d originToIpnt = getVector3d();
+ double distance;
+
+ Point3d iPnt1 = getPoint3d();
+ Vector3d vector = getVector3d();
+
+ if (iPnt == null) {
+ iPnt = new Point3d();
+ }
+ // 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),
+ dist, iPnt)) {
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+ return true;
+ }
+ }
+ else {
+ if (intersectSegment(coordinates, origin, end, dist, iPnt)) {
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+ return true;
+ }
+ }
+ }
+
+ // Ray doesn't intersect, check distance to edges
+ double sqDistToEdge;
+ int j = 0;
+ for (int i=0; i<coordinates.length;i++) {
+ j = (i < coordinates.length-1 ? i+1: 0);
+ if (cone instanceof PickConeSegment) {
+ sqDistToEdge =
+ Distance.segmentToSegment (origin, end,
+ coordinates[i], coordinates[j],
+ iPnt1, iPnt, null);
+ }
+ else {
+ sqDistToEdge =
+ Distance.rayToSegment (origin, direction,
+ coordinates[i], coordinates[j],
+ iPnt1, iPnt, 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");
+ dist[0] = distance;
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+ return true;
+ }
+ }
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+ return false;
+ }
+
+
+ /**
+ Return true if point intersects with cylinder and the distance is
+ stored in dist.
+ */
+ boolean intersectCylinder(Point3d pt, PickCylinder cyl,
+ double[] dist) {
+
+ 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) {
+ originToIpnt.sub (iPnt, origin);
+ dist[0] = originToIpnt.length();
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt);
+ freeVector3d(originToIpnt);
+ freeVector3d(direction);
+ return true;
+ }
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt);
+ freeVector3d(originToIpnt);
+ freeVector3d(direction);
+ return false;
+ }
+
+ /**
+ Return true if point intersects with cone and the
+ distance is stored in pi.
+ */
+ boolean intersectCone(Point3d pt, PickCone cone, double[] dist)
+ {
+ Point3d origin = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ Point3d iPnt = getPoint3d();
+ Vector3d originToIpnt = getVector3d();
+
+ // Get cone information
+ cone.getOrigin (origin);
+ cone.getDirection (direction);
+ double radius;
+ double distance;
+ double sqDist;
+
+ if (iPnt == null) {
+ iPnt = new Point3d();
+ }
+
+ 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) {
+ dist[0] = distance;
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ return true;
+ }
+ return false;
+ }
+
+
+ void setCoordRefBuffer(J3DBuffer coords) {
+ if (coords != null) {
+ switch (coords.getBufferType()) {
+ case J3DBuffer.TYPE_FLOAT:
+ if ( !((FloatBufferWrapper)coords.getBufferImpl()).isDirect())
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+
+ // TODO: may need to check whether it is direct and if so,
+ // whether it is consistent with native byte order
+ break;
+ case J3DBuffer.TYPE_DOUBLE:
+ if ( !((DoubleBufferWrapper)coords.getBufferImpl()).isDirect())
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+
+ // TODO: may need to check whether it is direct and if so,
+ // whether it is consistent with native byte order
+ break;
+ case J3DBuffer.TYPE_NULL:
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray115"));
+
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+ if (3 * idx.maxCoordIndex >= coords.getBufferImpl().limit()) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ } else if (coords.getBufferImpl().limit() < (3*(initialCoordIndex+validVertexCount))) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ }
+
+ //throw new RuntimeException("method not implemeted");
+ // lock the geometry and start to do real work
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ coordRefBuffer = coords;
+ if(coords == null) {
+ floatBufferRefCoords = null;
+ doubleBufferRefCoords = null;
+ // TODO: if not mix java array with nio buffer
+ // vertexType can be used as vertexTypeBuffer
+ vertexType &= ~PD;
+ vertexType &= ~PF;
+ }else {
+ switch (coords.getBufferType()) {
+ case J3DBuffer.TYPE_FLOAT:
+ floatBufferRefCoords =
+ (FloatBufferWrapper)coords.getBufferImpl();
+ doubleBufferRefCoords = null;
+ vertexType |= PF;
+ vertexType &= ~PD;
+ break;
+ case J3DBuffer.TYPE_DOUBLE:
+ floatBufferRefCoords = null;
+ doubleBufferRefCoords =
+ (DoubleBufferWrapper)coords.getBufferImpl();
+ vertexType |= PD;
+ vertexType &= ~PF;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // need not call setupMirrorVertexPointer() since
+ // we are not going to set mirror in NIO buffer case
+ // TODO: if we need to mix java array with buffer,
+ // we may need to consider setupMirrorVertexPointer()
+
+ geomLock.unLock();
+
+ if (!inUpdater && source != null) {
+ if (source.isLive()) {
+ processCoordsChanged((coords == null));
+ sendDataChangedMessage(true);
+ } else {
+ boundsDirty = true;
+ }
+ }
+
+ }
+
+
+ J3DBuffer getCoordRefBuffer() {
+ return coordRefBuffer;
+ }
+
+
+ void setCoordRefFloat(float[] coords) {
+
+ // If non-null coordinate and vertType is either defined
+ // to be something other than PF, then issue an error
+ if (coords != null) {
+ if ((vertexType & VERTEX_DEFINED) != 0 &&
+ (vertexType & VERTEX_DEFINED) != PF) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (3 * idx.maxCoordIndex >= coords.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ } else if (coords.length < 3 * (initialCoordIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+
+ floatRefCoords = coords;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (coords == null)
+ vertexType &= ~PF;
+ else
+ vertexType |= PF;
+ }
+ else {
+ setupMirrorVertexPointer(PF);
+ }
+
+ geomLock.unLock();
+
+ if (!inUpdater && source != null) {
+ if (source.isLive()) {
+ processCoordsChanged(coords == null);
+ sendDataChangedMessage(true);
+ } else {
+ boundsDirty = true;
+ }
+ }
+ }
+
+
+ float[] getCoordRefFloat() {
+ return floatRefCoords;
+ }
+
+
+ void setCoordRefDouble(double[] coords) {
+
+ if (coords != null) {
+ if ((vertexType & VERTEX_DEFINED) != 0 &&
+ (vertexType & VERTEX_DEFINED) != PD) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+ if (3 * idx.maxCoordIndex >= coords.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ } else if (coords.length < 3 * (initialCoordIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ }
+
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ doubleRefCoords = coords;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (coords == null)
+ vertexType &= ~PD;
+ else
+ vertexType |= PD;
+ }
+ else {
+ setupMirrorVertexPointer(PD);
+ }
+ geomLock.unLock();
+
+ if (!inUpdater && source != null) {
+ if (source.isLive()) {
+ processCoordsChanged(coords == null);
+ sendDataChangedMessage(true);
+ } else {
+ boundsDirty = true;
+ }
+ }
+ }
+
+ double[] getCoordRefDouble() {
+ return doubleRefCoords;
+ }
+
+ void setCoordRef3f(Point3f[] coords) {
+
+ if (coords != null) {
+ if ((vertexType & VERTEX_DEFINED) != 0 &&
+ (vertexType & VERTEX_DEFINED) != P3F) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (idx.maxCoordIndex >= coords.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ } else if (coords.length < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ p3fRefCoords = coords;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (coords == null)
+ vertexType &= ~P3F;
+ else
+ vertexType |= P3F;
+ }
+ else {
+ setupMirrorVertexPointer(P3F);
+ }
+ geomLock.unLock();
+
+ if (!inUpdater && source != null) {
+ if (source.isLive()) {
+ processCoordsChanged(coords == null);
+ sendDataChangedMessage(true);
+ } else {
+ boundsDirty = true;
+ }
+ }
+ }
+
+ Point3f[] getCoordRef3f() {
+ return p3fRefCoords;
+
+ }
+
+ void setCoordRef3d(Point3d[] coords) {
+
+ if (coords != null) {
+ if ((vertexType & VERTEX_DEFINED) != 0 &&
+ (vertexType & VERTEX_DEFINED) != P3D) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (idx.maxCoordIndex >= coords.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ } else if (coords.length < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ p3dRefCoords = coords;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (coords == null)
+ vertexType &= ~P3D;
+ else
+ vertexType |= P3D;
+ } else {
+ setupMirrorVertexPointer(P3D);
+ }
+ geomLock.unLock();
+
+ if (!inUpdater && source != null) {
+ if (source.isLive()) {
+ processCoordsChanged(coords == null);
+ sendDataChangedMessage(true);
+ } else {
+ boundsDirty = true;
+ }
+ }
+ }
+
+ Point3d[] getCoordRef3d() {
+ return p3dRefCoords;
+ }
+
+ void setColorRefFloat(float[] colors) {
+
+ if (colors != null) {
+ if ((vertexType & COLOR_DEFINED) != 0 &&
+ (vertexType & COLOR_DEFINED) != CF) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray123"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (getColorStride() * idx.maxColorIndex >= colors.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ } else if (colors.length < getColorStride() * (initialColorIndex+ validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ floatRefColors = colors;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (colors == null)
+ vertexType &= ~CF;
+ else
+ vertexType |= CF;
+ }
+ else {
+ setupMirrorColorPointer(CF, false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+
+ }
+
+ float[] getColorRefFloat() {
+ return floatRefColors;
+ }
+
+
+ // set the color with nio buffer
+ void setColorRefBuffer(J3DBuffer colors) {
+ if (colors != null) {
+ switch(colors.getBufferType()) {
+ case J3DBuffer.TYPE_FLOAT:
+ if ( !((FloatBufferWrapper)colors.getBufferImpl()).isDirect())
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+ break;
+ case J3DBuffer.TYPE_BYTE:
+ if ( !((ByteBufferWrapper)colors.getBufferImpl()).isDirect())
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+ break;
+ case J3DBuffer.TYPE_NULL:
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray115"));
+
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray123"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (getColorStride() * idx.maxColorIndex >= colors.getBufferImpl().limit()) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ } else if (colors.getBufferImpl().limit() <
+ getColorStride() * (initialColorIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ colorRefBuffer = colors;
+ if(colors == null) {
+ floatBufferRefColors = null;
+ byteBufferRefColors = null;
+ } else {
+ switch (colors.getBufferType()) {
+ case J3DBuffer.TYPE_FLOAT:
+ floatBufferRefColors = (FloatBufferWrapper)colors.getBufferImpl();
+ byteBufferRefColors = null;
+ break;
+
+ case J3DBuffer.TYPE_BYTE:
+ byteBufferRefColors = (ByteBufferWrapper)colors.getBufferImpl();
+ floatBufferRefColors = null;
+ break;
+ default:
+ break;
+ }
+ }
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if(colors == null) {
+ vertexType &= ~CF;
+ vertexType &= ~CUB;
+ } else {
+ switch (colors.getBufferType()) {
+ case J3DBuffer.TYPE_FLOAT:
+ vertexType |= CF;
+ vertexType &= ~CUB;
+ break;
+
+ case J3DBuffer.TYPE_BYTE:
+ vertexType |= CUB;
+ vertexType &= ~CF;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else {
+ setupMirrorColorPointer(CF|CUB, false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+ // return the color data in nio buffer format
+ J3DBuffer getColorRefBuffer() {
+ return colorRefBuffer;
+ }
+
+ void setColorRefByte(byte[] colors) {
+
+ if (colors != null) {
+ if ((vertexType & COLOR_DEFINED) != 0 &&
+ (vertexType & COLOR_DEFINED) != CUB) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray123"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (getColorStride() * idx.maxColorIndex >= colors.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ } else if (colors.length < getColorStride() * (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ byteRefColors = colors;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (colors == null)
+ vertexType &= ~CUB;
+ else
+ vertexType |= CUB;
+ }
+ else {
+ setupMirrorColorPointer(CUB, false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+
+ }
+
+ byte[] getColorRefByte() {
+ return byteRefColors;
+ }
+
+ void setColorRef3f(Color3f[] colors) {
+
+ if (colors != null) {
+ if ((vertexType & COLOR_DEFINED) != 0 &&
+ (vertexType & COLOR_DEFINED) != C3F) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR_3) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+ if (idx.maxColorIndex >= colors.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ } else if (colors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ c3fRefColors = colors;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (colors == null)
+ vertexType &= ~C3F;
+ else
+ vertexType |= C3F;
+ }
+ else {
+ setupMirrorColorPointer(C3F, false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+
+ }
+
+ Color3f[] getColorRef3f() {
+ return c3fRefColors;
+ }
+
+
+ void setColorRef4f(Color4f[] colors) {
+
+ if (colors != null) {
+ if ((vertexType & COLOR_DEFINED) != 0 &&
+ (vertexType & COLOR_DEFINED) != C4F) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+ if ((vertexFormat & GeometryArray.COLOR_4) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+ if (idx.maxColorIndex >= colors.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ } else if (colors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ c4fRefColors = colors;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (colors == null)
+ vertexType &= ~C4F;
+ else
+ vertexType |= C4F;
+ }
+ else {
+ setupMirrorColorPointer(C4F, false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+ Color4f[] getColorRef4f() {
+ return c4fRefColors;
+ }
+
+
+ void setColorRef3b(Color3b[] colors) {
+
+ if (colors != null) {
+
+ if ((vertexType & COLOR_DEFINED) != 0 &&
+ (vertexType & COLOR_DEFINED) != C3UB) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR_3) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (idx.maxColorIndex >= colors.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ } else if (colors.length < (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ c3bRefColors = colors;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (colors == null)
+ vertexType &= ~C3UB;
+ else
+ vertexType |= C3UB;
+ }
+ else {
+ setupMirrorColorPointer(C3UB, false);
+ }
+
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+
+ Color3b[] getColorRef3b() {
+ return c3bRefColors;
+ }
+
+ void setColorRef4b(Color4b[] colors) {
+
+ if (colors != null) {
+ if ((vertexType & COLOR_DEFINED) != 0 &&
+ (vertexType & COLOR_DEFINED) != C4UB) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR_4) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained) this;
+
+ if (idx.maxColorIndex >= colors.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ } else if (colors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ c4bRefColors = colors;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (colors == null)
+ vertexType &= ~C4UB;
+ else
+ vertexType |= C4UB;
+ }
+ else {
+ setupMirrorColorPointer(C4UB, false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+
+ Color4b[] getColorRef4b() {
+ return c4bRefColors;
+ }
+
+ void setNormalRefFloat(float[] normals) {
+
+ if (normals != null) {
+ if ((vertexType & NORMAL_DEFINED) != 0 &&
+ (vertexType & NORMAL_DEFINED) != NF) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.NORMALS) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray122"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (idx.maxNormalIndex*3 >= normals.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ } else if (normals.length < 3 * (initialNormalIndex + validVertexCount )) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= NORMAL_CHANGED;
+ floatRefNormals = normals;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (normals == null)
+ vertexType &= ~NF;
+ else
+ vertexType |= NF;
+ }
+ else {
+ setupMirrorNormalPointer(NF);
+ }
+ geomLock.unLock();
+
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+
+ }
+
+ float[] getNormalRefFloat() {
+ return floatRefNormals;
+ }
+
+ // setup the normal with nio buffer
+ void setNormalRefBuffer(J3DBuffer normals) {
+
+ FloatBufferWrapper bufferImpl = null;
+
+ if (normals != null) {
+ if(normals.getBufferType() != J3DBuffer.TYPE_FLOAT)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
+
+ bufferImpl = (FloatBufferWrapper)normals.getBufferImpl();
+
+ if ( ! bufferImpl.isDirect())
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+
+ if ((vertexFormat & GeometryArray.NORMALS) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray122"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+ if (idx.maxNormalIndex * 3 >=
+ ((FloatBufferWrapper)normals.getBufferImpl()).limit()) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ } else if (bufferImpl.limit() < 3 * (initialNormalIndex + validVertexCount )) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= NORMAL_CHANGED;
+ normalRefBuffer = normals;
+
+ if (normals == null) {
+ vertexType &= ~NF;
+ floatBufferRefNormals = null;
+ }
+ else {
+ vertexType |= NF;
+ floatBufferRefNormals = bufferImpl;
+ }
+ geomLock.unLock();
+
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+ J3DBuffer getNormalRefBuffer() {
+ return normalRefBuffer;
+ }
+
+ void setNormalRef3f(Vector3f[] normals) {
+
+ if (normals != null) {
+ if ((vertexType & NORMAL_DEFINED) != 0 &&
+ (vertexType & NORMAL_DEFINED) != N3F) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.NORMALS) == 0) {
+ throw new IllegalStateException(J3dI18N.getString("GeometryArray122"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+ if (idx.maxNormalIndex >= normals.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ } else if (normals.length < (initialNormalIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= NORMAL_CHANGED;
+ v3fRefNormals = normals;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (normals == null)
+ vertexType &= ~N3F;
+ else
+ vertexType |= N3F;
+ }
+ else {
+ setupMirrorNormalPointer(N3F);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+ Vector3f[] getNormalRef3f() {
+ return v3fRefNormals;
+ }
+
+ final int getColorStride() {
+ return ((vertexFormat & GeometryArray.WITH_ALPHA) != 0 ? 4 : 3);
+ }
+
+ final int getTexStride() {
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ return 2;
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ return 3;
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ return 4;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray121"));
+ }
+
+ void setTexCoordRefFloat(int texCoordSet, float[] texCoords) {
+
+ if (texCoords != null) {
+
+ if ((vertexType & TEXCOORD_DEFINED) != 0 &&
+ (vertexType & TEXCOORD_DEFINED) != TF) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("GeometryArray98"));
+ }
+
+ int ts = getTexStride();
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (idx.maxTexCoordIndices[texCoordSet]*ts >= texCoords.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ } else if (texCoords.length < ts*(initialTexCoordIndex[texCoordSet]+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+ refTexCoords[texCoordSet] = texCoords;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (texCoords == null)
+ vertexType &= ~TF;
+ else
+ vertexType |= TF;
+ }
+ else {
+ setupMirrorTexCoordPointer(texCoordSet, TF);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+
+
+ float[] getTexCoordRefFloat(int texCoordSet) {
+ return ((float[])refTexCoords[texCoordSet]);
+ }
+
+ // set the tex coord with nio buffer
+ void setTexCoordRefBuffer(int texCoordSet, J3DBuffer texCoords) {
+
+ FloatBufferWrapper bufferImpl = null;
+
+ if (texCoords != null) {
+ if(texCoords.getBufferType() != J3DBuffer.TYPE_FLOAT)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
+
+ bufferImpl = (FloatBufferWrapper)texCoords.getBufferImpl();
+ int bufferSize = bufferImpl.limit();
+
+ if ( ! bufferImpl.isDirect())
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+
+ int ts = getTexStride();
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+ if (idx.maxTexCoordIndices[texCoordSet] * ts >= bufferSize) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ } else if (bufferSize < ts*(initialTexCoordIndex[texCoordSet] + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+ // refTexCoordsBuffer contains J3DBuffer object for tex coord
+ refTexCoordsBuffer[texCoordSet] = texCoords;
+ if (texCoords == null) {
+ vertexType &= ~TF;
+ refTexCoords[texCoordSet] = null;
+ }
+ else {
+ vertexType |= TF;
+ // refTexCoords contains NIOBuffer object for tex coord
+ refTexCoords[texCoordSet] = bufferImpl.getBufferAsObject();
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+ J3DBuffer getTexCoordRefBuffer(int texCoordSet) {
+ return (J3DBuffer)(refTexCoordsBuffer[texCoordSet]);
+ }
+
+ void setTexCoordRef2f(int texCoordSet, TexCoord2f[] texCoords) {
+
+ if (texCoords != null) {
+ if ((vertexType & TEXCOORD_DEFINED) != 0 &&
+ (vertexType & TEXCOORD_DEFINED) != T2F) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) == 0) {
+ throw new IllegalStateException(
+ J3dI18N.getString("GeometryArray94"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (idx.maxTexCoordIndices[texCoordSet] >= texCoords.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ } else if (texCoords.length < (initialTexCoordIndex[texCoordSet] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
+ }
+
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+ refTexCoords[texCoordSet] = texCoords;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (texCoords == null)
+ vertexType &= ~T2F;
+ else
+ vertexType |= T2F;
+ }
+ else {
+ setupMirrorTexCoordPointer(T2F);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+
+ TexCoord2f[] getTexCoordRef2f(int texCoordSet) {
+ if (refTexCoords != null && refTexCoords[texCoordSet] != null &&
+ refTexCoords[texCoordSet] instanceof TexCoord2f[]) {
+ return ((TexCoord2f[])refTexCoords[texCoordSet]);
+ } else {
+ return null;
+ }
+ }
+
+
+ void setTexCoordRef3f(int texCoordSet, TexCoord3f[] texCoords) {
+
+ if (texCoords != null) {
+
+ if ((vertexType & TEXCOORD_DEFINED) != 0 &&
+ (vertexType & TEXCOORD_DEFINED) != T3F) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("GeometryArray98"));
+ }
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) == 0) {
+ throw new IllegalStateException(
+ J3dI18N.getString("GeometryArray95"));
+ }
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (idx.maxTexCoordIndices[texCoordSet] >= texCoords.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+
+ } else if (texCoords.length < (initialTexCoordIndex[texCoordSet] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
+ }
+
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+ refTexCoords[texCoordSet] = texCoords;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ if (texCoords == null)
+ vertexType &= ~T3F;
+ else
+ vertexType |= T3F;
+ }
+ else {
+ setupMirrorTexCoordPointer(T3F);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+
+
+ TexCoord3f[] getTexCoordRef3f(int texCoordSet) {
+ if (refTexCoords != null && refTexCoords[texCoordSet] != null &&
+ refTexCoords[texCoordSet] instanceof TexCoord3f[]) {
+ return ((TexCoord3f[])refTexCoords[texCoordSet]);
+ } else {
+ return null;
+ }
+ }
+
+
+ void setInterleavedVertices(float[] vertexData) {
+ if (vertexData != null) {
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (stride * idx.maxCoordIndex >= vertexData.length) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ if (stride * idx.maxTexCoordIndices[i] >= vertexData.length) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ }
+ }
+
+ if (((this.vertexFormat & GeometryArray.COLOR) != 0) &&
+ (stride * idx.maxColorIndex >= vertexData.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+
+ if (((this.vertexFormat & GeometryArray.NORMALS) != 0) &&
+ (stride * idx.maxNormalIndex >= vertexData.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ } else {
+ if (vertexData.length < (stride * (initialVertexIndex+validVertexCount)))
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
+ }
+ }
+
+ // If the geometry has been rendered transparent, then make a copy
+ // of the color pointer with 4f
+ geomLock.getLock();
+ dirtyFlag |= VERTEX_CHANGED;
+ colorChanged = 0xffff;
+ interLeavedVertexData = vertexData;
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ setupMirrorInterleavedColorPointer(false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ processCoordsChanged(vertexData == null);
+ sendDataChangedMessage(true);
+ }
+ }
+
+ // set the interleaved vertex with NIO buffer
+ void setInterleavedVertexBuffer(J3DBuffer vertexData) {
+
+ FloatBufferWrapper bufferImpl = null;
+
+ if (vertexData != null ){
+
+ if (vertexData.getBufferType() != J3DBuffer.TYPE_FLOAT)
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
+
+ bufferImpl = (FloatBufferWrapper)vertexData.getBufferImpl();
+
+ if (!bufferImpl.isDirect())
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+
+ int bufferSize = bufferImpl.limit();
+
+ if (this instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
+
+ if (stride * idx.maxCoordIndex >= bufferSize) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+
+ if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ if (stride * idx.maxTexCoordIndices[i] >= bufferSize) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ }
+ }
+
+ if (((this.vertexFormat & GeometryArray.COLOR) != 0) &&
+ (stride * idx.maxColorIndex >= bufferSize)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+
+ if (((this.vertexFormat & GeometryArray.NORMALS) != 0) &&
+ (stride * idx.maxNormalIndex >= bufferSize)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ } else {
+ if (bufferSize < (stride * (initialVertexIndex+validVertexCount)))
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
+ }
+ }
+ // If the geometry has been rendered transparent, then make a copy
+ // of the color pointer with 4f
+ geomLock.getLock();
+ dirtyFlag |= VERTEX_CHANGED;
+ colorChanged = 0xffff;
+ interleavedVertexBuffer = vertexData;
+
+ if(vertexData == null)
+ interleavedFloatBufferImpl = null;
+ else
+ interleavedFloatBufferImpl = bufferImpl;
+
+ if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
+ ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
+ setupMirrorInterleavedColorPointer(false);
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ processCoordsChanged(vertexData == null);
+ sendDataChangedMessage(true);
+ }
+ }
+
+ float[] getInterleavedVertices() {
+ return interLeavedVertexData;
+ }
+
+ J3DBuffer getInterleavedVertexBuffer() {
+ return interleavedVertexBuffer;
+ }
+
+ void setValidVertexCount(int validVertexCount) {
+ boolean nullGeo = false;
+ if (validVertexCount < 0) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray110"));
+ }
+ if ((initialVertexIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray100"));
+ }
+ else if ((initialCoordIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray104"));
+ }
+ else if ((initialColorIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray101"));
+ }
+ else if ((initialNormalIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray102"));
+ }
+ else {
+ if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) {
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ if ((initialTexCoordIndex[i] + validVertexCount)
+ > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString(
+ "GeometryArray103"));
+ }
+ }
+ }
+ }
+ }
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ // use nio buffer for interleaved data
+ if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 && interleavedFloatBufferImpl != null){
+ if(interleavedFloatBufferImpl.limit() < stride * (initialVertexIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
+ }
+ }
+ //use java array for interleaved data
+ else if( interLeavedVertexData != null) {
+ if(interLeavedVertexData.length < stride * (initialVertexIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
+ }
+ }
+ else {
+ nullGeo = true;
+ }
+ } else if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0) {
+ if ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0)
+ nullGeo = true;
+
+ if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ // by reference with nio buffer
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case PF:
+ if(floatBufferRefCoords.limit() < 3 * (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case PD:
+ if(doubleBufferRefCoords.limit() < 3 * (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ }
+
+ switch ((vertexType & COLOR_DEFINED)) {
+ case CF:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (floatBufferRefColors.limit() < 3 * (initialColorIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (floatBufferRefColors.limit() < 4 * (initialColorIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+ case CUB:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (byteBufferRefColors.limit() < 3 * (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (byteBufferRefColors.limit() < 4 * (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+ }
+ switch ((vertexType & GeometryArrayRetained.TEXCOORD_DEFINED)) {
+ case TF:
+ FloatBufferWrapper texBuffer;
+ for (int i = 0; i < texCoordSetCount; i++) {
+ texBuffer = (FloatBufferWrapper)(((J3DBuffer)refTexCoordsBuffer[i]).getBufferImpl());
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ if (texBuffer.limit() < 2 * (initialTexCoordIndex[i] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ if (texBuffer.limit() < 3 * (initialTexCoordIndex[i] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ if (texBuffer.limit() < 4 * (initialTexCoordIndex[i] + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ }
+ }
+ break;
+ }
+ switch ((vertexType & GeometryArrayRetained.NORMAL_DEFINED)) {
+ case NF:
+ if (floatBufferRefNormals.limit() < 3 * (initialNormalIndex + validVertexCount )) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ break;
+ }
+ }
+ // By reference with java array
+ else {
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case PF:
+ if (floatRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case PD:
+ if (doubleRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case P3F:
+ if (p3fRefCoords.length < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case P3D:
+ if (p3dRefCoords.length < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ }
+ switch ((vertexType & COLOR_DEFINED)) {
+ case CF:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (floatRefColors.length < 3 * (initialColorIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (floatRefColors.length < 4 * (initialColorIndex+ validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+ case CUB:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (byteRefColors.length < 3 * (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (byteRefColors.length < 4 * (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+ case C3F:
+ if (c3fRefColors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ case C4F:
+ if (c4fRefColors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ case C3UB:
+ if (c3bRefColors.length < (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ case C4UB:
+ if (c4bRefColors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ }
+ switch ((vertexType & GeometryArrayRetained.TEXCOORD_DEFINED)) {
+ case TF:
+ for (int i = 0; i < texCoordSetCount; i++) {
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ if (((float[])refTexCoords[i]).length < 2 * (initialTexCoordIndex[i] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ if (((float[])refTexCoords[i]).length < 3 * (initialTexCoordIndex[i] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ if (((float[])refTexCoords[i]).length < 4 * (initialTexCoordIndex[i] + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ }
+ }
+ }
+ break;
+ case T2F:
+ for (int i = 0; i < texCoordSetCount; i++) {
+ if (((TexCoord2f[])refTexCoords[i]).length < (initialTexCoordIndex[i] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ }
+ break;
+ case T3F:
+ for (int i = 0; i < texCoordSetCount; i++) {
+ if (((TexCoord3f[])refTexCoords[i]).length < (initialTexCoordIndex[i] + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ }
+ break;
+ }
+ switch ((vertexType & GeometryArrayRetained.NORMAL_DEFINED)) {
+ case NF:
+ if (floatRefNormals.length < 3 * (initialNormalIndex + validVertexCount )) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ break;
+ case N3F:
+ if (v3fRefNormals.length < (initialNormalIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ }
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= VERTEX_CHANGED;
+ this.validVertexCount = validVertexCount;
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ processCoordsChanged(nullGeo);
+ sendDataChangedMessage(true);
+ }
+
+ }
+
+
+ int getValidVertexCount() {
+ return validVertexCount;
+ }
+
+ //Used for interleaved data (array or nio buffer)
+ void setInitialVertexIndex(int initialVertexIndex) {
+ boolean nullGeo = false;
+
+ if ((initialVertexIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray100"));
+ }
+
+ if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 && interleavedFloatBufferImpl != null) {
+ if(interleavedFloatBufferImpl.limit() < stride * (initialVertexIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
+ }
+ }
+ // interleaved data using java array
+ else if(interLeavedVertexData != null) {
+ if (interLeavedVertexData.length < stride * (initialVertexIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
+ }
+ }
+ else {
+ nullGeo = (vertexFormat & GeometryArray.INTERLEAVED) != 0; // Only for byRef
+ }
+ geomLock.getLock();
+ dirtyFlag |= VERTEX_CHANGED;
+ this.initialVertexIndex = initialVertexIndex;
+ geomLock.unLock();
+ if (!inUpdater&& source != null && source.isLive()) {
+ processCoordsChanged(nullGeo);
+ sendDataChangedMessage(true);
+ }
+ }
+
+ int getInitialVertexIndex() {
+ return initialVertexIndex;
+ }
+
+ void setInitialCoordIndex(int initialCoordIndex) {
+ if ((initialCoordIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray104"));
+ }
+ // use NIO buffer
+ if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case PF:
+ if(floatBufferRefCoords.limit() < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case PD:
+ if(doubleBufferRefCoords.limit() < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ }
+ } else {
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case PF:
+ if (floatRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case PD:
+ if (doubleRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case P3F:
+ if (p3fRefCoords.length < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ case P3D:
+ if (p3dRefCoords.length < (initialCoordIndex+validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
+ }
+ break;
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= COORDINATE_CHANGED;
+ this.initialCoordIndex = initialCoordIndex;
+ dirtyFlag |= COORDINATE_CHANGED;
+ geomLock.unLock();
+ // Send a message, since bounds changed
+ if (!inUpdater && source != null && source.isLive()) {
+ processCoordsChanged((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
+ sendDataChangedMessage(true);
+ }
+ }
+
+ int getInitialCoordIndex() {
+ return initialCoordIndex;
+ }
+
+ void setInitialColorIndex(int initialColorIndex) {
+ if ((initialColorIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray101"));
+ }
+ // NIO BUFFER CASE
+ if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
+ switch ((vertexType & COLOR_DEFINED)) {
+ case CF:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (floatBufferRefColors.limit() < 3 * (initialColorIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (floatBufferRefColors.limit() < 4 * (initialColorIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+
+ case CUB:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (byteBufferRefColors.limit() < 3 * (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (byteBufferRefColors.limit() < 4 * (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+ }
+ }
+ // Java ARRAY CASE
+ else {
+ switch ((vertexType & COLOR_DEFINED)) {
+ case CF:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (floatRefColors.length < 3 * (initialColorIndex+validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (floatRefColors.length < 4 * (initialColorIndex+ validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+ case CUB:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ if (byteRefColors.length < 3 * (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ if (byteRefColors.length < 4 * (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ }
+ break;
+ case C3F:
+ if (c3fRefColors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ case C4F:
+ if (c4fRefColors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ case C3UB:
+ if (c3bRefColors.length < (initialColorIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ case C4UB:
+ if (c4bRefColors.length < (initialColorIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
+ }
+ break;
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= COLOR_CHANGED;
+ colorChanged = 0xffff;
+ this.initialColorIndex = initialColorIndex;
+ geomLock.unLock();
+ // There is no need to send message for by reference, since we
+ // use VA
+
+ }
+
+ int getInitialColorIndex() {
+ return initialColorIndex;
+ }
+
+ void setInitialNormalIndex(int initialNormalIndex) {
+ if ((initialNormalIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray102"));
+ }
+ if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
+ if((vertexType & NORMAL_DEFINED) == NF){
+ if (floatBufferRefNormals.limit() < 3 * (initialNormalIndex + validVertexCount )) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ }
+ } else {
+ switch((vertexType & NORMAL_DEFINED)){
+ case NF:
+ if (floatRefNormals.length < 3 * (initialNormalIndex + validVertexCount )) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ break;
+ case N3F:
+ if (v3fRefNormals.length < (initialNormalIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
+ }
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= NORMAL_CHANGED;
+ this.initialNormalIndex = initialNormalIndex;
+ geomLock.unLock();
+ // There is no need to send message for by reference, since we
+ // use VA
+ }
+
+ int getInitialNormalIndex() {
+ return initialNormalIndex;
+ }
+
+ void setInitialTexCoordIndex(int texCoordSet, int initialTexCoordIndex) {
+ if ((initialTexCoordIndex + validVertexCount) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryArray103"));
+ }
+
+ if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
+ if((vertexType & TEXCOORD_DEFINED) == TF) {
+ FloatBufferWrapper texBuffer = (FloatBufferWrapper)(((J3DBuffer) refTexCoordsBuffer[texCoordSet]).getBufferImpl());
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ if (texBuffer.limit() < 2 * (initialTexCoordIndex+ validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ if (texBuffer.limit() < 3 * (initialTexCoordIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ if (texBuffer.limit() < 4 * (initialTexCoordIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ }
+ }
+ } else {
+ switch ((vertexType & TEXCOORD_DEFINED)) {
+ case TF:
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ if (((float[])refTexCoords[texCoordSet]).length < 2 * (initialTexCoordIndex+ validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ if (((float[])refTexCoords[texCoordSet]).length < 3 * (initialTexCoordIndex + validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ if (((float[])refTexCoords[texCoordSet]).length < 4 * (initialTexCoordIndex + validVertexCount)) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ }
+ break;
+ case T2F:
+ if (((TexCoord2f[])refTexCoords[texCoordSet]).length < (initialTexCoordIndex+ validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ break;
+ case T3F:
+ if (((TexCoord3f[])refTexCoords[texCoordSet]).length < (initialTexCoordIndex+ validVertexCount) ) {
+ throw new ArrayIndexOutOfBoundsException(
+ J3dI18N.getString("GeometryArray113"));
+ }
+ break;
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= TEXTURE_CHANGED;
+ this.initialTexCoordIndex[texCoordSet] = initialTexCoordIndex;
+ geomLock.unLock();
+ // There is no need to send message for by reference, since we
+ // use VA
+ }
+
+ int getInitialTexCoordIndex(int texCoordSet) {
+ return initialTexCoordIndex[texCoordSet];
+ }
+
+
+ int getTexCoordSetCount() {
+ return this.texCoordSetCount;
+ }
+
+ int getTexCoordSetMapLength() {
+ if (this.texCoordSetMap != null)
+ return this.texCoordSetMap.length;
+ else
+ return 0;
+ }
+
+ void getTexCoordSetMap(int [] texCoordSetMap) {
+
+ if (this.texCoordSetMap!=null) {
+ for (int i = 0; i < this.texCoordSetMap.length; i++) {
+ texCoordSetMap[i] = this.texCoordSetMap[i];
+ }
+ }
+ }
+
+ protected void finalize() {
+ // For Pure immediate mode, there is no clearLive so
+ // surface will free when JVM do GC
+ if (pVertexBuffers != 0) {
+ // memory not yet free for immediate mode rendering
+ // It is thread safe since D3D only free surface in
+ // the next swapBuffer() call which must be in the
+ // same renderer threads
+ freeD3DArray(true);
+ }
+ }
+
+ void freeDlistId() {
+ if (dlistId != -1) {
+ VirtualUniverse.mc.freeDisplayListId(dlistObj);
+ dlistId = -1;
+ }
+ }
+
+ void assignDlistId() {
+ if (dlistId == -1) {
+ dlistObj = VirtualUniverse.mc.getDisplayListId();
+ dlistId = dlistObj.intValue();
+ }
+ }
+
+ void incrDlistRefCount(int bit) {
+ int index = getIndex(bit);
+ int[] newList = null;
+ int i;
+ synchronized(renderMolPerDlist) {
+ if (index >= renderMolPerDlist.length) {
+ newList = new int[index * 2];
+ for (i = 0; i < renderMolPerDlist.length; i++) {
+ newList[i] = renderMolPerDlist[i];
+ }
+ newList[index] = 1;
+
+ // This must be the last statment for
+ // sync. to work correctly
+ renderMolPerDlist = newList;
+ }
+ else {
+ renderMolPerDlist[index]++;
+ }
+ }
+ }
+
+ int decrDlistRefCount(int rdrBit) {
+ int index = getIndex(rdrBit);
+ synchronized(renderMolPerDlist) {
+ if (index >= renderMolPerDlist.length) {
+ // In case of sharedDlist it is possible that
+ // decrDlistRefCount is invoke for cleanup
+ // from another canvas
+ return -1;
+ }
+ renderMolPerDlist[index]--;
+ return renderMolPerDlist[index];
+ }
+ }
+
+ void setDlistTimeStamp(int rdrBit, long timeStamp) {
+ int index = getIndex(rdrBit);
+ if (index >= timeStampPerDlist.length) {
+ long[] newList = new long[index * 2];
+ for (int i = 0; i < timeStampPerDlist.length; i++) {
+ newList[i] = timeStampPerDlist[i];
+ }
+ timeStampPerDlist = newList;
+ }
+ timeStampPerDlist[index] = timeStamp;
+ }
+
+ long getDlistTimeStamp(int rdrBit) {
+ int index = getIndex(rdrBit);
+ // If index is greater than what currently exists, increase
+ // the array and return zero
+ if (index >= timeStampPerDlist.length) {
+ setDlistTimeStamp(rdrBit, 0);
+ }
+ return timeStampPerDlist[index];
+ }
+
+ int getIndex(int bit) {
+ int num = 0;
+
+ while (bit > 0) {
+ num++;
+ bit >>= 1;
+ }
+ return num;
+ }
+
+
+ boolean isWriteStatic() {
+
+ if (source.getCapability(GeometryArray.ALLOW_COORDINATE_WRITE ) ||
+ source.getCapability(GeometryArray.ALLOW_COLOR_WRITE) ||
+ source.getCapability(GeometryArray.ALLOW_NORMAL_WRITE) ||
+ source.getCapability(GeometryArray.ALLOW_TEXCOORD_WRITE) ||
+ source.getCapability(GeometryArray.ALLOW_COUNT_WRITE) ||
+ source.getCapability(GeometryArray.ALLOW_REF_DATA_WRITE))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * The functions below are only used in compile mode
+ */
+ void setCompiled(ArrayList curList) {
+ int i;
+ int num = curList.size();
+ int offset = 0;
+ geoOffset = new int[num];
+ compileVcount = new int[num];
+ int vcount = 0, vformat = 0;
+ vcount = 0;
+ isCompiled = true;
+
+ if (num > 0)
+ source = ((SceneGraphObjectRetained)curList.get(0)).source;
+ for (i = 0; i < num; i++) {
+ // Build the back mapping
+ GeometryArrayRetained geo = (GeometryArrayRetained)curList.get(i);
+ ((GeometryArray)geo.source).retained = this;
+ compileVcount[i] = geo.getValidVertexCount();
+ vcount += geo.getValidVertexCount();
+ geoOffset[i] = offset;
+ offset += geo.stride() * compileVcount[i];
+ vformat = geo.getVertexFormat();
+ }
+ createGeometryArrayData(vcount, vformat);
+
+ // Assign the initial and valid fields
+ validVertexCount = vcount;
+ initialVertexIndex = 0;
+
+ mergeGeometryArrays(curList);
+
+ }
+
+ /*
+ // Ununsed
+ int getVertexCount(int index) {
+ return compileVcount[index];
+ }
+
+
+ int getValidVertexCount(int index) {
+ return compileVcount[index];
+ }
+
+
+ int getInitialVertexIndex(int index) {
+ return 0;
+ }
+ */
+
+ void mergeGeometryArrays(ArrayList list) {
+ float[] curVertexData;
+ int length, srcOffset;
+ int curOffset = 0;
+ // We only merge if the texCoordSetCount is 1;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ texCoordSetCount = 1;
+ texCoordSetMap = new int[1];
+ texCoordSetMap[0] = 1;
+ }
+ for (int i = 0; i < list.size(); i++) {
+ GeometryArrayRetained geo = (GeometryArrayRetained)list.get(i);
+ // Take into account the validVertexCount and initialVertexIndex
+ curVertexData = geo.vertexData;
+ length = geo.validVertexCount * stride;
+ srcOffset = geo.initialVertexIndex * stride;
+ System.arraycopy(curVertexData, srcOffset, this.vertexData, curOffset,
+ length);
+ curOffset += length;
+
+ // assign geoBounds
+ geoBounds.combine(geo.geoBounds);
+
+ }
+ this.centroid.set(geoBounds.getCenter());
+ }
+
+ boolean isMergeable() {
+
+ // For now, turn off by ref geometry
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0)
+ return false;
+
+ if (!isStatic())
+ return false;
+
+ // If there is more than one set of texture coordinate set defined
+ // then don't merge geometry (we avoid dealing with texCoordSetMap
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0 &&
+ (texCoordSetCount > 1 ||
+ texCoordSetMap != null && texCoordSetMap.length > 1)) {
+ return false;
+ }
+
+
+ // If intersect is allowed turn off merging
+ if (source.getCapability(Geometry.ALLOW_INTERSECT))
+ return false;
+
+ return true;
+ }
+
+ boolean isTextureGeometryMergeable(GeometryArrayRetained srcGeo) {
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ if (texCoordSetCount != srcGeo.texCoordSetCount )
+ return false;
+
+ // If they are both non-null, then check if they are equivalent
+ if (texCoordSetMap != null && srcGeo.texCoordSetMap != null) {
+ if (texCoordSetMap.length != srcGeo.texCoordSetMap.length)
+ return false;
+
+ // Check the texCoordSetMap is same
+ for (int j = 0; j < texCoordSetMap.length; j++) {
+ if (texCoordSetMap[j] != srcGeo.texCoordSetMap[j])
+ return false;
+ }
+ }
+ // Check if they are both null;
+ // if one is null and other is non-null return false
+ else if (texCoordSetMap != srcGeo.texCoordSetMap)
+ return false;
+ }
+
+ return true;
+ }
+
+ void compile(CompileState compState) {
+ super.compile(compState);
+
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ compState.needNormalsTransform = true;
+ }
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ if (geoBounds != null) {
+ geoBounds.transform(xform.transform);
+ }
+ }
+
+ // This adds a MorphRetained to the list of users of this geometry
+ void addMorphUser(MorphRetained m) {
+ int index;
+ ArrayList morphList;
+
+ if(morphUniverseList == null) {
+ morphUniverseList = new ArrayList(1);
+ morphUserLists = new ArrayList(1);
+ }
+ synchronized (morphUniverseList) {
+ if (morphUniverseList.contains(m.universe)) {
+ index = morphUniverseList.indexOf(m.universe);
+ morphList = (ArrayList)morphUserLists.get(index);
+ morphList.add(m);
+ } else {
+ morphUniverseList.add(m.universe);
+ morphList = new ArrayList(5);
+ morphList.add(m);
+ morphUserLists.add(morphList);
+ }
+ }
+ }
+
+ // This adds a MorphRetained to the list of users of this geometry
+ void removeMorphUser(MorphRetained m) {
+ int index;
+ ArrayList morphList;
+
+ if(morphUniverseList == null)
+ return;
+
+ synchronized (morphUniverseList) {
+ index = morphUniverseList.indexOf(m.universe);
+ morphList = (ArrayList)morphUserLists.get(index);
+ morphList.remove(morphList.indexOf(m));
+ if (morphList.size() == 0) {
+ morphUserLists.remove(index);
+ morphUniverseList.remove(index);
+ }
+ }
+ }
+ // Initialize mirror object when geometry is first setLived
+ void initMirrorGeometry() {
+ geomLock.getLock();
+ if (this instanceof IndexedGeometryArrayRetained) {
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ mirrorGeometry = (GeometryRetained)
+ ((IndexedGeometryArrayRetained)this).cloneNonIndexedGeometry();
+ }
+ else {
+ mirrorGeometry = null;
+ }
+ }
+ geomLock.unLock();
+
+ }
+
+ // Update Mirror Object in response to change in geometry
+ void updateMirrorGeometry() {
+ geomLock.getLock();
+ if (this instanceof IndexedGeometryArrayRetained) {
+ if (mirrorGeometry != null) {
+ mirrorGeometry = (GeometryRetained)
+ ((IndexedGeometryArrayRetained)this).cloneNonIndexedGeometry();
+ }
+ }
+ geomLock.unLock();
+
+ }
+
+
+ // Used by the picking intersect routines
+ void getVertexData(int i, Point3d pnts) {
+ int offset;
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ offset = stride * i + coordinateOffset;
+ pnts.x = this.vertexData[offset];
+ pnts.y = this.vertexData[offset+1];
+ pnts.z = this.vertexData[offset+2];
+ return;
+ }
+
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0 ) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ offset = stride * i + coordinateOffset;
+ pnts.x = this.interLeavedVertexData[offset];
+ pnts.y = this.interLeavedVertexData[offset+1];
+ pnts.z = this.interLeavedVertexData[offset+2];
+ }
+ else {
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case GeometryArrayRetained.PF:
+ offset = i*3;
+ pnts.x = this.floatRefCoords[offset];
+ pnts.y = this.floatRefCoords[offset+1];
+ pnts.z = this.floatRefCoords[offset+2];
+ break;
+ case GeometryArrayRetained.PD:
+ offset = i*3;
+ pnts.x = this.doubleRefCoords[offset];
+ pnts.y = this.doubleRefCoords[offset+1];
+ pnts.z = this.doubleRefCoords[offset+2];
+ break;
+ case GeometryArrayRetained.P3F:
+ pnts.x = this.p3fRefCoords[i].x;
+ pnts.y = this.p3fRefCoords[i].y;
+ pnts.z = this.p3fRefCoords[i].z;
+ break;
+ case GeometryArrayRetained.P3D:
+ pnts.x = this.p3dRefCoords[i].x;
+ pnts.y = this.p3dRefCoords[i].y;
+ pnts.z = this.p3dRefCoords[i].z;
+ break;
+ }
+ }
+ }// end of non nio buffer
+ else { // NIO BUFFER
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ offset = stride * i + coordinateOffset;
+ pnts.x = this.interleavedFloatBufferImpl.get(offset);
+ pnts.y = this.interleavedFloatBufferImpl.get(offset+1);
+ pnts.z = this.interleavedFloatBufferImpl.get(offset+2);
+ }
+ else {
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case GeometryArrayRetained.PF:
+ offset = i*3;
+ pnts.x = this.floatBufferRefCoords.get(offset);
+ pnts.y = this.floatBufferRefCoords.get(offset+1);
+ pnts.z = this.floatBufferRefCoords.get(offset+2);
+ break;
+ case GeometryArrayRetained.PD:
+ offset = i*3;
+ pnts.x = this.doubleBufferRefCoords.get(offset);
+ pnts.y = this.doubleBufferRefCoords.get(offset+1);
+ pnts.z = this.doubleBufferRefCoords.get(offset+2);
+ break;
+ }
+ }
+ } // end of nio buffer
+ }
+
+ void getCrossValue(Point3d p1, Point3d p2, Vector3d value) {
+ value.x += p1.y*p2.z - p1.z*p2.y;
+ value.y += p2.x*p1.z - p2.z*p1.x;
+ value.z += p1.x*p2.y - p1.y*p2.x;
+ }
+
+
+ boolean intersect(Transform3D thisLocalToVworld,
+ Transform3D otherLocalToVworld, GeometryRetained geom) {
+
+ Transform3D tg = VirtualUniverse.mc.getTransform3D(null);
+ boolean isIntersect = false;
+
+ if (geom instanceof GeometryArrayRetained ) {
+ GeometryArrayRetained geomArray = (GeometryArrayRetained) geom;
+
+ if (geomArray.validVertexCount >= validVertexCount) {
+ tg.invert(otherLocalToVworld);
+ tg.mul(thisLocalToVworld);
+ isIntersect = intersect(tg, geom);
+ } else {
+ tg.invert(thisLocalToVworld);
+ tg.mul(otherLocalToVworld);
+ isIntersect = geomArray.intersect(tg, this);
+ }
+ } else {
+ tg.invert(thisLocalToVworld);
+ tg.mul(otherLocalToVworld);
+ isIntersect = geom.intersect(tg, this);
+ }
+
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, tg);
+ return isIntersect;
+ }
+
+ int getNumCoordCount() {
+ int count = 0;
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
+ count = vertexCount;
+ return count;
+ }
+
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case PF:
+ count = floatRefCoords.length/3;
+ break;
+ case PD:
+ count = doubleRefCoords.length/3;
+ break;
+ case P3F:
+ count = p3fRefCoords.length;
+ break;
+ case P3D:
+ count = p3dRefCoords.length;
+ break;
+ }
+ }
+ else {
+ count = interLeavedVertexData.length/stride;
+ }
+ }
+ else { // nio buffer
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case PF:
+ count = floatBufferRefCoords.limit()/3; // TODO: limit or capacity
+ break;
+ case PD:
+ count = doubleBufferRefCoords.limit()/3;
+ break;
+ }
+ }
+ else {
+ count = interleavedFloatBufferImpl.limit()/stride;
+ }
+ }
+ }
+ return count;
+ }
+
+ int getNumColorCount() {
+ int count = 0;
+ if ((vertexFormat & GeometryArray.COLOR) != 0){
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
+ count = vertexCount;
+ return count;
+ }
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ switch ((vertexType & GeometryArrayRetained.COLOR_DEFINED)) {
+ case CF:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ count = floatRefColors.length/3;
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
+ count = floatRefColors.length/4;
+ }
+ break;
+ case CUB:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ count = byteRefColors.length/3;
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
+ count = byteRefColors.length/4;
+ }
+ break;
+ case C3F:
+ count = c3fRefColors.length;
+ break;
+ case C4F:
+ count = c4fRefColors.length;
+ break;
+ case C3UB:
+ count = c3bRefColors.length;
+ break;
+ case C4UB:
+ count = c4bRefColors.length;
+ break;
+ }
+ }
+ else {
+ count = interLeavedVertexData.length/stride;
+ }
+ } // end of non nio buffer
+ else {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ switch ((vertexType & GeometryArrayRetained.COLOR_DEFINED)) {
+ case CF:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ count = floatBufferRefColors.limit()/3;
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
+ count = floatBufferRefColors.limit()/4;
+ }
+ break;
+ case CUB:
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ count = byteBufferRefColors.limit()/3;
+ }
+ else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
+ count = byteBufferRefColors.limit()/4;
+ }
+ break;
+ }
+ }
+ else {
+ count = interleavedFloatBufferImpl.limit()/stride;
+ }
+ } // end of nio buffer
+ }
+ return count;
+ }
+
+ int getNumNormalCount() {
+ int count = 0;
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
+ count = vertexCount;
+ return count;
+ }
+
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ switch ((vertexType & NORMAL_DEFINED)) {
+ case NF:
+ count = floatRefNormals.length/3;
+ break;
+ case N3F:
+ count = v3fRefNormals.length;
+ break;
+ }
+ }
+ else {
+ count = interLeavedVertexData.length/stride;
+ }
+ } // end of non nio buffer
+ else {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ if ((vertexType & NORMAL_DEFINED) == NF ) {
+ count = floatBufferRefNormals.limit()/3;
+ }
+ }
+ else {
+ count = interleavedFloatBufferImpl.limit()/stride;
+ }
+ }
+ }
+ return count;
+ }
+
+ int getNumTexCoordCount(int i) {
+ int count = 0;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0){
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
+ count = vertexCount;
+ return count;
+ }
+
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ switch ((vertexType & TEXCOORD_DEFINED)) {
+ case TF:
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ count = ((float[])refTexCoords[i]).length/2;
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ count = ((float[])refTexCoords[i]).length/3;
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ count = ((float[])refTexCoords[i]).length/4;
+ }
+
+ break;
+ case T2F:
+ count = ((TexCoord2f[])refTexCoords[i]).length;
+ break;
+ case T3F:
+ count = ((TexCoord3f[])refTexCoords[i]).length;
+ }
+ }
+ else {
+ count = interLeavedVertexData.length/stride;
+ }
+ }
+ else { // nio buffer
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
+ if ((vertexType & TEXCOORD_DEFINED) == TF) {
+ FloatBufferWrapper texBuffer = (FloatBufferWrapper)(((J3DBuffer) refTexCoordsBuffer[i]).getBufferImpl());
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ count = texBuffer.limit()/2;
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ count = texBuffer.limit()/3;
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ count = texBuffer.limit()/4;
+ }
+ }
+ }
+ else {
+ count = interleavedFloatBufferImpl.limit()/stride;
+ }
+ }
+ }
+ return count;
+ }
+
+ // Found the min distance from center to the point/line/tri/quad
+ // form by dist[]
+ void computeMinDistance(Point3d coordinates[], Point3d center,
+ Vector3d normal,
+ double dist[], Point3d iPnt) {
+ double x, y, z;
+ int i, j;
+
+ if (coordinates.length == 1) {
+ // a point
+ iPnt.x = coordinates[0].x;
+ iPnt.y = coordinates[0].y;
+ iPnt.z = coordinates[0].z;
+ x = iPnt.x - center.x;
+ y = iPnt.y - center.y;
+ z = iPnt.z - center.z;
+ dist[0] = Math.sqrt(x*x + y*y + z*z);
+ return;
+ }
+
+
+ if (coordinates.length == 2) {
+ // a line
+ dist[0] = Math.sqrt(Distance.pointToSegment(center,
+ coordinates[0],
+ coordinates[1],
+ iPnt, null));
+ return;
+ }
+
+ double normalLen = 0;
+
+ if (normal == null) {
+ Vector3d vec0 = new Vector3d();
+ Vector3d vec1 = new Vector3d();
+ normal = new Vector3d();
+ // 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)) {
+ // Degenerate polygon, check with edge only
+ normal = null;
+ } else {
+ normal.cross(vec0,vec1);
+ }
+ }
+
+ if (normal != null) {
+ normalLen = normal.length();
+ if ( normalLen == 0.0) {
+ // Degenerate polygon, check with edge only
+ normal = null;
+ }
+ }
+
+
+ if (coordinates.length == 3) {
+ // a triangle
+ if (normal != null) {
+ double d = -(normal.x*coordinates[0].x +
+ normal.y*coordinates[0].y +
+ normal.z*coordinates[0].z);
+ dist[0] = (normal.x*center.x + normal.y*center.y +
+ normal.z*center.z +
+ d)/normalLen;
+ iPnt.x = center.x - dist[0]*normal.x/normalLen;
+ iPnt.y = center.y - dist[0]*normal.y/normalLen;
+ iPnt.z = center.z - dist[0]*normal.z/normalLen;
+
+ if (pointInTri(iPnt, coordinates[0], coordinates[1],
+ coordinates[2], normal)) {
+ return;
+ }
+ }
+
+ // checking point to line distance
+ double minDist;
+ Point3d minPnt = new Point3d();
+
+ dist[0] = Distance.pointToSegment(center, coordinates[0],
+ coordinates[1], iPnt, null);
+ minDist = Distance.pointToSegment(center, coordinates[1],
+ coordinates[2], minPnt, null);
+ if (minDist < dist[0]) {
+ dist[0] = minDist;
+ iPnt.x = minPnt.x;
+ iPnt.y = minPnt.y;
+ iPnt.z = minPnt.z;
+ }
+ minDist = Distance.pointToSegment(center, coordinates[2],
+ coordinates[0], minPnt, null);
+ if (minDist < dist[0]) {
+ dist[0] = minDist;
+ iPnt.x = minPnt.x;
+ iPnt.y = minPnt.y;
+ iPnt.z = minPnt.z;
+ }
+ dist[0] = Math.sqrt(dist[0]);
+ return;
+ }
+
+ // a quad
+ if (normal != null) {
+ double d = -(normal.x*coordinates[0].x +
+ normal.y*coordinates[0].y +
+ normal.z*coordinates[0].z);
+ dist[0] = (normal.x*center.x + normal.y*center.y +
+ normal.z*center.z +
+ d)/normalLen;
+ iPnt.x = center.x - dist[0]*normal.x/normalLen;
+ iPnt.y = center.y - dist[0]*normal.y/normalLen;
+ iPnt.z = center.z - dist[0]*normal.z/normalLen;
+
+ if (pointInTri(iPnt, coordinates[0], coordinates[1],
+ coordinates[2], normal) ||
+ pointInTri(iPnt, coordinates[1], coordinates[2],
+ coordinates[3], normal)) {
+ return;
+ }
+ }
+
+ // checking point to line distance
+ double minDist;
+ Point3d minPnt = new Point3d();
+
+ dist[0] = Distance.pointToSegment(center, coordinates[0],
+ coordinates[1], iPnt, null);
+ minDist = Distance.pointToSegment(center, coordinates[1],
+ coordinates[2], minPnt, null);
+ if (minDist < dist[0]) {
+ dist[0] = minDist;
+ iPnt.x = minPnt.x;
+ iPnt.y = minPnt.y;
+ iPnt.z = minPnt.z;
+ }
+ minDist = Distance.pointToSegment(center, coordinates[2],
+ coordinates[3], minPnt, null);
+ if (minDist < dist[0]) {
+ dist[0] = minDist;
+ iPnt.x = minPnt.x;
+ iPnt.y = minPnt.y;
+ iPnt.z = minPnt.z;
+ }
+
+ minDist = Distance.pointToSegment(center, coordinates[3],
+ coordinates[0], minPnt, null);
+ if (minDist < dist[0]) {
+ dist[0] = minDist;
+ iPnt.x = minPnt.x;
+ iPnt.y = minPnt.y;
+ iPnt.z = minPnt.z;
+ }
+
+ dist[0] = Math.sqrt(dist[0]);
+ }
+
+ Vector3d getVector3d() {
+ return (Vector3d)FreeListManager.getObject(FreeListManager.VECTOR3D);
+ }
+
+ void freeVector3d(Vector3d v) {
+ FreeListManager.freeObject(FreeListManager.VECTOR3D, v);
+ }
+
+ Point3d getPoint3d() {
+ return (Point3d)FreeListManager.getObject(FreeListManager.POINT3D);
+ }
+
+ void freePoint3d(Point3d p) {
+ FreeListManager.freeObject(FreeListManager.POINT3D, p);
+ }
+
+
+ void handleFrequencyChange(int bit) {
+ int mask = 0;
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ if ((bit == GeometryArray.ALLOW_COORDINATE_WRITE) ||
+ (((vertexFormat & GeometryArray.COLOR) != 0) &&
+ bit == GeometryArray.ALLOW_COLOR_WRITE)||
+ (((vertexFormat & GeometryArray.NORMALS) != 0) &&
+ bit ==GeometryArray.ALLOW_NORMAL_WRITE) ||
+ (((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0)&&
+ bit == GeometryArray.ALLOW_TEXCOORD_WRITE) ||
+ (bit == GeometryArray.ALLOW_COUNT_WRITE)) {
+ mask = 1;
+ }
+ }
+ else {
+ if (bit == GeometryArray.ALLOW_REF_DATA_WRITE)
+ mask = 1;
+ }
+ if (mask != 0) {
+ setFrequencyChangeMask(bit, mask);
+ }
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryAtom.java b/src/classes/share/javax/media/j3d/GeometryAtom.java
new file mode 100644
index 0000000..b8dc3f5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryAtom.java
@@ -0,0 +1,263 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+import javax.vecmath.*;
+
+/**
+ * A GeometryAtom is the smallest object representing Geometry.
+ */
+
+class GeometryAtom extends Object implements BHLeafInterface, NnuId {
+
+ /**
+ * Array of geometry components of this geometry atom
+ */
+ // The first index of geometryArr should always be 0, unless geometryArr contains
+ // multiple Text3Ds.
+ GeometryRetained[] geometryArray = null;
+
+ /**
+ * Array of transforms used only for Text3d.
+ */
+ Transform3D[] lastLocalTransformArray = null;
+
+
+ /**
+ * The locale that this geometry atom is attatched to. This is only non-null
+ * if this instance is directly linked into a locale.
+ */
+ Locale locale = null;
+
+ /**
+ * The mirror Shape3DRetained for this GeometryAtom.
+ */
+ Shape3DRetained source = null;
+
+ /**
+ * The BHLeafNode for this GeometryAtom.
+ */
+ BHLeafNode bhLeafNode = null;
+
+ // true if alpha channel is editable
+ boolean alphaEditable;
+
+ // true if this ga is visible. Default is true.
+ boolean visible = true;
+
+ /**
+ * This is the original geometry type from which this atom came
+ */
+ int geoType = -1;
+
+ /**
+ * The list of RenderAtoms for this GeometryAtom
+ */
+ RenderAtom[] renderAtoms = new RenderAtom[0];
+
+ // Id use for quick search.
+ int nnuId;
+
+ Point3d[] centroid = null;
+ boolean centroidIsDirty = true;
+ Object lockObj = new Object();
+
+
+ GeometryAtom() {
+ // Get a not necessary unique Id.
+ nnuId = NnuIdManager.getId();
+ }
+
+ public int getId() {
+ return nnuId;
+ }
+
+ public int equal(NnuId obj) {
+ int keyId = obj.getId();
+ if(nnuId < keyId) {
+ return -1;
+ }
+ else if(nnuId > keyId) {
+ return 1;
+ }
+ else { // Found it!
+ return 0;
+ }
+ }
+
+ public BoundingBox computeBoundingHull() {
+ /*
+ System.out.println("Bounds is " + source.vwcBounds);
+ for(int i=0; i<geometryArray.length; i++) {
+ System.out.println( i + " geoBounds " +
+ geometryArray[i].geoBounds);
+ }
+ */
+
+ return source.vwcBounds;
+ }
+
+ // This method is use by picking and collision queries.
+ public boolean isEnable() {
+ return ((source.vwcBounds != null) &&
+ (source.vwcBounds.isEmpty() == false) &&
+ (source.switchState.currentSwitchOn));
+ }
+
+ // This method is use by visibility query.
+ public boolean isEnable(int vis) {
+ if((source.vwcBounds != null) && (source.vwcBounds.isEmpty() == false) &&
+ (source.switchState.currentSwitchOn)) {
+ switch(vis) {
+ case View.VISIBILITY_DRAW_VISIBLE:
+ return visible;
+ case View.VISIBILITY_DRAW_INVISIBLE:
+ return (!visible);
+ case View.VISIBILITY_DRAW_ALL:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Locale getLocale2() {
+ return locale;
+ }
+
+
+ /**
+ * Gets a RenderAtom for the given viewIndex.
+ * If it doesn't exist, it creates one.
+ */
+ RenderAtom getRenderAtom(View view) {
+ RenderAtom ra;
+ int index;
+ // If renderAtom is not scoped to this view, don't even
+ // bother creating the renderAtom
+
+ synchronized (renderAtoms) {
+ index = view.viewIndex;
+ if (index >= renderAtoms.length) {
+
+ // If creating a new RenderAtom, but this ga is not scoped
+ // to this view, then just return ..
+ if (source.viewList != null &&
+ !source.viewList.contains(view))
+ return null;
+ RenderAtom[] newList = new RenderAtom[index+1];
+ for (int i = 0; i < renderAtoms.length; i++) {
+ newList[i] = renderAtoms[i];
+ }
+ ra = new RenderAtom();
+ newList[index] = ra;
+ newList[index].geometryAtom = this;
+
+ // Allocate space based on number of geometry in the list
+ ra.rListInfo = new RenderAtomListInfo[geometryArray.length];
+ if (geoType != GeometryRetained.GEO_TYPE_TEXT3D) {
+ for (int j = 0; j < ra.rListInfo.length; j++) {
+ ra.rListInfo[j] = new RenderAtomListInfo();
+ ra.rListInfo[j].renderAtom = ra;
+ ra.rListInfo[j].index = j;
+ }
+ }
+ else {
+ for (int j = 0; j < ra.rListInfo.length; j++) {
+ ra.rListInfo[j] = new RenderAtomListInfo();
+ ra.rListInfo[j].renderAtom = ra;
+ ra.rListInfo[j].index = j;
+ ra.rListInfo[j].localToVworld = VirtualUniverse.mc.getTransform3D(null);
+ }
+ }
+
+ // Note this must be the last line in synchronized.
+ // Otherwise the lock is changed to newList and
+ // another thread can come in modified. This cause
+ // NullPointerException in
+ // renderAtoms[index].geometryAtom = this;
+ // which I encounter.
+ renderAtoms = newList;
+ } else {
+ if (renderAtoms[index] == null) {
+ // If creating a new RenderAtom, but this ga is not scoped
+ // to this view, then just return ..
+ if (source.viewList != null &&
+ !source.viewList.contains(view))
+ return null;
+
+ ra = new RenderAtom();
+ renderAtoms[index] = ra;
+ renderAtoms[index].geometryAtom = this;
+ // Allocate space based on number of geometry in the list
+ ra.rListInfo = new RenderAtomListInfo[geometryArray.length];
+ if (geoType != GeometryRetained.GEO_TYPE_TEXT3D) {
+ for (int j = 0; j < ra.rListInfo.length; j++) {
+ ra.rListInfo[j] = new RenderAtomListInfo();
+ ra.rListInfo[j].renderAtom = ra;
+ ra.rListInfo[j].index = j;
+ }
+ }
+ else {
+ for (int j = 0; j < ra.rListInfo.length; j++) {
+ ra.rListInfo[j] = new RenderAtomListInfo();
+ ra.rListInfo[j].renderAtom = ra;
+ ra.rListInfo[j].index = j;
+ ra.rListInfo[j].localToVworld = VirtualUniverse.mc.getTransform3D(null);
+ }
+ }
+ }
+ }
+ }
+
+ return (renderAtoms[index]);
+ }
+ // If the renderAtom is transparent, then make sure that the
+ // value is up-to-date
+
+ void updateCentroid() {
+ synchronized(lockObj) {
+ for (int j = 0; j < geometryArray.length; j++) {
+ if (geometryArray[j] == null)
+ continue;
+ synchronized(geometryArray[j].centroid) {
+ if (geometryArray[j].recompCentroid) {
+ geometryArray[j].computeCentroid();
+ geometryArray[j].recompCentroid = false;
+ }
+ }
+ }
+ if (centroidIsDirty) {
+ if (centroid == null) {
+ centroid = new Point3d[geometryArray.length];
+ for (int j = 0; j < centroid.length; j++) {
+ if (geometryArray[j] == null)
+ continue;
+ centroid[j] = new Point3d(geometryArray[j].centroid);
+ source.getCurrentLocalToVworld(0).transform(centroid[j]);
+ }
+ }
+ else {
+ for (int j = 0; j < centroid.length; j++) {
+ if (geometryArray[j] == null)
+ continue;
+ centroid[j].set(geometryArray[j].centroid);
+ source.getCurrentLocalToVworld(0).transform(centroid[j]);
+ }
+ }
+ centroidIsDirty = false;
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryDecompressor.java b/src/classes/share/javax/media/j3d/GeometryDecompressor.java
new file mode 100644
index 0000000..dce4596
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryDecompressor.java
@@ -0,0 +1,1200 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.* ;
+
+/**
+ * This abstract class provides the base methods needed to create a geometry
+ * decompressor. Subclasses must implement a backend to handle the output,
+ * consisting of a generalized triangle strip, line strip, or point array,
+ * along with possible global color and normal changes.
+ */
+abstract class GeometryDecompressor {
+ private static final boolean debug = false ;
+ private static final boolean benchmark = false ;
+
+ /**
+ * Compressed geometry format version supported.
+ */
+ static final int majorVersionNumber = 1 ;
+ static final int minorVersionNumber = 0 ;
+ static final int minorMinorVersionNumber = 2 ;
+
+ /**
+ * This method is called when a SetState command is encountered in the
+ * decompression stream.
+ *
+ * @param bundlingNorm true indicates normals are bundled with vertices
+ * @param bundlingColor true indicates colors are bundled with vertices
+ * @param doingAlpha true indicates alpha values are bundled with vertices
+ */
+ abstract void outputVertexFormat(boolean bundlingNorm,
+ boolean bundlingColor,
+ boolean doingAlpha) ;
+
+ /**
+ * This method captures the vertex output of the decompressor. The normal
+ * or color references may be null if the corresponding data is not
+ * bundled with the vertices in the compressed geometry buffer. Alpha
+ * values may be included in the color.
+ *
+ * @param position The coordinates of the vertex.
+ * @param normal The normal bundled with the vertex. May be null.
+ * @param color The color bundled with the vertex. May be null.
+ * Alpha may be present.
+ * @param vertexReplaceCode Specifies the generalized strip flag
+ * that is bundled with each vertex.
+ * @see GeneralizedStripFlags
+ * @see CompressedGeometryHeader
+ */
+ abstract void outputVertex(Point3f position, Vector3f normal,
+ Color4f color, int vertexReplaceCode) ;
+
+ /**
+ * This method captures the global color output of the decompressor. It
+ * is only invoked if colors are not bundled with the vertex data. The
+ * global color applies to all succeeding vertices until the next time the
+ * method is invoked.
+ *
+ * @param color The current global color.
+ */
+ abstract void outputColor(Color4f color) ;
+
+ /**
+ * This method captures the global normal output of the decompressor. It
+ * is only invoked if normals are not bundled with the vertex data. The
+ * global normal applies to all succeeding vertices until the next time the
+ * method is invoked.
+ *
+ * @param normal The current global normal.
+ */
+ abstract void outputNormal(Vector3f normal) ;
+
+ // Geometry compression opcodes.
+ private static final int GC_VERTEX = 0x40 ;
+ private static final int GC_SET_NORM = 0xC0 ;
+ private static final int GC_SET_COLOR = 0x80 ;
+ private static final int GC_MESH_B_R = 0x20 ;
+ private static final int GC_SET_STATE = 0x18 ;
+ private static final int GC_SET_TABLE = 0x10 ;
+ private static final int GC_PASS_THROUGH = 0x08 ;
+ private static final int GC_EOS = 0x00 ;
+ private static final int GC_V_NO_OP = 0x01 ;
+ private static final int GC_SKIP_8 = 0x07 ;
+
+ // Three 64-entry decompression tables are used: gctables[0] for
+ // positions, gctables[1] for colors, and gctables[2] for normals.
+ private HuffmanTableEntry gctables[][] ;
+
+ /**
+ * Decompression table entry.
+ */
+ static class HuffmanTableEntry {
+ int tagLength, dataLength ;
+ int rightShift, absolute ;
+
+ public String toString() {
+ return
+ " tag length: " + tagLength +
+ " data length: " + dataLength +
+ " shift: " + rightShift +
+ " abs/rel: " + absolute ;
+ }
+ }
+
+ // A 16-entry mesh buffer is used.
+ private MeshBufferEntry meshBuffer[] ;
+ private int meshIndex = 15 ;
+ private int meshState ;
+
+ // meshState values. These are needed to determine if colors and/or
+ // normals should come from meshBuffer or from SetColor or SetNormal.
+ private static final int USE_MESH_NORMAL = 0x1 ;
+ private static final int USE_MESH_COLOR = 0x2 ;
+
+ /**
+ * Mesh buffer entry containing position, normal, and color.
+ */
+ static class MeshBufferEntry {
+ short x, y, z ;
+ short octant, sextant, u, v ;
+ short r, g, b, a ;
+ }
+
+ // Geometry compression state variables.
+ private short curX, curY, curZ ;
+ private short curR, curG, curB, curA ;
+ private int curSex, curOct, curU, curV ;
+
+ // Current vertex data.
+ private Point3f curPos ;
+ private Vector3f curNorm ;
+ private Color4f curColor ;
+ private int repCode ;
+
+ // Flags indicating what data is bundled with the vertex.
+ private boolean bundlingNorm ;
+ private boolean bundlingColor ;
+ private boolean doingAlpha ;
+
+ // Internal decompression buffering variables.
+ private int currentHeader = 0 ;
+ private int nextHeader = 0 ;
+ private int bitBuffer = 0 ;
+ private int bitBufferCount = 32 ;
+
+ // Used for benchmarking if so configured.
+ private long startTime ;
+ private int vertexCount ;
+
+ // Bit-field masks: BMASK[i] = (1<<i)-1
+ private static final int BMASK[] = {
+ 0x0, 0x1, 0x3, 0x7,
+ 0xF, 0x1F, 0x3F, 0x7F,
+ 0xFF, 0x1FF, 0x3FF, 0x7FF,
+ 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF,
+ 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF,
+ 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF,
+ 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF,
+ 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+ 0xFFFFFFFF,
+ } ;
+
+ // A reference to the compressed data and the current offset.
+ private byte gcData[] ;
+ private int gcIndex ;
+
+ // The normals table for decoding 6-bit [u,v] spherical sextant coordinates.
+ private static final double gcNormals[][][] ;
+ private static final double NORMAL_MAX_Y_ANG = 0.615479709 ;
+ private static final boolean printNormalTable = false ;
+
+ /**
+ * Initialize the normals table.
+ */
+ static {
+ int i, j, inx, iny, inz ;
+ double th, psi, qnx, qny, qnz ;
+
+ gcNormals = new double[65][65][3] ;
+
+ for (i = 0 ; i < 65 ; i++) {
+ for (j = 0 ; j < 65 ; j++) {
+ if (i+j > 64) continue ;
+
+ psi = NORMAL_MAX_Y_ANG * (i / 64.0) ;
+ th = Math.asin(Math.tan(NORMAL_MAX_Y_ANG * ((64-j)/64.0))) ;
+
+ qnx = Math.cos(th) * Math.cos(psi) ;
+ qny = Math.sin(psi) ;
+ qnz = Math.sin(th) * Math.cos(psi) ;
+
+ // Convert the floating point normal to s1.14 bit notation,
+ // then back again.
+ qnx = qnx*16384.0 ; inx = (int)qnx ;
+ qnx = (double)inx ; qnx = qnx/16384.0 ;
+
+ qny = qny*16384.0 ; iny = (int)qny ;
+ qny = (double)iny ; qny = qny/16384.0 ;
+
+ qnz = qnz*16384.0 ; inz = (int)qnz ;
+ qnz = (double)inz ; qnz = qnz/16384.0 ;
+
+ gcNormals[i][j][0] = qnx ;
+ gcNormals[i][j][1] = qny ;
+ gcNormals[i][j][2] = qnz ;
+ }
+ }
+
+ if (printNormalTable) {
+ System.out.println("struct {") ;
+ System.out.println(" double nx, ny, nz ;") ;
+ System.out.println("} gcNormals[65][65] = {");
+ for (i = 0 ; i <= 64 ; i++) {
+ System.out.println("{") ;
+ for (j = 0 ; j <= 64 ; j++) {
+ if (j+i > 64) continue ;
+ System.out.println("{ " + gcNormals[i][j][0] +
+ ", " + gcNormals[i][j][1] +
+ ", " + gcNormals[i][j][2] + " }") ;
+ }
+ System.out.println("},") ;
+ }
+ System.out.println("}") ;
+ }
+ }
+
+ //
+ // The constructor.
+ //
+ GeometryDecompressor() {
+ curPos = new Point3f() ;
+ curNorm = new Vector3f() ;
+ curColor = new Color4f() ;
+ gctables = new HuffmanTableEntry[3][64] ;
+
+ for (int i = 0 ; i < 64 ; i++) {
+ gctables[0][i] = new HuffmanTableEntry() ;
+ gctables[1][i] = new HuffmanTableEntry() ;
+ gctables[2][i] = new HuffmanTableEntry() ;
+ }
+
+ meshBuffer = new MeshBufferEntry[16] ;
+ for (int i = 0 ; i < 16 ; i++)
+ meshBuffer[i] = new MeshBufferEntry() ;
+ }
+
+ /**
+ * Check version numbers and return true if compatible.
+ */
+ boolean checkVersion(int majorVersionNumber, int minorVersionNumber) {
+ return ((majorVersionNumber < this.majorVersionNumber) ||
+ ((majorVersionNumber == this.majorVersionNumber) &&
+ (minorVersionNumber <= this.minorVersionNumber))) ;
+ }
+
+ /**
+ * Decompress data and invoke abstract output methods.
+ *
+ * @param start byte offset to start of compressed geometry in data array
+ * @param length size of compressed geometry in bytes
+ * @param data array containing compressed geometry buffer of the
+ * specified length at the given offset from the start of the array
+ * @exception ArrayIndexOutOfBoundsException if start+length > data size
+ */
+ void decompress(int start, int length, byte data[]) {
+ if (debug)
+ System.out.println("GeometryDecompressor.decompress\n" +
+ " start: " + start +
+ " length: " + length +
+ " data array size: " + data.length) ;
+ if (benchmark)
+ benchmarkStart(length) ;
+
+ if (start+length > data.length)
+ throw new ArrayIndexOutOfBoundsException
+ (J3dI18N.getString("GeometryDecompressor0")) ;
+
+ // Set reference to compressed data and skip to start of data.
+ gcData = data ;
+ gcIndex = start ;
+
+ // Initialize state.
+ bitBufferCount = 0 ;
+ meshState = 0 ;
+ bundlingNorm = false ;
+ bundlingColor = false ;
+ doingAlpha = false ;
+ repCode = 0 ;
+
+ // Headers are interleaved for hardware implementations, so the
+ // first is always a nullop.
+ nextHeader = GC_V_NO_OP ;
+
+ // Enter decompression loop.
+ while (gcIndex < start+length)
+ processDecompression() ;
+
+ // Finish out any bits left in bitBuffer.
+ while (bitBufferCount > 0)
+ processDecompression() ;
+
+ if (benchmark)
+ benchmarkPrint(length) ;
+ }
+
+ //
+ // Return the next bitCount bits of compressed data.
+ //
+ private int getBits(int bitCount, String d) {
+ int bits ;
+
+ if (debug)
+ System.out.print(" getBits(" + bitCount + ") " + d + ", " +
+ bitBufferCount + " available at gcIndex " +
+ gcIndex) ;
+
+ if (bitCount == 0) {
+ if (debug) System.out.println(": got 0x0") ;
+ return 0 ;
+ }
+
+ if (bitBufferCount == 0) {
+ bitBuffer = (((gcData[gcIndex++] & 0xff) << 24) |
+ ((gcData[gcIndex++] & 0xff) << 16) |
+ ((gcData[gcIndex++] & 0xff) << 8) |
+ ((gcData[gcIndex++] & 0xff))) ;
+
+ bitBufferCount = 32 ;
+ }
+
+ if (bitBufferCount >= bitCount) {
+ bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount] ;
+ bitBuffer = bitBuffer << bitCount ;
+ bitBufferCount -= bitCount ;
+ } else {
+ bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount] ;
+ bits = bits >>> (bitCount - bitBufferCount) ;
+ bits = bits << (bitCount - bitBufferCount) ;
+
+ bitBuffer = (((gcData[gcIndex++] & 0xff) << 24) |
+ ((gcData[gcIndex++] & 0xff) << 16) |
+ ((gcData[gcIndex++] & 0xff) << 8) |
+ ((gcData[gcIndex++] & 0xff))) ;
+
+ bits = bits |
+ ((bitBuffer >>> (32 - (bitCount - bitBufferCount))) &
+ BMASK[bitCount - bitBufferCount]) ;
+
+ bitBuffer = bitBuffer << (bitCount - bitBufferCount) ;
+ bitBufferCount = 32 - (bitCount - bitBufferCount) ;
+ }
+
+ if (debug)
+ System.out.println(": got 0x" + Integer.toHexString(bits)) ;
+
+ return bits ;
+ }
+
+ //
+ // Shuffle interleaved headers and opcodes.
+ //
+ private void processDecompression() {
+ int mbp ;
+ currentHeader = nextHeader ;
+
+ if ((currentHeader & 0xC0) == GC_VERTEX) {
+ // Process a vertex.
+ if (!bundlingNorm && !bundlingColor) {
+ // get next opcode, process current position opcode
+ nextHeader = getBits(8, "header") ;
+ mbp = processDecompressionOpcode(0) ;
+
+ } else if (bundlingNorm && !bundlingColor) {
+ // get normal header, process current position opcode
+ nextHeader = getBits(6, "normal") ;
+ mbp = processDecompressionOpcode(0) ;
+ currentHeader = nextHeader | GC_SET_NORM ;
+
+ // get next opcode, process current normal opcode
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(mbp) ;
+
+ } else if (!bundlingNorm && bundlingColor) {
+ // get color header, process current position opcode
+ nextHeader = getBits(6, "color") ;
+ mbp = processDecompressionOpcode(0) ;
+ currentHeader = nextHeader | GC_SET_COLOR ;
+
+ // get next opcode, process current color opcode
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(mbp) ;
+
+ } else {
+ // get normal header, process current position opcode
+ nextHeader = getBits(6, "normal") ;
+ mbp = processDecompressionOpcode(0) ;
+ currentHeader = nextHeader | GC_SET_NORM ;
+
+ // get color header, process current normal opcode
+ nextHeader = getBits(6, "color") ;
+ processDecompressionOpcode(mbp) ;
+ currentHeader = nextHeader | GC_SET_COLOR ;
+
+ // get next opcode, process current color opcode
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(mbp) ;
+ }
+
+ // Send out the complete vertex.
+ outputVertex(curPos, curNorm, curColor, repCode) ;
+ if (benchmark) vertexCount++ ;
+
+ // meshState bits get turned off in the setColor and setNormal
+ // routines in order to keep track of what data a mesh buffer
+ // reference should use.
+ meshState |= USE_MESH_NORMAL ;
+ meshState |= USE_MESH_COLOR ;
+
+ } else {
+ // Non-vertex case: get next opcode, then process current opcode.
+ nextHeader = getBits(8, "header") ;
+ processDecompressionOpcode(0) ;
+ }
+ }
+
+ //
+ // Decode the opcode in currentHeader, and dispatch to the appropriate
+ // processing method.
+ //
+ private int processDecompressionOpcode(int mbp) {
+ if ((currentHeader & 0xC0) == GC_SET_NORM)
+ processSetNormal(mbp) ;
+ else if ((currentHeader & 0xC0) == GC_SET_COLOR)
+ processSetColor(mbp) ;
+ else if ((currentHeader & 0xC0) == GC_VERTEX)
+ // Return the state of the mesh buffer push bit
+ // when processing a vertex.
+ return processVertex() ;
+ else if ((currentHeader & 0xE0) == GC_MESH_B_R) {
+ processMeshBR() ;
+
+ // Send out the complete vertex.
+ outputVertex(curPos, curNorm, curColor, repCode) ;
+ if (benchmark) vertexCount++ ;
+
+ // meshState bits get turned off in the setColor and setNormal
+ // routines in order to keep track of what data a mesh buffer
+ // reference should use.
+ meshState |= USE_MESH_NORMAL ;
+ meshState |= USE_MESH_COLOR ;
+ }
+ else if ((currentHeader & 0xF8) == GC_SET_STATE)
+ processSetState() ;
+ else if ((currentHeader & 0xF8) == GC_SET_TABLE)
+ processSetTable() ;
+ else if ((currentHeader & 0xFF) == GC_EOS)
+ processEos() ;
+ else if ((currentHeader & 0xFF) == GC_V_NO_OP)
+ processVNoop() ;
+ else if ((currentHeader & 0xFF) == GC_PASS_THROUGH)
+ processPassThrough() ;
+ else if ((currentHeader & 0xFF) == GC_SKIP_8)
+ processSkip8() ;
+
+ return 0 ;
+ }
+
+ //
+ // Process a set state opcode.
+ //
+ private void processSetState() {
+ int ii ;
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetState") ;
+
+ ii = getBits(3, "bundling") ;
+
+ bundlingNorm = ((currentHeader & 0x1) != 0) ;
+ bundlingColor = (((ii >>> 2) & 0x1) != 0) ;
+ doingAlpha = (((ii >>> 1) & 0x1) != 0) ;
+
+ if (debug)
+ System.out.println(" bundling normal: " + bundlingNorm +
+ " bundling color: " + bundlingColor +
+ " alpha present: " + doingAlpha) ;
+
+ // Call the abstract output implementation.
+ outputVertexFormat(bundlingNorm, bundlingColor, doingAlpha) ;
+ }
+
+ //
+ // Process a set decompression table opcode.
+ //
+ // Extract the parameters of the table set command,
+ // and set the approprate table entries.
+ //
+ private void processSetTable() {
+ HuffmanTableEntry gct[] ;
+ int i, adr, tagLength, dataLength, rightShift, absolute ;
+ int ii, index ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetTable") ;
+
+ // Get reference to approprate 64 entry table.
+ index = (currentHeader & 0x6) >>> 1 ;
+ gct = gctables[index] ;
+
+ // Get the remaining bits of the set table command.
+ ii = getBits(15, "set table") ;
+
+ // Extract the individual fields from the two bit strings.
+ adr = ((currentHeader & 0x1) << 6) | ((ii >>> 9) & 0x3F) ;
+
+ // Get data length. For positions and colors, 0 really means 16, as 0
+ // lengths are meaningless for them. Normal components are allowed to
+ // have lengths of 0.
+ dataLength = (ii >>> 5) & 0x0F ;
+ if (dataLength == 0 && index != 2)
+ dataLength = 16 ;
+
+ rightShift = ii & 0x0F ;
+ absolute = (ii >>> 4) & 0x1 ;
+
+ //
+ // Decode the tag length from the address field by finding the
+ // first set 1 from the left in the bitfield.
+ //
+ for (tagLength = 6 ; tagLength > 0 ; tagLength--) {
+ if ((adr >> tagLength) != 0) break ;
+ }
+
+ // Shift the address bits up into place, and off the leading 1.
+ adr = (adr << (6 - tagLength)) & 0x3F ;
+
+ if (debug)
+ System.out.println(" table " + ((currentHeader & 0x6) >>> 1) +
+ " address " + adr +
+ " tag length " + tagLength +
+ " data length " + dataLength +
+ " shift " + rightShift +
+ " absolute " + absolute) ;
+
+ // Fill in the table fields with the specified values.
+ for (i = 0 ; i < (1 << (6 - tagLength)) ; i++) {
+ gct[adr+i].tagLength = tagLength ;
+ gct[adr+i].dataLength = dataLength ;
+ gct[adr+i].rightShift = rightShift ;
+ gct[adr+i].absolute = absolute ;
+ }
+ }
+
+
+ //
+ // Process a vertex opcode. Any bundled normal and/or color will be
+ // processed by separate methods. Return the mesh buffer push indicator.
+ //
+ private int processVertex() {
+ HuffmanTableEntry gct ;
+ float fX, fY, fZ ;
+ short dx, dy, dz ;
+ int mbp, x, y, z, dataLen ;
+ int ii ;
+
+ // If the next command is a mesh buffer reference
+ // then use colors and normals from the mesh buffer.
+ meshState = 0 ;
+
+ // Get a reference to the approprate tag table entry.
+ gct = gctables[0][currentHeader & 0x3F] ;
+
+ if (debug) System.out.println("GeometryDecompressor.processVertex\n" +
+ gct.toString()) ;
+
+ // Get the true length of the data.
+ dataLen = gct.dataLength - gct.rightShift ;
+
+ // Read in the replace code and mesh buffer push bits,
+ // if they're not in the current header.
+ if (6 - (3 * dataLen) - gct.tagLength > 0) {
+ int numBits = 6 - (3 * dataLen) - gct.tagLength ;
+ int jj ;
+
+ jj = currentHeader & BMASK[numBits] ;
+ ii = getBits(3 - numBits, "repcode/mbp") ;
+ ii |= (jj << (3 - numBits)) ;
+ }
+ else
+ ii = getBits(3, "repcode/mbp") ;
+
+ repCode = ii >>> 1 ;
+ mbp = ii & 0x1 ;
+
+ // Read in x, y, and z components.
+ x = currentHeader & BMASK[6-gct.tagLength] ;
+
+ if (gct.tagLength + dataLen == 6) {
+ y = getBits(dataLen, "y") ;
+ z = getBits(dataLen, "z") ;
+ } else if (gct.tagLength + dataLen < 6) {
+ x = x >> (6 - gct.tagLength - dataLen) ;
+
+ y = currentHeader & BMASK[6 - gct.tagLength - dataLen] ;
+ if (gct.tagLength + 2*dataLen == 6) {
+ z = getBits(dataLen, "z") ;
+ } else if (gct.tagLength + 2*dataLen < 6) {
+ y = y >> (6 - gct.tagLength - 2*dataLen) ;
+
+ z = currentHeader & BMASK[6 - gct.tagLength - 2*dataLen] ;
+ if (gct.tagLength + 3*dataLen < 6) {
+ z = z >> (6 - gct.tagLength - 3*dataLen) ;
+ } else if (gct.tagLength + 3*dataLen > 6) {
+ ii = getBits(dataLen - (6 - gct.tagLength - 2*dataLen),
+ "z") ;
+ z = (z << (dataLen - (6 - gct.tagLength - 2*dataLen)))
+ | ii ;
+ }
+ } else {
+ ii = getBits(dataLen - (6 - gct.tagLength - dataLen), "y") ;
+ y = (y << (dataLen - (6 - gct.tagLength - dataLen))) | ii ;
+ z = getBits(dataLen, "z") ;
+ }
+ } else {
+ ii = getBits(dataLen - (6 - gct.tagLength), "x") ;
+ x = (x << (dataLen - (6 - gct.tagLength))) | ii ;
+ y = getBits(dataLen, "y") ;
+ z = getBits(dataLen, "z") ;
+ }
+
+ // Sign extend delta x y z components.
+ x = x << (32 - dataLen) ; x = x >> (32 - dataLen) ;
+ y = y << (32 - dataLen) ; y = y >> (32 - dataLen) ;
+ z = z << (32 - dataLen) ; z = z >> (32 - dataLen) ;
+
+ // Normalize values.
+ dx = (short)(x << gct.rightShift) ;
+ dy = (short)(y << gct.rightShift) ;
+ dz = (short)(z << gct.rightShift) ;
+
+ // Update current position, first adding deltas if in relative mode.
+ if (gct.absolute != 0) {
+ curX = dx ; curY = dy ; curZ = dz ;
+ if (debug) System.out.println(" absolute position: " +
+ curX + " " + curY + " " + curZ) ;
+ } else {
+ curX += dx ; curY += dy ; curZ += dz ;
+ if (debug) System.out.println(" delta position: " +
+ dx + " " + dy + " " + dz) ;
+ }
+
+ // Do optional mesh buffer push.
+ if (mbp != 0) {
+ // Increment to next position (meshIndex is initialized to 15).
+ meshIndex = (meshIndex + 1) & 0xF ;
+ meshBuffer[meshIndex].x = curX ;
+ meshBuffer[meshIndex].y = curY ;
+ meshBuffer[meshIndex].z = curZ ;
+ if (debug)
+ System.out.println(" pushed position into mesh buffer at " +
+ meshIndex) ;
+ }
+
+ // Convert point back to [-1..1] floating point.
+ fX = curX ; fX /= 32768.0 ;
+ fY = curY ; fY /= 32768.0 ;
+ fZ = curZ ; fZ /= 32768.0 ;
+ if (debug)
+ System.out.println(" result position " + fX + " " + fY + " " + fZ) ;
+
+ curPos.set(fX, fY, fZ) ;
+ return mbp ;
+ }
+
+
+ //
+ // Process a set current normal opcode.
+ //
+ private void processSetNormal(int mbp) {
+ HuffmanTableEntry gct ;
+ int index, du, dv, n, dataLength ;
+ int ii ;
+
+ // if next command is a mesh buffer reference, use this normal
+ meshState &= ~USE_MESH_NORMAL ;
+
+ // use table 2 for normals
+ gct = gctables[2][currentHeader & 0x3F] ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetNormal\n" +
+ gct.toString()) ;
+
+ // subtract up-shift amount to get true data (u, v) length
+ dataLength = gct.dataLength - gct.rightShift ;
+
+ if (gct.absolute != 0) {
+ //
+ // Absolute normal case. Extract index from 6-bit tag.
+ //
+ index = currentHeader & BMASK[6-gct.tagLength] ;
+
+ if (gct.tagLength != 0) {
+ // read in the rest of the 6-bit sex/oct pair (index)
+ ii = getBits(6 - (6 - gct.tagLength), "sex/oct") ;
+ index = (index << (6 - (6 - gct.tagLength))) | ii ;
+ }
+
+ // read in u and v data
+ curU = getBits(dataLength, "u") ;
+ curV = getBits(dataLength, "v") ;
+
+ // normalize u, v, sextant, and octant
+ curU = curU << gct.rightShift ;
+ curV = curV << gct.rightShift ;
+
+ curSex = (index >> 3) & 0x7 ;
+ curOct = index & 0x7 ;
+
+ if (debug) {
+ if (curSex < 6)
+ System.out.println(" absolute normal: sex " + curSex +
+ " oct " + curOct +
+ " u " + curU + " v " + curV) ;
+ else
+ System.out.println(" special normal: sex " + curSex +
+ " oct " + curOct) ;
+ }
+ } else {
+ //
+ // Relative normal case. Extract du from 6-bit tag.
+ //
+ du = currentHeader & BMASK[6-gct.tagLength] ;
+
+ if (gct.tagLength + dataLength < 6) {
+ // normalize du, get dv
+ du = du >> (6 - gct.tagLength - dataLength) ;
+ dv = currentHeader & BMASK[6 - gct.tagLength - dataLength] ;
+
+ if (gct.tagLength + 2*dataLength < 6) {
+ // normalize dv
+ dv = dv >> (6 - gct.tagLength - 2*dataLength) ;
+ } else if (gct.tagLength + 2*dataLength > 6) {
+ // read in rest of dv and normalize it
+ ii = getBits(dataLength -
+ (6 - gct.tagLength - dataLength), "dv") ;
+ dv = (dv << (dataLength -
+ (6 - gct.tagLength - dataLength))) | ii ;
+ }
+ } else if (gct.tagLength + dataLength > 6) {
+ // read in rest of du and normalize it
+ ii = getBits(dataLength - (6 - gct.tagLength), "du") ;
+ du = (du << (dataLength - (6 - gct.tagLength))) | ii ;
+ // read in dv
+ dv = getBits(dataLength, "dv") ;
+ } else {
+ // read in dv
+ dv = getBits(dataLength, "dv") ;
+ }
+
+ // Sign extend delta uv components.
+ du = du << (32 - dataLength) ; du = du >> (32 - dataLength) ;
+ dv = dv << (32 - dataLength) ; dv = dv >> (32 - dataLength) ;
+
+ // normalize values
+ du = du << gct.rightShift ;
+ dv = dv << gct.rightShift ;
+
+ // un-delta
+ curU += du ;
+ curV += dv ;
+
+ if (debug)
+ System.out.println(" delta normal: du " + du + " dv " + dv) ;
+
+ //
+ // Check for normal wrap.
+ //
+ if (! ((curU >= 0) && (curV >= 0) && (curU + curV <= 64)))
+ if ((curU < 0) && (curV >= 0)) {
+ // wrap on u, same octant, different sextant
+ curU = -curU ;
+ switch (curSex) {
+ case 0: curSex = 4 ; break ;
+ case 1: curSex = 5 ; break ;
+ case 2: curSex = 3 ; break ;
+ case 3: curSex = 2 ; break ;
+ case 4: curSex = 0 ; break ;
+ case 5: curSex = 1 ; break ;
+ }
+ } else if ((curU >= 0) && (curV < 0)) {
+ // wrap on v, same sextant, different octant
+ curV = -curV ;
+ switch (curSex) {
+ case 1: case 5:
+ curOct = curOct ^ 4 ; // invert x axis
+ break ;
+ case 0: case 4:
+ curOct = curOct ^ 2 ; // invert y axis
+ break ;
+ case 2: case 3:
+ curOct = curOct ^ 1 ; // invert z axis
+ break ;
+ }
+ } else if (curU + curV > 64) {
+ // wrap on uv, same octant, different sextant
+ curU = 64 - curU ;
+ curV = 64 - curV ;
+ switch (curSex) {
+ case 0: curSex = 2 ; break ;
+ case 1: curSex = 3 ; break ;
+ case 2: curSex = 0 ; break ;
+ case 3: curSex = 1 ; break ;
+ case 4: curSex = 5 ; break ;
+ case 5: curSex = 4 ; break ;
+ }
+ } else {
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeometryDecompressor1")) ;
+ }
+ }
+
+ // do optional mesh buffer push
+ if (mbp != 0) {
+ if (debug)
+ System.out.println(" pushing normal into mesh buffer at " +
+ meshIndex) ;
+
+ meshBuffer[meshIndex].sextant = (short)curSex ;
+ meshBuffer[meshIndex].octant = (short)curOct ;
+ meshBuffer[meshIndex].u = (short)curU ;
+ meshBuffer[meshIndex].v = (short)curV ;
+ }
+
+ // convert normal back to [-1..1] floating point
+ indexNormal(curSex, curOct, curU, curV, curNorm) ;
+
+ // a set normal opcode when normals aren't bundled with the vertices
+ // is a global normal change.
+ if (! bundlingNorm) outputNormal(curNorm) ;
+ }
+
+
+ //
+ // Get the floating point normal from its sextant, octant, u, and v.
+ //
+ private void indexNormal(int sex, int oct, int u, int v, Vector3f n) {
+ float nx, ny, nz, t ;
+
+ if (debug) System.out.println(" sextant " + sex + " octant " + oct +
+ " u " + u + " v " + v) ;
+ if (sex > 5) {
+ // special normals
+ switch (oct & 0x1) {
+ case 0: // six coordinate axes
+ switch (((sex & 0x1) << 1) | ((oct & 0x4) >> 2)) {
+ case 0: nx = 1.0f ; ny = nz = 0.0f ; break ;
+ case 1: ny = 1.0f ; nx = nz = 0.0f ; break ;
+ default:
+ case 2: nz = 1.0f ; nx = ny = 0.0f ; break ;
+ }
+ sex = 0 ; oct = (oct & 0x2) >> 1 ;
+ oct = (oct << 2) | (oct << 1) | oct ;
+ break ;
+ case 1: // eight mid
+ default:
+ oct = ((sex & 0x1) << 2) | (oct >> 1) ;
+ sex = 0 ;
+ nx = ny = nz = (float)(1.0/Math.sqrt(3.0)) ;
+ break ;
+ }
+ if ((oct & 0x1) != 0) nz = -nz ;
+ if ((oct & 0x2) != 0) ny = -ny ;
+ if ((oct & 0x4) != 0) nx = -nx ;
+
+ } else {
+ // regular normals
+ nx = (float)gcNormals[v][u][0] ;
+ ny = (float)gcNormals[v][u][1] ;
+ nz = (float)gcNormals[v][u][2] ;
+
+ // reverse the swap
+ if ((sex & 0x4) != 0) { t = nx ; nx = nz ; nz = t ; }
+ if ((sex & 0x2) != 0) { t = ny ; ny = nz ; nz = t ; }
+ if ((sex & 0x1) != 0) { t = nx ; nx = ny ; ny = t ; }
+
+ // reverse the sign flip
+ if ((oct & 0x1) != 0) nz = -nz ;
+ if ((oct & 0x2) != 0) ny = -ny ;
+ if ((oct & 0x4) != 0) nx = -nx ;
+ }
+
+ // return resulting normal
+ n.set(nx, ny, nz) ;
+ if (debug)
+ System.out.println(" result normal: " + nx + " " + ny + " " + nz) ;
+ }
+
+
+ //
+ // Process a set current color command.
+ //
+ private void processSetColor(int mbp) {
+ HuffmanTableEntry gct ;
+ short dr, dg, db, da ;
+ float fR, fG, fB, fA ;
+ int r, g, b, a, index, dataLength ;
+ int ii ;
+
+ // If the next command is a mesh buffer reference, use this color.
+ meshState &= ~USE_MESH_COLOR ;
+
+ // Get the huffman table entry.
+ gct = gctables[1][currentHeader & 0x3F] ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processSetColor\n" +
+ gct.toString()) ;
+
+ // Get the true length of the data.
+ dataLength = gct.dataLength - gct.rightShift ;
+
+ // Read in red, green, blue, and possibly alpha.
+ r = currentHeader & BMASK[6 - gct.tagLength] ;
+ a = 0 ;
+
+ if (gct.tagLength + dataLength == 6) {
+ g = getBits(dataLength, "g") ;
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ else if (gct.tagLength + dataLength < 6) {
+ r = r >> (6 - gct.tagLength - dataLength) ;
+
+ g = currentHeader & BMASK[6-gct.tagLength-dataLength] ;
+ if (gct.tagLength + 2*dataLength == 6) {
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ else if (gct.tagLength + 2*dataLength < 6) {
+ g = g >> (6 - gct.tagLength - 2*dataLength) ;
+
+ b = currentHeader & BMASK[6-gct.tagLength-2*dataLength] ;
+ if (gct.tagLength + 3*dataLength == 6) {
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ else if (gct.tagLength + 3*dataLength < 6) {
+ b = b >> (6 - gct.tagLength - 3*dataLength) ;
+
+ if (doingAlpha) {
+ a = currentHeader &
+ BMASK[6 - gct.tagLength - 4*dataLength] ;
+ if (gct.tagLength + 4 * dataLength < 6) {
+ a = a >> (6 - gct.tagLength - 3*dataLength) ;
+ }
+ else if (gct.tagLength + 4 * dataLength > 6) {
+ ii = getBits(dataLength -
+ (6-gct.tagLength - 3*dataLength), "a") ;
+ a = (a << (dataLength -
+ (6-gct.tagLength - 3*dataLength))) | ii ;
+ }
+ }
+ } else {
+ ii = getBits(dataLength -
+ (6 - gct.tagLength - 2*dataLength), "b") ;
+ b = (b << (dataLength -
+ (6 - gct.tagLength - 2*dataLength))) | ii ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ } else {
+ ii = getBits(dataLength - (6 - gct.tagLength - dataLength),
+ "g") ;
+ g = (g << (dataLength -
+ (6 - gct.tagLength - dataLength))) | ii ;
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+ } else {
+ ii = getBits(dataLength - (6 - gct.tagLength), "r") ;
+ r = (r << (dataLength - (6 - gct.tagLength))) | ii ;
+ g = getBits(dataLength, "g") ;
+ b = getBits(dataLength, "b") ;
+ if (doingAlpha)
+ a = getBits(dataLength, "a") ;
+ }
+
+ // Sign extend delta x y z components.
+ r <<= (32 - dataLength) ; r >>= (32 - dataLength) ;
+ g <<= (32 - dataLength) ; g >>= (32 - dataLength) ;
+ b <<= (32 - dataLength) ; b >>= (32 - dataLength) ;
+ a <<= (32 - dataLength) ; a >>= (32 - dataLength) ;
+
+ // Normalize values.
+ dr = (short)(r << gct.rightShift) ;
+ dg = (short)(g << gct.rightShift) ;
+ db = (short)(b << gct.rightShift) ;
+ da = (short)(a << gct.rightShift) ;
+
+ // Update current position, first adding deltas if in relative mode.
+ if (gct.absolute != 0) {
+ curR = dr ; curG = dg ; curB = db ;
+ if (doingAlpha) curA = da ;
+ if (debug) System.out.println(" absolute color: r " + curR +
+ " g " + curG + " b " + curB +
+ " a " + curA) ;
+ } else {
+ curR += dr ; curG += dg ; curB += db ;
+ if (doingAlpha) curA += da ;
+ if (debug) System.out.println(" delta color: dr " + dr +
+ " dg " + dg + " db " + db +
+ " da " + da) ;
+ }
+
+ // Do optional mesh buffer push.
+ if (mbp != 0) {
+ if (debug)
+ System.out.println(" pushing color into mesh buffer at " +
+ meshIndex) ;
+
+ meshBuffer[meshIndex].r = curR ;
+ meshBuffer[meshIndex].g = curG ;
+ meshBuffer[meshIndex].b = curB ;
+ meshBuffer[meshIndex].a = curA ;
+ }
+
+ // Convert point back to [-1..1] floating point.
+ fR = curR ; fR /= 32768.0 ;
+ fG = curG ; fG /= 32768.0 ;
+ fB = curB ; fB /= 32768.0 ;
+ fA = curA ; fA /= 32768.0 ;
+
+ curColor.set(fR, fG, fB, fA) ;
+ if (debug) System.out.println(" result color: " + fR +
+ " " + fG + " " + fB + " " + fA) ;
+
+ // A set color opcode when colors aren't bundled with the vertices
+ // is a global color change.
+ if (! bundlingColor) outputColor(curColor) ;
+ }
+
+
+ //
+ // Process a mesh buffer reference command.
+ //
+ private void processMeshBR() {
+ MeshBufferEntry entry ;
+ int index, normal ;
+ int ii ;
+
+ if (debug)
+ System.out.println("GeometryDecompressor.processMeshBR") ;
+
+ ii = getBits(1, "mbr") ;
+
+ index = (currentHeader >>> 1) & 0xF ;
+ repCode = ((currentHeader & 0x1) << 1) | ii ;
+
+ // Adjust index to proper place in fifo.
+ index = (meshIndex - index) & 0xf ;
+ if (debug)
+ System.out.println(" using index " + index) ;
+
+ // Get reference to mesh buffer entry.
+ entry = meshBuffer[index] ;
+ curX = entry.x ;
+ curY = entry.y ;
+ curZ = entry.z ;
+
+ // Convert point back to [-1..1] floating point.
+ curPos.set(((float)curX)/32768.0f,
+ ((float)curY)/32768.0f,
+ ((float)curZ)/32768.0f) ;
+
+ if (debug) System.out.println(" retrieved position " + curPos.x +
+ " " + curPos.y + " " + curPos.z +
+ " replace code " + repCode) ;
+
+ // Get mesh buffer normal if previous opcode was not a setNormal.
+ if (bundlingNorm && ((meshState & USE_MESH_NORMAL) != 0)) {
+ curSex = entry.sextant ;
+ curOct = entry.octant ;
+ curU = entry.u ;
+ curV = entry.v ;
+
+ // Convert normal back to -1.0 - 1.0 floating point from index.
+ normal = (curSex<<15) | (curOct<<12) | (curU<<6) | curV ;
+
+ if (debug) System.out.println(" retrieving normal") ;
+ indexNormal(curSex, curOct, curU, curV, curNorm) ;
+ }
+
+ // Get mesh buffer color if previous opcode was not a setColor.
+ if (bundlingColor && ((meshState & USE_MESH_COLOR) != 0)) {
+ curR = entry.r ;
+ curG = entry.g ;
+ curB = entry.b ;
+
+ // Convert point back to -1.0 - 1.0 floating point.
+ curColor.x = curR ; curColor.x /= 32768.0 ;
+ curColor.y = curG ; curColor.y /= 32768.0 ;
+ curColor.z = curB ; curColor.z /= 32768.0 ;
+
+ if (doingAlpha) {
+ curA = entry.a ;
+ curColor.w = curA ; curColor.w /= 32768.0 ;
+ }
+ if (debug)
+ System.out.println(" retrieved color " + curColor.x +
+ " " + curColor.y + " " + curColor.z +
+ " " + curColor.w) ;
+ }
+
+ // Reset meshState.
+ meshState = 0 ;
+ }
+
+
+ // Process a end-of-stream opcode.
+ private void processEos() {
+ if (debug) System.out.println("GeometryDecompressor.processEos") ;
+ }
+
+ // Process a variable length no-op opcode.
+ private void processVNoop() {
+ int ii, ct ;
+ if (debug) System.out.println("GeometryDecompressor.processVNoop") ;
+
+ ct = getBits(5, "noop count") ;
+ ii = getBits(ct, "noop bits") ;
+ }
+
+ // Process a pass-through opcode.
+ private void processPassThrough() {
+ int ignore ;
+ if (debug)
+ System.out.println("GeometryDecompressor.processPassThrough") ;
+
+ ignore = getBits(24, "passthrough") ;
+ ignore = getBits(32, "passthrough") ;
+ }
+
+ // Process a skip-8 opcode.
+ private void processSkip8() {
+ int skip ;
+ if (debug) System.out.println("GeometryDecompressor.processSkip8") ;
+
+ skip = getBits(8, "skip8") ;
+ }
+
+ private void benchmarkStart(int length) {
+ vertexCount = 0 ;
+ System.out.println(" GeometryDecompressor: decompressing " +
+ length + " bytes...") ;
+ startTime = System.currentTimeMillis() ;
+ }
+
+ private void benchmarkPrint(int length) {
+ float t = (System.currentTimeMillis() - startTime) / 1000.0f ;
+ System.out.println
+ (" done in " + t + " sec." + "\n" +
+ " decompressed " + vertexCount + " vertices at " +
+ (vertexCount/t) + " vertices/sec\n") ;
+
+ System.out.print(" vertex data present: coords") ;
+ int floatVertexSize = 12 ;
+ if (bundlingNorm) {
+ System.out.print(" normals") ;
+ floatVertexSize += 12 ;
+ }
+ if (bundlingColor) {
+ System.out.println(" colors") ;
+ floatVertexSize += 12 ;
+ }
+ if (doingAlpha) {
+ System.out.println(" alpha") ;
+ floatVertexSize += 4 ;
+ }
+ System.out.println() ;
+
+ System.out.println
+ (" bytes of data in generalized strip output: " +
+ (vertexCount * floatVertexSize) + "\n" +
+ " compression ratio: " +
+ (length / (float)(vertexCount * floatVertexSize)) + "\n") ;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java b/src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java
new file mode 100644
index 0000000..03673e8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java
@@ -0,0 +1,384 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.* ;
+import java.util.* ;
+
+/**
+ * This class implements a retained geometry backend for the abstract
+ * GeometryDecompressor.
+ */
+class GeometryDecompressorRetained extends GeometryDecompressor {
+ private static final boolean debug = false ;
+ private static final boolean benchmark = false ;
+ private static final boolean statistics = false ;
+ private static final boolean printInfo = debug || benchmark || statistics ;
+
+ // Type of connections in the compressed data:
+ // TYPE_POINT (1), TYPE_LINE (2), or TYPE_TRIANGLE (4).
+ private int bufferDataType ;
+
+ // Data bundled with each vertex: bitwise combination of
+ // NORMAL_IN_BUFFER (1), COLOR_IN_BUFFER (2), ALPHA_IN_BUFFER (4).
+ private int dataPresent ;
+
+ // Size of the compressed geometry in bytes.
+ private int size ;
+
+ // Decompressor output state variables.
+ private Color4f curColor ;
+ private Vector3f curNormal ;
+
+ // List for accumulating the output of the decompressor and converting to
+ // GeometryArray representations.
+ private GeneralizedVertexList vlist ;
+
+ // Geometric bounds.
+ private Point3d lbounds = new Point3d() ;
+ private Point3d ubounds = new Point3d() ;
+
+ // Decompression output constraints. The decompressor will still process
+ // all data contained in the compressed buffer, but will only retain data
+ // for output subject to these booleans.
+ private boolean boundsOnly = false ;
+ private boolean positionsOnly = false ;
+
+ // A very rough gauge used to initialize the size of vlist, based on
+ // normal-per-vertex data collected from the HelloUniverse.cg file
+ // (seagull, '57 Chevy, dinosaur).
+ //
+ // TODO: get fudge values for other vertex combinations
+ private static final float bytesPerVertexFudge = 5.3f ;
+
+ // Used for benchmarking if so configured.
+ private long startTime ;
+ private long endTime ;
+
+ // Private convenience copies of various constants.
+ private static final int TYPE_POINT =
+ CompressedGeometryRetained.TYPE_POINT ;
+ private static final int TYPE_LINE =
+ CompressedGeometryRetained.TYPE_LINE ;
+ private static final int TYPE_TRIANGLE =
+ CompressedGeometryRetained.TYPE_TRIANGLE ;
+ private static final int FRONTFACE_CCW =
+ GeneralizedStripFlags.FRONTFACE_CCW ;
+
+ /**
+ * If the given argument is true, sets the decompressor to output only the
+ * bounding box of the decompressed geometry.
+ * @param boundsOnly set to true if the decompressor should output only the
+ * geometric bounding box.
+ */
+ void setDecompressBoundsOnly(boolean boundsOnly) {
+ this.boundsOnly = boundsOnly ;
+ if (boundsOnly) this.positionsOnly = false ;
+ }
+
+ /**
+ * If the given argument is true, sets the decompressor to output only the
+ * decompressed positions, their connections, and the bounding box.
+ * @param positionsOnly set to true if the decompressor should output only
+ * position, connection, and bounding box data.
+ */
+ void setDecompressPositionsOnly(boolean positionsOnly) {
+ this.positionsOnly = positionsOnly ;
+ if (positionsOnly) this.boundsOnly = false ;
+ }
+
+ /**
+ * Decompress the geometry data in a CompressedGeometryRetained. The
+ * GeometryArray output is intended to be cached as retained data for
+ * efficient rendering. Global color and normal changes output by the
+ * decompressor are stored as redundant per-vertex data so that a single
+ * GeometryArray can be used.
+ *
+ * Since only one GeometryArray is created, if the bundling attributes
+ * change while building the vertex list then the decompression is
+ * aborted. There should be only one SetState bundling attribute command
+ * per CompressedGeometry.
+ *
+ * @param cgr CompressedGeometryRetained containing compressed geometry
+ * @return GeometryArrayRetained containing the results of the
+ * decompression, or null if only the bounds are computed.
+ */
+ GeometryRetained decompress(CompressedGeometryRetained cgr) {
+
+ if (! checkVersion(cgr.majorVersionNumber, cgr.minorVersionNumber)) {
+ return null ;
+ }
+
+ vlist = null ;
+ curColor = null ;
+ curNormal = null ;
+ lbounds.set( 1.0, 1.0, 1.0) ;
+ ubounds.set(-1.0,-1.0,-1.0) ;
+
+ // Get the descriptors for the compressed data.
+ bufferDataType = cgr.bufferType ;
+ dataPresent = cgr.bufferContents ;
+ if (printInfo) beginPrint() ;
+
+ // Call the superclass decompress() method which calls the output
+ // methods of this subclass. The results are stored in vlist.
+ size = cgr.size ;
+ super.decompress(cgr.offset, size, cgr.compressedGeometry) ;
+
+ if (boundsOnly) {
+ if (printInfo) endPrint() ;
+ return null ;
+ }
+
+ // Convert the output to a GeometryRetained.
+ GeometryArray ga ;
+ switch(bufferDataType) {
+ case TYPE_TRIANGLE:
+ ga = vlist.toTriangleStripArray() ;
+ break ;
+ case TYPE_LINE:
+ ga = vlist.toLineStripArray() ;
+ break ;
+ case TYPE_POINT:
+ ga = vlist.toPointArray() ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeometryDecompressorRetained0")) ;
+ }
+
+ // Release the reference to the non-retained data.
+ ga.retained.setSource(null) ;
+
+ if (printInfo) endPrint() ;
+ return (GeometryRetained)ga.retained ;
+ }
+
+ /**
+ * Get the bounds of the decompressed geometry.
+ * @param bb BoundingBox to receive bounds
+ */
+ void getBoundingBox(BoundingBox bb) {
+ bb.setLower(lbounds) ;
+ bb.setUpper(ubounds) ;
+ }
+
+ /**
+ * Initialize the vertex output list based on the vertex format provided
+ * by the SetState decompression command.
+ */
+ void outputVertexFormat(boolean bundlingNorm, boolean bundlingColor,
+ boolean doingAlpha) {
+
+ if (boundsOnly) return ;
+
+ if (vlist != null)
+ throw new IllegalStateException
+ (J3dI18N.getString("GeometryDecompressorRetained1")) ;
+
+ int vertexFormat = GeometryArray.COORDINATES ;
+
+ if (! positionsOnly) {
+ if (bundlingNorm) vertexFormat |= GeometryArray.NORMALS ;
+ if (bundlingColor) vertexFormat |= GeometryArray.COLOR ;
+ if (doingAlpha) vertexFormat |= GeometryArray.WITH_ALPHA ;
+ }
+
+ vlist = new GeneralizedVertexList(vertexFormat, FRONTFACE_CCW,
+ (int)(size/bytesPerVertexFudge)) ;
+ }
+
+ /**
+ * Process a decompressed vertex.
+ */
+ void outputVertex(Point3f position, Vector3f normal,
+ Color4f color, int vertexReplaceCode) {
+
+ if (position.x < lbounds.x) lbounds.x = position.x ;
+ if (position.y < lbounds.y) lbounds.y = position.y ;
+ if (position.z < lbounds.z) lbounds.z = position.z ;
+
+ if (position.x > ubounds.x) ubounds.x = position.x ;
+ if (position.y > ubounds.y) ubounds.y = position.y ;
+ if (position.z > ubounds.z) ubounds.z = position.z ;
+
+ if (boundsOnly) return ;
+ if (curColor != null) color = curColor ;
+ if (curNormal != null) normal = curNormal ;
+
+ vlist.addVertex(position, normal, color, vertexReplaceCode) ;
+
+ if (debug) {
+ System.out.println("outputVertex: flag " + vertexReplaceCode) ;
+ System.out.println(" position " + position.toString()) ;
+ if (normal != null)
+ System.out.println(" normal " + normal.toString()) ;
+ if (color != null)
+ System.out.println(" color " + color.toString()) ;
+ }
+ }
+
+ /**
+ * Any global colors output by the decompressor are stored as per-vertex
+ * color in the retained data used internally by the renderer. This is
+ * done for performance and simplicity reasons, at the expense of
+ * replicating colors.
+ *
+ * The next method sets the current color that will be copied to each
+ * succeeding vertex. The outputColor() method is never called if
+ * colors are bundled with each vertex in the compressed buffer.
+ */
+ void outputColor(Color4f color) {
+ if (boundsOnly || positionsOnly) return ;
+ if (debug) System.out.println("outputColor: " + color.toString()) ;
+
+ if ((vlist.vertexFormat & GeometryArray.COLOR) == 0) {
+ if (vlist.size() > 0)
+ throw new IllegalStateException
+ (J3dI18N.getString("GeometryDecompressorRetained2")) ;
+
+ vlist.setVertexFormat(vlist.vertexFormat | GeometryArray.COLOR) ;
+ }
+
+ if (curColor == null) curColor = new Color4f() ;
+ curColor.set(color) ;
+ }
+
+ /**
+ * Set the current normal that will be copied to each succeeding vertex
+ * output by the decompressor. The per-vertex copy is always needed since
+ * in Java 3D a normal is always associated with a vertex. This is never
+ * called if normals are bundled with each vertex in the compressed
+ * buffer.
+ */
+ void outputNormal(Vector3f normal) {
+ if (boundsOnly || positionsOnly) return ;
+ if (debug) System.out.println("outputNormal: " + normal.toString()) ;
+
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) == 0) {
+ if (vlist.size() > 0)
+ throw new IllegalStateException
+ (J3dI18N.getString("GeometryDecompressorRetained3")) ;
+
+ vlist.setVertexFormat(vlist.vertexFormat | GeometryArray.NORMALS) ;
+ }
+
+ if (curNormal == null) curNormal = new Vector3f() ;
+ curNormal.set(normal) ;
+ }
+
+ private void beginPrint() {
+ System.out.println("\nGeometryDecompressorRetained") ;
+
+ switch(bufferDataType) {
+ case TYPE_TRIANGLE:
+ System.out.println(" buffer TYPE_TRIANGLE") ;
+ break ;
+ case TYPE_LINE:
+ System.out.println(" buffer TYPE_LINE") ;
+ break ;
+ case TYPE_POINT:
+ System.out.println(" buffer TYPE_POINT") ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeometryDecompressorRetained4")) ;
+ }
+
+ System.out.print(" buffer data present: coords") ;
+
+ if ((dataPresent & CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
+ System.out.print(" normals") ;
+ if ((dataPresent & CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
+ System.out.print(" colors") ;
+ if ((dataPresent & CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0)
+ System.out.print(" alpha") ;
+
+ System.out.println() ;
+ if (boundsOnly) System.out.println(" computing bounds only") ;
+ if (positionsOnly) System.out.println(" computing positions only") ;
+
+ startTime = System.currentTimeMillis() ;
+ }
+
+ private void endPrint() {
+ endTime = System.currentTimeMillis() ;
+
+ if (benchmark || statistics)
+ printBench() ;
+
+ if (statistics)
+ printStats() ;
+ }
+
+ private void printBench() {
+ float t = (endTime - startTime) / 1000.0f ;
+
+ if (boundsOnly) {
+ System.out.println(" decompression took " + t + " sec.\n") ;
+ return ;
+ }
+
+ System.out.println
+ (" decompression + strip conversion took " + t + " sec.") ;
+
+ switch(bufferDataType) {
+ case TYPE_POINT:
+ System.out.println
+ (" decompressed " + (vlist.size()) +
+ " points at " + (vlist.size()/t) +
+ " points/sec.\n") ;
+ break ;
+ case TYPE_LINE:
+ System.out.println
+ (" decompressed " + (vlist.vertexCount - vlist.stripCount) +
+ " lines at " + ((vlist.vertexCount - vlist.stripCount)/t) +
+ " lines/sec.\n") ;
+ break ;
+ case TYPE_TRIANGLE:
+ System.out.println
+ (" decompressed " +
+ (vlist.vertexCount - 2*vlist.stripCount) +
+ " triangles at " +
+ ((vlist.vertexCount - 2*vlist.stripCount)/t) +
+ " triangles/sec.\n") ;
+ break ;
+ }
+ }
+
+ private void printStats() {
+ System.out.println(" bounding box:\n lower " + lbounds.toString() +
+ "\n upper " + ubounds.toString()) ;
+
+ if (boundsOnly) return ;
+
+ System.out.print
+ (" number of vertices in GeometryArray output: " +
+ vlist.vertexCount + "\n" +
+ " GeometryArray vertex data present: coords") ;
+
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) != 0)
+ System.out.print(" normals") ;
+
+ if ((vlist.vertexFormat & GeometryArray.COLOR) != 0)
+ System.out.print(" colors") ;
+
+ if ((vlist.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ System.out.print(" alpha") ;
+
+ System.out.println("\n number of strips: " + vlist.stripCount) ;
+ if (vlist.stripCount > 0)
+ System.out.println
+ (" vertices/strip: " +
+ ((float)vlist.vertexCount / (float)vlist.stripCount)) ;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryDecompressorShape3D.java b/src/classes/share/javax/media/j3d/GeometryDecompressorShape3D.java
new file mode 100644
index 0000000..366a2bc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryDecompressorShape3D.java
@@ -0,0 +1,466 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.* ;
+import java.util.* ;
+
+/**
+ * This class implements a Shape3D backend for the abstract
+ * GeometryDecompressor.
+ */
+class GeometryDecompressorShape3D extends GeometryDecompressor {
+ private static final boolean debug = false ;
+ private static final boolean benchmark = false ;
+ private static final boolean statistics = false ;
+ private static final boolean printInfo = debug || benchmark || statistics ;
+
+ // Type of connections in the compressed data:
+ // TYPE_POINT (1), TYPE_LINE (2), or TYPE_TRIANGLE (4).
+ private int bufferDataType ;
+
+ // Data bundled with each vertex: bitwise combination of
+ // NORMAL_IN_BUFFER (1), COLOR_IN_BUFFER (2), ALPHA_IN_BUFFER (4).
+ private int dataPresent ;
+
+ // List for accumulating the output of the decompressor and converting to
+ // GeometryArray representations.
+ private GeneralizedVertexList vlist ;
+
+ // Accumulates Shape3D objects constructed from decompressor output.
+ private ArrayList shapes ;
+
+ // Decompressor output state variables.
+ private Color4f curColor ;
+ private Vector3f curNormal ;
+
+ // Variables for gathering statistics.
+ private int origVertexCount ;
+ private int stripCount ;
+ private int vertexCount ;
+ private int triangleCount ;
+ private long startTime ;
+ private long endTime ;
+
+ // Triangle array type to construct.
+ private int triOutputType ;
+
+ // Types of triangle output available.
+ private static final int TRI_SET = 0 ;
+ private static final int TRI_STRIP_SET = 1 ;
+ private static final int TRI_STRIP_AND_FAN_SET = 2 ;
+ private static final int TRI_STRIP_AND_TRI_SET = 3 ;
+
+ // Private convenience copies of various constants.
+ private static final int TYPE_POINT =
+ CompressedGeometryRetained.TYPE_POINT ;
+ private static final int TYPE_LINE =
+ CompressedGeometryRetained.TYPE_LINE ;
+ private static final int TYPE_TRIANGLE =
+ CompressedGeometryRetained.TYPE_TRIANGLE ;
+ private static final int FRONTFACE_CCW =
+ GeneralizedStripFlags.FRONTFACE_CCW ;
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleArray geometry if compressed
+ * data contains triangles; otherwise, Shape3D array containing PointArray
+ * or LineStripArray geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toTriangleArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_SET) ;
+ }
+
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleStripArray geometry if
+ * compressed data contains triangles; otherwise, Shape3D array containing
+ * PointArray or LineStripArray geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toTriangleStripArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_STRIP_SET) ;
+ }
+
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleStripArray and
+ * TriangleFanArray geometry if compressed data contains triangles;
+ * otherwise, Shape3D array containing PointArray or LineStripArray
+ * geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toStripAndFanArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_STRIP_AND_FAN_SET) ;
+ }
+
+
+ /**
+ * Decompress the given compressed geometry.
+ * @param cgr CompressedGeometryRetained object with compressed geometry
+ * @return an array of Shape3D with TriangleStripArray and
+ * TriangleArray geometry if compressed data contains triangles;
+ * otherwise, Shape3D array containing PointArray or LineStripArray
+ * geometry
+ * @see CompressedGeometry
+ * @see GeometryDecompressor
+ */
+ Shape3D[] toStripAndTriangleArrays(CompressedGeometryRetained cgr) {
+ return decompress(cgr, TRI_STRIP_AND_TRI_SET) ;
+ }
+
+ /**
+ * Decompress the data contained in a CompressedGeometryRetained and
+ * return an array of Shape3D objects using the specified triangle output
+ * type. The triangle output type is ignored if the compressed data
+ * contains points or lines.
+ */
+ private Shape3D[] decompress(CompressedGeometryRetained cgr,
+ int triOutputType) {
+
+ if (! checkVersion(cgr.majorVersionNumber, cgr.minorVersionNumber)) {
+ return null ;
+ }
+
+ vlist = null ;
+ curColor = null ;
+ curNormal = null ;
+
+ // Get descriptors for compressed data.
+ bufferDataType = cgr.bufferType ;
+ dataPresent = cgr.bufferContents ;
+ if (printInfo) beginPrint() ;
+
+ // Initialize the decompressor backend.
+ this.triOutputType = triOutputType ;
+ shapes = new ArrayList() ;
+
+ // Call the superclass decompress() method which calls the output
+ // methods of this subclass. The results are stored in vlist.
+ super.decompress(cgr.offset, cgr.size, cgr.compressedGeometry) ;
+
+ // Convert the decompressor output to Shape3D objects.
+ addShape3D() ;
+ if (printInfo) endPrint() ;
+
+ // Return the fixed-length output array.
+ Shape3D shapeArray[] = new Shape3D[shapes.size()] ;
+ return (Shape3D[])shapes.toArray(shapeArray) ;
+ }
+
+ /**
+ * Initialize the vertex output list based on the vertex format provided
+ * by the SetState decompression command.
+ */
+ void outputVertexFormat(boolean bundlingNorm, boolean bundlingColor,
+ boolean doingAlpha) {
+
+ if (vlist != null)
+ // Construct shapes using the current vertex format.
+ addShape3D() ;
+
+ int vertexFormat = GeometryArray.COORDINATES ;
+
+ if (bundlingNorm) vertexFormat |= GeometryArray.NORMALS ;
+ if (bundlingColor) vertexFormat |= GeometryArray.COLOR ;
+ if (doingAlpha) vertexFormat |= GeometryArray.WITH_ALPHA ;
+
+ vlist = new GeneralizedVertexList(vertexFormat, FRONTFACE_CCW) ;
+ }
+
+ /**
+ * Add a new decompressed vertex to the current list.
+ */
+ void outputVertex(Point3f position, Vector3f normal,
+ Color4f color, int vertexReplaceCode) {
+
+ if (curNormal != null) normal = curNormal ;
+ vlist.addVertex(position, normal, color, vertexReplaceCode) ;
+
+ if (debug) {
+ System.out.println(" outputVertex: flag " + vertexReplaceCode) ;
+ System.out.println(" position " + position.toString()) ;
+ if (normal != null)
+ System.out.println(" normal " + normal.toString()) ;
+ if (color != null)
+ System.out.println(" color " + color.toString()) ;
+ }
+ }
+
+ /**
+ * Create a Shape3D using the current color for both the ambient and
+ * diffuse material colors, then start a new vertex list for the new
+ * color. The outputColor() method is never called if colors are bundled
+ * with each vertex in the compressed buffer.
+ */
+ void outputColor(Color4f color) {
+ if (debug) System.out.println(" outputColor: " + color.toString()) ;
+
+ if (vlist.size() > 0) {
+ // Construct Shape3D using the current color.
+ addShape3D() ;
+
+ // Start a new vertex list for the new color.
+ vlist = new GeneralizedVertexList(vlist.vertexFormat,
+ FRONTFACE_CCW) ;
+ }
+ if (curColor == null) curColor = new Color4f() ;
+ curColor.set(color) ;
+ }
+
+ /**
+ * Set the current normal that will be copied to each succeeding vertex
+ * output by the decompressor. The per-vertex copy is needed since in
+ * Java 3D a normal is always associated with a vertex. This method is
+ * never called if normals are bundled with each vertex in the compressed
+ * buffer.
+ */
+ void outputNormal(Vector3f normal) {
+ if (debug) System.out.println(" outputNormal: " + normal.toString()) ;
+
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) == 0) {
+ if (vlist.size() > 0)
+ // Construct Shape3D using the current vertex format.
+ addShape3D() ;
+
+ // Start a new vertex list with the new format.
+ vlist = new GeneralizedVertexList
+ (vlist.vertexFormat|GeometryArray.NORMALS, FRONTFACE_CCW) ;
+ }
+ if (curNormal == null) curNormal = new Vector3f() ;
+ curNormal.set(normal) ;
+ }
+
+ /**
+ * Create a Shape3D object of the desired type from the current vertex
+ * list. Apply the current color, if non-null, as a Material attribute.
+ */
+ private void addShape3D() {
+ Material m = new Material() ;
+
+ if (curColor != null) {
+ if ((vlist.vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
+ m.setAmbientColor(curColor.x, curColor.y, curColor.z) ;
+ m.setDiffuseColor(curColor.x, curColor.y, curColor.z) ;
+ }
+ else {
+ m.setAmbientColor(curColor.x, curColor.y, curColor.z) ;
+ m.setDiffuseColor(curColor.x, curColor.y, curColor.z,
+ curColor.w) ;
+ }
+ }
+
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) == 0)
+ m.setLightingEnable(false) ;
+ else
+ m.setLightingEnable(true) ;
+
+ Appearance a = new Appearance() ;
+ a.setMaterial(m) ;
+
+ switch(bufferDataType) {
+ case TYPE_TRIANGLE:
+ switch(triOutputType) {
+ case TRI_SET:
+ TriangleArray ta = vlist.toTriangleArray() ;
+ if (ta != null)
+ shapes.add(new Shape3D(ta, a)) ;
+ break ;
+ case TRI_STRIP_SET:
+ TriangleStripArray tsa = vlist.toTriangleStripArray() ;
+ if (tsa != null)
+ shapes.add(new Shape3D(tsa, a)) ;
+ break ;
+ case TRI_STRIP_AND_FAN_SET:
+ GeometryStripArray gsa[] = vlist.toStripAndFanArrays() ;
+ if (gsa[0] != null)
+ shapes.add(new Shape3D(gsa[0], a)) ;
+ if (gsa[1] != null)
+ shapes.add(new Shape3D(gsa[1], a)) ;
+ break ;
+ case TRI_STRIP_AND_TRI_SET:
+ GeometryArray ga[] = vlist.toStripAndTriangleArrays() ;
+ if (ga[0] != null)
+ shapes.add(new Shape3D(ga[0], a)) ;
+ if (ga[1] != null)
+ shapes.add(new Shape3D(ga[1], a)) ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeometryDecompressorShape3D0")) ;
+ }
+ break ;
+
+ case TYPE_LINE:
+ LineStripArray lsa = vlist.toLineStripArray() ;
+ if (lsa != null)
+ shapes.add(new Shape3D(lsa, a)) ;
+ break ;
+
+ case TYPE_POINT:
+ PointArray pa = vlist.toPointArray() ;
+ if (pa != null)
+ shapes.add(new Shape3D(pa, a)) ;
+ break ;
+
+ default:
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeometryDecompressorShape3D1")) ;
+ }
+
+ if (benchmark || statistics) {
+ origVertexCount += vlist.size() ;
+ vertexCount += vlist.vertexCount ;
+ stripCount += vlist.stripCount ;
+ triangleCount += vlist.triangleCount ;
+ }
+ }
+
+ private void beginPrint() {
+ System.out.println("\nGeometryDecompressorShape3D") ;
+
+ switch(bufferDataType) {
+ case TYPE_TRIANGLE:
+ System.out.println(" buffer TYPE_TRIANGLE") ;
+ break ;
+ case TYPE_LINE:
+ System.out.println(" buffer TYPE_LINE") ;
+ break ;
+ case TYPE_POINT:
+ System.out.println(" buffer TYPE_POINT") ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ (J3dI18N.getString("GeometryDecompressorShape3D1")) ;
+ }
+
+ System.out.print(" buffer data present: coords") ;
+
+ if ((dataPresent & CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
+ System.out.print(" normals") ;
+ if ((dataPresent & CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
+ System.out.print(" colors") ;
+ if ((dataPresent & CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0)
+ System.out.print(" alpha") ;
+
+ System.out.println() ;
+
+ stripCount = 0 ;
+ vertexCount = 0 ;
+ triangleCount = 0 ;
+ origVertexCount = 0 ;
+
+ startTime = System.currentTimeMillis() ;
+ }
+
+ private void endPrint() {
+ endTime = System.currentTimeMillis() ;
+
+ if (benchmark || statistics)
+ printBench() ;
+
+ if (statistics)
+ printStats() ;
+ }
+
+ private void printBench() {
+ float t = (endTime - startTime) / 1000.0f ;
+ System.out.println
+ (" decompression + strip conversion took " + t + " sec.") ;
+
+ switch(bufferDataType) {
+ case TYPE_POINT:
+ System.out.println
+ (" points decompressed: " + vertexCount + "\n" +
+ " net decompression rate: " + (vertexCount/t) +
+ " points/sec.\n") ;
+ break ;
+ case TYPE_LINE:
+ System.out.println
+ (" lines decompressed: " + (vertexCount - stripCount) + "\n" +
+ " net decompression rate: " + ((vertexCount - stripCount)/t) +
+ " lines/sec.\n") ;
+ break ;
+ case TYPE_TRIANGLE:
+ System.out.println
+ (" triangles decompressed: " +
+ (vertexCount - 2*stripCount) + "\n" +
+ " net decompression rate: " +
+ ((vertexCount - 2*stripCount)/t) + " triangles/sec.\n") ;
+ break ;
+ }
+ }
+
+ private void printStats() {
+ switch(triOutputType) {
+ case TRI_SET:
+ System.out.println(" using individual triangle output") ;
+ break ;
+ case TRI_STRIP_SET:
+ System.out.println(" using strip output") ;
+ break ;
+ case TRI_STRIP_AND_FAN_SET:
+ System.out.println(" using strips and fans for output") ;
+ break ;
+ case TRI_STRIP_AND_TRI_SET:
+ System.out.println(" using strips and triangles for output") ;
+ break ;
+ }
+
+ System.out.print
+ (" number of Shape3D objects: " + shapes.size() +
+ "\n number of Shape3D decompressed vertices: ") ;
+
+ if (triOutputType == TRI_SET || bufferDataType == TYPE_POINT) {
+ System.out.println(vertexCount) ;
+ }
+ else if (triOutputType == TRI_STRIP_AND_TRI_SET) {
+ System.out.println((vertexCount + triangleCount*3) +
+ "\n number of strips: " + stripCount +
+ "\n number of individual triangles: " +
+ triangleCount) ;
+ if (stripCount > 0)
+ System.out.println
+ (" vertices/strip: " + (float)vertexCount/stripCount +
+ "\n triangles represented in strips: " +
+ (vertexCount - 2*stripCount)) ;
+ }
+ else {
+ System.out.println(vertexCount +
+ "\n number of strips: " + stripCount) ;
+ if (stripCount > 0)
+ System.out.println
+ (" vertices/strip: " + (float)vertexCount/stripCount) ;
+ }
+
+ System.out.print(" vertex data present in last Shape3D: coords") ;
+ if ((vlist.vertexFormat & GeometryArray.NORMALS) != 0)
+ System.out.print(" normals") ;
+
+ if ((vlist.vertexFormat & GeometryArray.COLOR) != 0) {
+ System.out.print(" colors") ;
+ if ((vlist.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ System.out.print(" alpha") ;
+ }
+ System.out.println() ;
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/GeometryLock.java b/src/classes/share/javax/media/j3d/GeometryLock.java
new file mode 100644
index 0000000..34b8378
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryLock.java
@@ -0,0 +1,72 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+
+class GeometryLock {
+
+ // Current thread holding the lock
+ Thread threadId = null;
+
+ // Whether the lock is currently owned
+ boolean lockOwned = false;
+
+ // Count > 1 , if there is nested lock by the same thread
+ int count = 0;
+
+ // Number of outstanding threads waiting for the lock
+ int waiting = 0;
+
+
+ synchronized void getLock() {
+ Thread curThread = Thread.currentThread();
+ // If the thread already has the lock, incr
+ // a count and return
+ if (threadId == curThread) {
+ count++;
+ return;
+ }
+ // Otherwise, wait until the lock is released
+ while (lockOwned) {
+ try {
+ waiting++;
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ waiting--;
+ }
+ count++;
+ // Acquire the lock
+ lockOwned = true;
+ threadId = curThread;
+ }
+
+ synchronized void unLock() {
+ Thread curThread = Thread.currentThread();
+ if (threadId == curThread) {
+ // If the lock count > 0, then return
+ if (--count > 0) {
+ return;
+ }
+ lockOwned = false;
+ threadId = null;
+ if (waiting > 0) {
+ notify();
+ }
+ }
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryRetained.java b/src/classes/share/javax/media/j3d/GeometryRetained.java
new file mode 100644
index 0000000..34b665c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryRetained.java
@@ -0,0 +1,276 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+abstract class GeometryRetained extends NodeComponentRetained {
+
+ static final int GEO_TYPE_NONE = -1;
+
+ static final int GEO_TYPE_QUAD_SET = 1;
+ static final int GEO_TYPE_TRI_SET = 2;
+ static final int GEO_TYPE_POINT_SET = 3;
+ static final int GEO_TYPE_LINE_SET = 4;
+ static final int GEO_TYPE_TRI_STRIP_SET = 5;
+ static final int GEO_TYPE_TRI_FAN_SET = 6;
+ static final int GEO_TYPE_LINE_STRIP_SET = 7;
+
+ static final int GEO_TYPE_INDEXED_QUAD_SET = 8;
+ static final int GEO_TYPE_INDEXED_TRI_SET = 9;
+ static final int GEO_TYPE_INDEXED_POINT_SET = 10;
+ static final int GEO_TYPE_INDEXED_LINE_SET = 11;
+ static final int GEO_TYPE_INDEXED_TRI_STRIP_SET = 12;
+ static final int GEO_TYPE_INDEXED_TRI_FAN_SET = 13;
+ static final int GEO_TYPE_INDEXED_LINE_STRIP_SET = 14;
+
+ static final int GEO_TYPE_RASTER = 15;
+ static final int GEO_TYPE_TEXT3D = 16;
+ static final int GEO_TYPE_COMPRESSED = 17;
+
+ static final int GEO_TYPE_TOTAL = 17;
+ static final int GEO_TYPE_GEOMETRYARRAY = 14;
+
+ BoundingBox geoBounds = new BoundingBox();
+
+ // Indicates whether bounds need to be computed.
+ // Checked when a user does addUser/removeUser and count goes from 0 to one
+ // but geometry has not changed and there is no need to recompute
+ boolean boundsDirty = true; // Changed while holding the geoBounds lock
+
+ int computeGeoBounds = 0; // Changed while holding the geoBounds lock
+
+ // The "type" of this object
+ int geoType = GEO_TYPE_NONE;
+
+ // The id used by the native code when building this object
+ int nativeId = -1;
+
+ // A mask that indicates that something has changed in this object
+ int isDirty = 0xffff;
+
+
+ // Geometry Lock (used only by GeometryArrayRetained and RasterRetained)
+ GeometryLock geomLock = new GeometryLock();
+
+ // Lock used for synchronization of live state
+ Object liveStateLock = new Object();
+
+ abstract void update();
+
+ // A reference to the mirror copy of the geometry
+ GeometryRetained mirrorGeometry = null;
+
+ // indicates whether the geometry in editable
+ boolean isEditable = true;
+
+ // A list of Universes that this Geometry is referenced from
+ ArrayList universeList = new ArrayList();
+
+ // A list of ArrayLists which contain all the Shape3DRetained objects
+ // refering to this geometry. Each list corresponds to the universe
+ // above.
+ ArrayList userLists = new ArrayList();
+
+ // true if color not specified with alpha channel
+ boolean noAlpha = false;
+ static final double EPSILON = 1.0e-6;
+
+ Point3d centroid = new Point3d();
+ boolean recompCentroid = true;
+ // The cached value is evaluated by renderBin and used in determining
+ // whether to put it in display list or not
+ int cachedChangedFrequent = 0;
+
+ static final int POINT_TYPE = 1;
+ static final int LINE_TYPE = 2;
+ static final int TRIANGLE_TYPE = 3;
+ static final int QUAD_TYPE = 4;
+ static final int RASTER_TYPE = 5;
+ static final int TEXT3D_TYPE = 6;
+ static final int COMPRESS_TYPE = 7;
+
+
+ boolean isEquivalenceClass( GeometryRetained geometry ) {
+ int t1 = getClassType();
+ int t2 = geometry.getClassType();
+
+ if (t1 == QUAD_TYPE) {
+ t1 = TRIANGLE_TYPE;
+ }
+ if (t2 == QUAD_TYPE) {
+ t2 = TRIANGLE_TYPE;
+ }
+ return (t1 == t2);
+ }
+
+ void incrComputeGeoBounds() {
+ synchronized(geoBounds) {
+ computeGeoBounds++;
+ // When it goes from zero to one, compute it ..
+ if (computeGeoBounds == 1 && source.isLive()) {
+ computeBoundingBox();
+ }
+ }
+ }
+
+ void decrComputeGeoBounds() {
+ synchronized(geoBounds) {
+ computeGeoBounds--;
+ }
+ }
+
+
+ // This adds a Shape3DRetained to the list of users of this geometry
+ void addUser(Shape3DRetained s) {
+ int index;
+ ArrayList shapeList;
+
+ if (s.sourceNode.boundsAutoCompute) {
+ incrComputeGeoBounds();
+ }
+
+ // If static, no need to maintain a userlist
+ if (this instanceof GeometryArrayRetained) {
+ if (((GeometryArrayRetained)this).isWriteStatic()) {
+ return;
+ }
+ }
+ synchronized (universeList) {
+ if (universeList.contains(s.universe)) {
+ index = universeList.indexOf(s.universe);
+ shapeList = (ArrayList)userLists.get(index);
+ shapeList.add(s);
+ } else {
+ universeList.add(s.universe);
+ shapeList = new ArrayList();
+ shapeList.add(s);
+ userLists.add(shapeList);
+ }
+ }
+
+ }
+
+ // This adds a Shape3DRetained to the list of users of this geometry
+ void removeUser(Shape3DRetained s) {
+ int index;
+ ArrayList shapeList;
+
+ if (s.sourceNode.boundsAutoCompute) {
+ decrComputeGeoBounds();
+ }
+
+ if (this instanceof GeometryArrayRetained) {
+ if (((GeometryArrayRetained)this).isWriteStatic()) {
+ return;
+ }
+ }
+
+ synchronized (universeList) {
+ index = universeList.indexOf(s.universe);
+ shapeList = (ArrayList)userLists.get(index);
+ shapeList.remove(shapeList.indexOf(s));
+ if (shapeList.size() == 0) {
+ userLists.remove(index);
+ universeList.remove(index);
+ }
+ }
+
+ }
+
+ public void updateObject() {
+ this.update();
+ }
+
+
+ abstract void computeBoundingBox();
+
+ void setLive(boolean inBackgroundGroup, int refCount) {
+ doSetLive(inBackgroundGroup,refCount);
+ super.markAsLive();
+ }
+
+ /**
+ * This setLive routine calls the superclass's method when reference
+ * count is 1
+ */
+ void doSetLive(boolean inBackgroundGroup, int refCount) {
+ super.doSetLive(inBackgroundGroup, refCount);
+ this.update();
+ this.computeBoundingBox();
+ }
+
+ abstract void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha,
+ boolean multiScreen, int screen,
+ boolean ignoreVertexColors,
+ int pass);
+
+ /**
+ * This method should return an int indicating the format of the vertices,
+ * if any, stored in the geometry. Instances that can return a valid value
+ * should override this method, otherwise it will be assumed that no
+ * valid vertex components exist.
+ * @return format of vertices in the GeometryRetained as specified by
+ * GeometryArray, if appropriate to this instance.
+ */
+ int getVertexFormat() {
+ return 0 ;
+ }
+
+ abstract boolean intersect(PickShape pickShape, double dist[], Point3d iPnt);
+ abstract boolean intersect(Bounds targetBound);
+ abstract boolean intersect(Point3d[] pnts);
+ abstract boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom);
+
+
+ boolean intersect(Transform3D thisLocalToVworld,
+ Transform3D otherLocalToVworld, GeometryRetained geom) {
+ Transform3D tg = VirtualUniverse.mc.getTransform3D(null);
+ tg.invert(otherLocalToVworld);
+ tg.mul(thisLocalToVworld);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, tg);
+ return intersect(tg, geom);
+ }
+
+
+ boolean intersect(Transform3D thisLocalToVworld, Bounds targetBound) {
+ Bounds transBound = (Bounds) targetBound.clone();
+
+ Transform3D tg = VirtualUniverse.mc.getTransform3D(null);
+ tg.invert(thisLocalToVworld);
+ transBound.transform(tg);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, tg);
+ return intersect(transBound);
+ }
+
+
+ boolean canBeInDisplayList(boolean alphaEditable) {
+
+ return (VirtualUniverse.mc.isDisplayList) &&
+ !(this.isEditable ||
+ (!(this instanceof GeometryArrayRetained) && alphaEditable)||
+ (alphaEditable && ((((GeometryArrayRetained)this).vertexFormat&
+ GeometryArray.COLOR) != 0)) ||
+ (((((GeometryArrayRetained)this).vertexFormat &
+ GeometryArray.BY_REFERENCE) != 0) && !VirtualUniverse.mc.buildDisplayListIfPossible));
+ }
+
+ void computeCentroid() {
+ this.centroid.set(geoBounds.getCenter());
+ }
+
+ abstract int getClassType();
+
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryStripArray.java b/src/classes/share/javax/media/j3d/GeometryStripArray.java
new file mode 100644
index 0000000..feee968
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryStripArray.java
@@ -0,0 +1,223 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The GeometryStripArray object is an abstract class that is extended for
+ * a set of GeometryArray strip primitives. These include LINE_STRIP,
+ * TRIANGLE_STRIP, and TRIANGLE_FAN. In addition to specifying the array
+ * of vertex elements, which is inherited from GeometryArray, the
+ * GeometryStripArray class specifies the number of strips and an
+ * array of per-strip vertex counts that specify where the separate strips
+ * appear in the vertex array.
+ */
+
+public abstract class GeometryStripArray extends GeometryArray {
+
+ // non-public, no parameter constructor
+ GeometryStripArray() {}
+
+ /**
+ * Constructs an empty GeometryStripArray object with the specified
+ * number of vertices, vertex format, and
+ * array of per-strip vertex counts.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2 or TEXTURE_COORDINATE_3, to signal the
+ * inclusion of per-vertex texture coordinates 2D or 3D.
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ * The sum of the elements in this array defines the total number
+ * of valid vertices that are rendered (validVertexCount).
+ *
+ * @exception IllegalArgumentException if
+ * <code>validVertexCount > vertexCount</code>
+ */
+ public GeometryStripArray(int vertexCount,
+ int vertexFormat,
+ int[] stripVertexCounts) {
+
+ super(vertexCount, vertexFormat);
+ ((GeometryStripArrayRetained)this.retained).setStripVertexCounts(stripVertexCounts);
+ }
+
+ /**
+ * Constructs an empty GeometryStripArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and
+ * array of per-strip vertex counts.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2 or TEXTURE_COORDINATE_3, to signal the
+ * inclusion of per-vertex texture coordinates 2D or 3D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code> or
+ * <code>TEXTURE_COORDINATE_3</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code> or
+ * <code>TEXTURE_COORDINATE_3</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ * The sum of the elements in this array defines the total number
+ * of valid vertices that are rendered (validVertexCount).
+ *
+ * @exception IllegalArgumentException if
+ * <code>validVertexCount > vertexCount</code>
+ *
+ * @since Java 3D 1.2
+ */
+ public GeometryStripArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int[] stripVertexCounts) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap);
+ ((GeometryStripArrayRetained)this.retained).setStripVertexCounts(stripVertexCounts);
+ }
+
+ /**
+ * Get number of strips in the GeometryStripArray.
+ * @return numStrips number of strips
+ */
+ public int getNumStrips() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(GeometryArray.ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryStripArray0"));
+
+ return ((GeometryStripArrayRetained)this.retained).getNumStrips();
+ }
+
+ /**
+ * Sets the array of strip vertex counts. The length of this
+ * array is the number of separate strips. The elements in this
+ * array specify the number of vertices for each separate strip.
+ * The sum of the elements in this array defines the total number
+ * of valid vertices that are rendered (validVertexCount).
+ *
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ *
+ * @exception IllegalArgumentException if any of the following are
+ * true:
+ * <ul>
+ * <code>initialVertexIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialCoordIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialColorIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialNormalIndex + validVertexCount > vertexCount</code>,<br>
+ * <code>initialTexCoordIndex + validVertexCount > vertexCount</code>
+ * </ul>
+ * <p>
+ *
+ * @exception ArrayIndexOutOfBoundsException if the geometry data format
+ * is <code>BY_REFERENCE</code> and any the following
+ * are true for non-null array references:
+ * <ul>
+ * <code>CoordRef.length</code> < <i>num_words</i> *
+ * (<code>initialCoordIndex + validVertexCount</code>)<br>
+ * <code>ColorRef.length</code> < <i>num_words</i> *
+ * (<code>initialColorIndex + validVertexCount</code>)<br>
+ * <code>NormalRef.length</code> < <i>num_words</i> *
+ * (<code>initialNormalIndex + validVertexCount</code>)<br>
+ * <code>TexCoordRef.length</code> < <i>num_words</i> *
+ * (<code>initialTexCoordIndex + validVertexCount</code>)<br>
+ * </ul>
+ * where <i>num_words</i> depends on which variant of
+ * <code>set</code><i>Array</i><code>Ref</code> is used.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setStripVertexCounts(int[] stripVertexCounts) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(GeometryArray.ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryStripArray2"));
+
+ ((GeometryStripArrayRetained)this.retained).setStripVertexCounts(stripVertexCounts);
+
+ }
+
+ /**
+ * Get a list of vertexCounts for each strip. The list is copied
+ * into the specified array. The array must be large enough to hold
+ * all of the ints.
+ * @param stripVertexCounts an array that will receive vertexCounts
+ */
+ public void getStripVertexCounts(int[] stripVertexCounts) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(GeometryArray.ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("GeometryStripArray1"));
+
+ ((GeometryStripArrayRetained)this.retained).getStripVertexCounts(stripVertexCounts);
+ }
+
+ /**
+ * This method is not supported for geometry strip arrays.
+ * The sum of the elements in the strip vertex counts array
+ * defines the valid vertex count.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setValidVertexCount(int validVertexCount) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java b/src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java
new file mode 100644
index 0000000..3f4dffd
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java
@@ -0,0 +1,739 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+import java.util.Vector;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+
+
+/**
+ * The GeometryStripArray object is an abstract class that is extended for
+ * a set of GeometryArray strip primitives. These include LINE_STRIP,
+ * TRIANGLE_STRIP, and TRIANGLE_FAN.
+ */
+
+abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
+
+ // Array of per-strip vertex counts
+ int stripVertexCounts[];
+
+ // Array of per-strip starting index
+ int stripStartVertexIndices[]; // start of vertices for both by-copy
+ // and by-ref
+ int stripStartOffsetIndices[]; // Used in byRef non_interleaved
+
+ // Following variables are only used in the compile mode
+ // isCompiled = true
+ int[] compileNumStrips;
+ int[] compileStripCountOffset;
+
+
+ /**
+ * Set stripVertexCount data into local array
+ */
+ void setStripVertexCounts(int stripVertexCounts[]){
+ boolean nullGeo = false;
+
+ int i, num = stripVertexCounts.length, total = 0;
+ for (i=0; i < num; i++) {
+ total += stripVertexCounts[i];
+ if (this instanceof LineStripArrayRetained) {
+ if (stripVertexCounts[i] < 2) {
+ throw new IllegalArgumentException(J3dI18N.getString("LineStripArrayRetained1"));
+ }
+ }
+ else if (this instanceof TriangleStripArrayRetained) {
+ if (stripVertexCounts[i] < 3) {
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleStripArrayRetained1"));
+ }
+ }
+ else if (this instanceof TriangleFanArrayRetained) {
+ if (stripVertexCounts[i] < 3) {
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleFanArrayRetained1"));
+ }
+ }
+ }
+
+ if ((initialVertexIndex + total) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray3"));
+ }
+ else if ((initialCoordIndex + total) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray7"));
+ }
+ else if ((initialColorIndex + total) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray4"));
+ }
+ else if ((initialNormalIndex + total) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray5"));
+ }
+ else {
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) {
+ for (i = 0; i < texCoordSetCount; i++) {
+ if ((initialTexCoordIndex[i] + total) > vertexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString(
+ "GeometryStripArray6"));
+ }
+ }
+ }
+ }
+ }
+ geomLock.getLock();
+ dirtyFlag |= STRIPCOUNT_CHANGED;
+ validVertexCount = total;
+ this.stripVertexCounts = new int[num];
+ stripStartVertexIndices = new int[num];
+ stripStartOffsetIndices = new int[num];
+ stripStartOffsetIndices[0] = 0;
+ if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) {
+ stripStartVertexIndices[0] = initialCoordIndex;
+ nullGeo = ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
+ }
+ else {
+ stripStartVertexIndices[0] = initialVertexIndex;
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ if (( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ nullGeo = (interLeavedVertexData == null);
+ }
+ else {
+ nullGeo = (interleavedFloatBufferImpl == null);
+ }
+ }
+ }
+
+ for (i=0; i<num-1; i++) {
+ this.stripVertexCounts[i] = stripVertexCounts[i];
+ stripStartVertexIndices[i+1] = stripStartVertexIndices[i] +
+ stripVertexCounts[i];
+ stripStartOffsetIndices[i+1] = stripStartOffsetIndices[i]+stripVertexCounts[i];
+ }
+ this.stripVertexCounts[num-1] = stripVertexCounts[num-1];
+
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ processCoordsChanged(nullGeo);
+ sendDataChangedMessage(true);
+ }
+
+ }
+ void unIndexify(IndexedGeometryStripArrayRetained src) {
+ if ((src.vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ unIndexifyJavaArray(src);
+ }
+ else {
+ unIndexifyNIOBuffer(src);
+ }
+ }
+ void unIndexifyJavaArray(IndexedGeometryStripArrayRetained src) {
+ int vOffset = 0, srcOffset, tOffset = 0;
+ int base = src.initialIndexIndex;
+ int i,j, k, index, colorStride = 0;
+ float[] vdata = null;
+
+ if (((src.vertexFormat & GeometryArray.BY_REFERENCE) == 0) ||
+ ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0)) {
+
+ if ((src.vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ vdata = src.vertexData;
+ if ((src.vertexFormat & GeometryArray.COLOR) != 0)
+ colorStride = 4;
+ }
+ else if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ vdata = src.interLeavedVertexData;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ colorStride = 4;
+ else if ((src.vertexFormat & GeometryArray.COLOR) != 0)
+ colorStride = 3;
+ }
+
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j + base;
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ System.arraycopy(vdata,
+ src.indexNormal[index]*src.stride + src.normalOffset,
+ vertexData, vOffset + normalOffset, 3);
+ }
+ if (colorStride == 4) {
+ /*
+ System.out.println("vdata.length = "+vdata.length);
+ System.out.println("vertexData.length = "+vertexData.length);
+ System.out.println("src.stride = "+src.stride);
+ System.out.println("src.colorOffset = "+src.colorOffset);
+ System.out.println("index = "+index+" src.indexColor.length = "+src.indexColor.length);
+ System.out.println("src.indexColor[index] = "+src.indexColor[index]);
+ System.out.println("base = "+base);
+ */
+ System.arraycopy(vdata,
+ src.indexColor[index]*src.stride + src.colorOffset,
+ vertexData, vOffset + colorOffset, colorStride);
+ } else if (colorStride == 3) {
+ System.arraycopy(vdata,
+ src.indexColor[index]*src.stride + src.colorOffset,
+ vertexData, vOffset + colorOffset, colorStride);
+ vertexData[vOffset + colorOffset + 3] = 1.0f;
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (k = 0; k < texCoordSetCount; k++) {
+ System.arraycopy(vdata,
+ (((int[])src.indexTexCoord[k])[index])
+ *src.stride + src.textureOffset +
+ src.texCoordSetMapOffset[k],
+ vertexData,
+ vOffset + textureOffset +
+ texCoordSetMapOffset[k],
+ texCoordStride);
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ System.arraycopy(vdata,
+ src.indexCoord[index]*src.stride + src.coordinateOffset,
+ vertexData, vOffset + coordinateOffset, 3);
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ }
+ else {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ base = src.initialIndexIndex;
+ vOffset = normalOffset;
+ switch ((src.vertexType & NORMAL_DEFINED)) {
+ case NF:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+ System.arraycopy(src.floatRefNormals,
+ src.indexNormal[index]*3,
+ vertexData,
+ vOffset, 3);
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case N3F:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexNormal[j+base];
+ vertexData[vOffset] = src.v3fRefNormals[index].x;
+ vertexData[vOffset+1] = src.v3fRefNormals[index].y;
+ vertexData[vOffset+2] = src.v3fRefNormals[index].z;
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.COLOR) != 0){
+ base = src.initialIndexIndex;
+ vOffset = colorOffset;
+ int multiplier = 3;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ multiplier = 4;
+
+ switch ((src.vertexType & COLOR_DEFINED)) {
+ case CF:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ System.arraycopy(src.floatRefColors,
+ src.indexColor[index]*multiplier,
+ vertexData,
+ vOffset, 4);
+ }
+ else {
+ System.arraycopy(src.floatRefColors,
+ src.indexColor[index]*multiplier,
+ vertexData,
+ vOffset, 3);
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case CUB:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexColor[j+base] * multiplier;
+ vertexData[vOffset] = (src.byteRefColors[index] & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.byteRefColors[index+1] & 0xff) * ByteToFloatScale;;
+ vertexData[vOffset+2] = (src.byteRefColors[index+2] & 0xff) * ByteToFloatScale;;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ vertexData[vOffset+3] = (src.byteRefColors[index+3] & 0xff) * ByteToFloatScale;
+ }
+ else {
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case C3F:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexColor[j+base];
+ vertexData[vOffset] = src.c3fRefColors[index].x;
+ vertexData[vOffset+1] = src.c3fRefColors[index].y;
+ vertexData[vOffset+2] = src.c3fRefColors[index].z;
+ vertexData[vOffset+3] = 1.0f;
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case C4F:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexColor[j+base];
+ vertexData[vOffset] = src.c4fRefColors[index].x;
+ vertexData[vOffset+1] = src.c4fRefColors[index].y;
+ vertexData[vOffset+2] = src.c4fRefColors[index].z;
+ vertexData[vOffset+3] = src.c4fRefColors[index].w;
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case C3UB:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexColor[j+base];
+ vertexData[vOffset] = (src.c3bRefColors[index].x & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.c3bRefColors[index].y & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+2] = (src.c3bRefColors[index].z & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+3] = 1.0f;
+
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case C4UB:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexColor[j+base];
+ vertexData[vOffset] = (src.c4bRefColors[index].x & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.c4bRefColors[index].y & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+2] = (src.c4bRefColors[index].z & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+3] = (src.c4bRefColors[index].w & 0xff) * ByteToFloatScale;
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ base = src.initialIndexIndex;
+ vOffset = textureOffset;
+ switch ((src.vertexType & TEXCOORD_DEFINED)) {
+ case TF:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+
+ for (k = 0, tOffset = vOffset;
+ k < texCoordSetCount; k++) {
+ System.arraycopy(src.refTexCoords[k],
+ ((int[])src.indexTexCoord[k])[index]
+ *texCoordStride,
+ vertexData, tOffset, texCoordStride);
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case T2F:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+ for (k = 0, tOffset = vOffset;
+ k < texCoordSetCount; k++) {
+ srcOffset =
+ ((int[])src.indexTexCoord[k])[index];
+ vertexData[tOffset] = ((TexCoord2f[])
+ src.refTexCoords[k])[srcOffset].x;
+ vertexData[tOffset+1] = ((TexCoord2f[])
+ src.refTexCoords[k])[srcOffset].y;
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case T3F:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+ for (k = 0, tOffset = vOffset;
+ k < texCoordSetCount; k++) {
+ srcOffset =
+ ((int[])src.indexTexCoord[k])[index];
+ vertexData[tOffset] = ((TexCoord3f[])
+ src.refTexCoords[k])[srcOffset].x;
+ vertexData[tOffset+1] = ((TexCoord3f[])
+ src.refTexCoords[k])[srcOffset].y;
+ vertexData[tOffset+2] = ((TexCoord3f[])
+ src.refTexCoords[k])[srcOffset].z;
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ vOffset = coordinateOffset;
+ base = src.initialIndexIndex;
+ switch ((src.vertexType & VERTEX_DEFINED)) {
+ case PF:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+ System.arraycopy(src.floatRefCoords,
+ src.indexCoord[index]*3,
+ vertexData,
+ vOffset, 3);
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case PD:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexCoord[j+base] * 3;
+ vertexData[vOffset] = (float)src.doubleRefCoords[index];
+ vertexData[vOffset+1] = (float)src.doubleRefCoords[index+1];
+ vertexData[vOffset+2] = (float)src.doubleRefCoords[index+2];
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case P3F:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexCoord[j+base];
+ vertexData[vOffset] = src.p3fRefCoords[index].x;
+ vertexData[vOffset+1] = src.p3fRefCoords[index].y;
+ vertexData[vOffset+2] = src.p3fRefCoords[index].z;
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case P3D:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexCoord[j+base];
+ vertexData[vOffset] = (float)src.p3dRefCoords[index].x;
+ vertexData[vOffset+1] = (float)src.p3dRefCoords[index].y;
+ vertexData[vOffset+2] = (float)src.p3dRefCoords[index].z;
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ void unIndexifyNIOBuffer(IndexedGeometryStripArrayRetained src) {
+ int vOffset = 0, srcOffset, tOffset = 0;
+ int base = src.initialIndexIndex;
+ int i,j, k, index, colorStride = 0;
+
+
+ // interleaved case
+ if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ colorStride = 4;
+ else if ((src.vertexFormat & GeometryArray.COLOR) != 0)
+ colorStride = 3;
+
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j + base;
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ src.interleavedFloatBufferImpl.position(src.indexNormal[index]*src.stride + src.normalOffset);
+ src.interleavedFloatBufferImpl.get(vertexData, vOffset + normalOffset, 3);
+ }
+ if (colorStride == 4) {
+ src.interleavedFloatBufferImpl.position(src.indexColor[index]*src.stride + src.colorOffset);
+ src.interleavedFloatBufferImpl.get(vertexData, vOffset + colorOffset, colorStride);
+ } else if (colorStride == 3) {
+ src.interleavedFloatBufferImpl.position(src.indexColor[index]*src.stride + src.colorOffset);
+ src.interleavedFloatBufferImpl.get(vertexData, vOffset + colorOffset, colorStride);
+ vertexData[vOffset + colorOffset + 3] = 1.0f;
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (k = 0; k < texCoordSetCount; k++) {
+ src.interleavedFloatBufferImpl.position((((int[])src.indexTexCoord[k])[index])
+ *src.stride + src.textureOffset +
+ src.texCoordSetMapOffset[k]);
+
+ src.interleavedFloatBufferImpl.get(vertexData,
+ vOffset + textureOffset + texCoordSetMapOffset[k], texCoordStride);
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ src.interleavedFloatBufferImpl.position(src.indexCoord[index]*src.stride + src.coordinateOffset);
+ src.interleavedFloatBufferImpl.get( vertexData, vOffset + coordinateOffset, 3);
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ }
+ else {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0){
+ base = src.initialIndexIndex;
+ vOffset = normalOffset;
+ if((src.vertexType & NORMAL_DEFINED) != 0) {
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+ src.floatBufferRefNormals.position(src.indexNormal[index]*3);
+ src.floatBufferRefNormals.get(vertexData, vOffset, 3);
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ }
+ }
+ if ((vertexFormat & GeometryArray.COLOR) != 0){
+ base = src.initialIndexIndex;
+ vOffset = colorOffset;
+ int multiplier = 3;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
+ multiplier = 4;
+
+ switch ((src.vertexType & COLOR_DEFINED)) {
+ case CF:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ src.floatBufferRefColors.position(src.indexColor[index]*multiplier);
+ src.floatBufferRefColors.get(vertexData, vOffset, 4);
+
+ }
+ else {
+ src.floatBufferRefColors.position(src.indexColor[index]*multiplier);
+ src.floatBufferRefColors.get(vertexData, vOffset, 3);
+
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case CUB:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexColor[j+base] * multiplier;
+ vertexData[vOffset] = (src.byteBufferRefColors.get(index) & 0xff) * ByteToFloatScale;
+ vertexData[vOffset+1] = (src.byteBufferRefColors.get(index+1) & 0xff) * ByteToFloatScale;;
+ vertexData[vOffset+2] = (src.byteBufferRefColors.get(index+2) & 0xff) * ByteToFloatScale;;
+ if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
+ vertexData[vOffset+3] = (src.byteBufferRefColors.get(index+3) & 0xff) * ByteToFloatScale;
+ }
+ else {
+ vertexData[vOffset+3] = 1.0f;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ base = src.initialIndexIndex;
+ vOffset = textureOffset;
+ FloatBufferWrapper texBuffer;
+ if((src.vertexType & TEXCOORD_DEFINED) != 0) {
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+
+ for (k = 0, tOffset = vOffset;
+ k < texCoordSetCount; k++) {
+ texBuffer = (FloatBufferWrapper)(((J3DBuffer) (src.refTexCoordsBuffer[k])).getBufferImpl());
+ texBuffer.position(((int[])src.indexTexCoord[k])[index]*texCoordStride);
+ texBuffer.get(vertexData, tOffset, texCoordStride);
+ tOffset += texCoordStride;
+ }
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ }
+ }
+ if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+ vOffset = coordinateOffset;
+ base = src.initialIndexIndex;
+ switch ((src.vertexType & VERTEX_DEFINED)) {
+ case PF:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = j+base;
+ src.floatBufferRefCoords.position(src.indexCoord[index]*3);
+ src.floatBufferRefCoords.get(vertexData, vOffset, 3);
+
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+ case PD:
+ for (i=0; i < src.stripIndexCounts.length; i++) {
+ for (j=0; j < src.stripIndexCounts[i]; j++) {
+ index = src.indexCoord[j+base] * 3;
+ vertexData[vOffset] = (float)src.doubleBufferRefCoords.get(index);
+ vertexData[vOffset+1] = (float)src.doubleBufferRefCoords.get(index+1);
+ vertexData[vOffset+2] = (float)src.doubleBufferRefCoords.get(index+2);
+ vOffset += stride;
+ }
+ base += src.stripIndexCounts[i];
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Get number of strips in the GeometryStripArray
+ * @return numStrips number of strips
+ */
+ int getNumStrips(){
+ return stripVertexCounts.length;
+ }
+
+ /**
+ * Get a list of vertexCounts for each strip
+ * @param stripVertexCounts an array that will receive vertexCounts
+ */
+ void getStripVertexCounts(int stripVertexCounts[]){
+
+ int i, num = this.stripVertexCounts.length;
+
+ for (i=0;i < num;i++)
+ {
+ stripVertexCounts[i] = this.stripVertexCounts[i];
+ }
+
+ }
+
+ void getStripVertexCounts(int id, int counts[]) {
+ int stripOffset = compileStripCountOffset[id];
+ int stripLength = compileNumStrips[id];
+ System.arraycopy(stripVertexCounts, stripOffset, counts, 0,stripLength);
+ }
+
+
+ int getNumStrips(int id) {
+ return compileNumStrips[id];
+ }
+
+ // Called only for "by-copy" geometry
+ void mergeGeometryArrays(ArrayList list) {
+ int numMerge = list.size();
+ int numStrips = 0;
+
+
+ for (int i = 0; i < numMerge; i++) {
+ numStrips +=
+ ((GeometryStripArrayRetained)list.get(i)).stripVertexCounts.length;
+ }
+ stripVertexCounts = new int[numStrips];
+ stripStartVertexIndices = new int[numStrips];
+ stripStartOffsetIndices = new int[numStrips];
+ int curStripOffset = 0;
+ int curStripIndexOffset = 0,stripLength;
+ int[] curStripVertexCounts;
+ int[] curStripStartIndices ;
+ int[] curStripOffsetIndices ;
+
+
+ compileNumStrips = new int[numMerge];
+ compileStripCountOffset = new int[numMerge];
+ for (int i = 0; i < numMerge; i++) {
+ GeometryStripArrayRetained strip =
+ (GeometryStripArrayRetained)list.get(i);
+ curStripVertexCounts = strip.stripVertexCounts;
+ curStripStartIndices = strip.stripStartVertexIndices;
+ curStripOffsetIndices = strip.stripStartOffsetIndices;
+ stripLength = curStripVertexCounts.length;
+ compileNumStrips[i] = stripLength;
+ compileStripCountOffset[i] = curStripOffset;
+ System.arraycopy(curStripVertexCounts, 0, stripVertexCounts,
+ curStripOffset, stripLength);
+ // Can't just copy StartIndices, have to update to reflect
+ // updated vertex position on the merged vertexData
+ for (int j = 0; j < stripLength; j++) {
+ stripStartVertexIndices[j+curStripOffset] = curStripStartIndices[j] +
+ curStripIndexOffset;
+ stripStartOffsetIndices[j+curStripOffset] = curStripOffsetIndices[j] +
+ curStripIndexOffset;
+ }
+ curStripOffset += stripLength;
+ curStripIndexOffset += strip.validVertexCount;
+ }
+ // Assign the merged validVertexCount
+ validVertexCount = curStripIndexOffset;
+
+ // call the super to merge the vertex data
+ super.mergeGeometryArrays(list);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryStructure.java b/src/classes/share/javax/media/j3d/GeometryStructure.java
new file mode 100644
index 0000000..2bdef2a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryStructure.java
@@ -0,0 +1,1157 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+
+/**
+ * A geometry structure is a object that organizes geometries
+ * and bounds.
+ */
+
+class GeometryStructure extends J3dStructure {
+ /**
+ * used during Transform Processing
+ */
+ UpdateTargets targets = null;
+
+ /**
+ * A multiple read single write Lock to sychronize access into this
+ * GeometryStructure.
+ * To prevent deadlock a call to read/write lock must end with a read/write
+ * unlock respectively.
+ */
+ private MRSWLock lock = null;
+
+ /**
+ * A lock object to prevent concurrent getVisibleBHTree query.
+ */
+ private Object visLock = new Object();
+
+ /**
+ * A lock object to prevent concurrent collideEntryList,
+ * collideExitList using toArray() in BehaviorStructure buildTree()
+ * while clearMirror() is invoked in GeometryStructure removeNode()
+ */
+ private Object collideListLock = new Object();
+
+ /**
+ * Binary Hull Tree structure for handling geometry atoms.
+ * Do not change the following private variables to public, their access
+ * need to synchronize via lock.
+ */
+
+ private BHTree[] bhTreeArr = null;
+ private int bhTreeCount;
+ private int bhTreeMax;
+ private int bhTreeBlockSize = 5;
+
+ /**
+ * The array of BHNode, a data pool, for passing data between GS and BHTrees.
+ * Do not change the following private variables to public, their access
+ * need to synchronize via lock.
+ */
+ private BHNode[] bhNodeArr = null;
+ private int bhNodeCount, bhNodeMax;
+ private int bhNodeBlockSize = 50;
+
+ // Support for multi-locale.
+ private Vector3d localeTrans = new Vector3d();
+
+
+ //The lists of wakeupCriterion object currently in collision.
+ WakeupIndexedList collideEntryList;
+ WakeupIndexedList collideExitList;
+ WakeupIndexedList collideMovementList;
+
+ // The lists of wakeupCriterion objects that GeometryStructure keeps
+ WakeupIndexedList wakeupOnCollisionEntry;
+ WakeupIndexedList wakeupOnCollisionExit;
+ WakeupIndexedList wakeupOnCollisionMovement;
+
+ // When Shape insert/remove for WakeupOnCollisionxxx() using
+ // Group node and USE_GEOMETRY, we need to reevaluate the
+ // cache geometryAtoms list.
+ boolean reEvaluateWakeupCollisionGAs;
+
+ private boolean transformMsg = false;
+
+ /**
+ * Constructor.
+ */
+ GeometryStructure(VirtualUniverse u) {
+ super(u, J3dThread.UPDATE_GEOMETRY);
+ bhNodeCount = 0;
+ bhNodeMax = bhNodeBlockSize;
+ bhNodeArr = new BHNode[bhNodeMax];
+ bhTreeMax = 1;
+ bhTreeArr = new BHTree[bhTreeMax];
+ bhTreeCount=0;
+ lock = new MRSWLock();
+ collideEntryList = new WakeupIndexedList(WakeupOnCollisionEntry.class,
+ WakeupOnCollisionEntry.COLLIDEENTRY_IN_BS_LIST, u);
+ collideExitList = new WakeupIndexedList(WakeupOnCollisionExit.class,
+ WakeupOnCollisionExit.COLLIDEEXIT_IN_BS_LIST, u);
+ collideMovementList = new WakeupIndexedList(WakeupOnCollisionMovement.class,
+ WakeupOnCollisionMovement.COLLIDEMOVE_IN_BS_LIST, u);
+ wakeupOnCollisionEntry = new WakeupIndexedList(WakeupOnCollisionEntry.class,
+ WakeupOnCollisionEntry.COND_IN_GS_LIST, u);
+ wakeupOnCollisionExit = new WakeupIndexedList(WakeupOnCollisionExit.class,
+ WakeupOnCollisionExit.COND_IN_GS_LIST, u);
+ wakeupOnCollisionMovement = new WakeupIndexedList(WakeupOnCollisionMovement.class,
+ WakeupOnCollisionMovement.COND_IN_GS_LIST, u);
+ }
+
+ void processMessages(long referenceTime) {
+ J3dMessage m;
+ J3dMessage[] messages = getMessages(referenceTime);
+ int nMsg = getNumMessage();
+
+ if (nMsg > 0) {
+ reEvaluateWakeupCollisionGAs = false;
+ for (int i=0; i < nMsg; i++) {
+ lock.writeLock();
+ m = messages[i];
+ switch (m.type) {
+ case J3dMessage.TRANSFORM_CHANGED:
+ transformMsg = true;
+ break;
+ case J3dMessage.SWITCH_CHANGED:
+ processSwitchChanged(m);
+ // may need to process dirty switched-on transform
+ if (universe.transformStructure.getLazyUpdate()) {
+ transformMsg = true;
+ }
+ break;
+ case J3dMessage.INSERT_NODES:
+ insertNodes((Object[])m.args[0]);
+ reEvaluateWakeupCollisionGAs = true;
+ break;
+ case J3dMessage.REMOVE_NODES:
+ removeNodes(m);
+ reEvaluateWakeupCollisionGAs = true;
+ break;
+ case J3dMessage.SHAPE3D_CHANGED: {
+ int comp = ((Integer)m.args[1]).intValue();
+ if (comp == Shape3DRetained.GEOMETRY_CHANGED) {
+ m.args[0] = m.args[2];
+ removeNodes(m);
+ insertNodes((Object[])m.args[3]);
+ reEvaluateWakeupCollisionGAs = true;
+ }
+ else if (comp == Shape3DRetained.APPEARANCE_CHANGED) {
+ processVisibleChanged(m.args[2],
+ ((GeometryAtom[]) m.args[3]));
+ }
+ break;
+ }
+ case J3dMessage.TEXT3D_DATA_CHANGED:
+ removeNodes(m);
+ insertNodes((Object[])m.args[1]);
+ break;
+ case J3dMessage.TEXT3D_TRANSFORM_CHANGED:
+ processBoundsChanged((Object []) m.args[0], false);
+ break;
+ case J3dMessage.MORPH_CHANGED: {
+ int comp = ((Integer)m.args[1]).intValue();
+ if (comp == MorphRetained.GEOMETRY_CHANGED) {
+ // TODO: Optimize this case.
+ processBoundsChanged((Object []) m.args[3], false);
+ }
+ else if (comp == MorphRetained.APPEARANCE_CHANGED) {
+ processVisibleChanged(m.args[2],
+ ((GeometryAtom[]) m.args[3]));
+ }
+ break;
+ }
+ case J3dMessage.REGION_BOUND_CHANGED:
+ case J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED:
+ // Only set this flag, when bounds might be empty.
+ processBoundsChanged((Object [])m.args[0], false);
+ break;
+ case J3dMessage.GEOMETRY_CHANGED:
+ // System.out.println("J3dMessage.GEOMETRY_CHANGED");
+ processBoundsChanged((Object []) m.args[0], false);
+ break;
+ case J3dMessage.RENDERINGATTRIBUTES_CHANGED:
+ processVisibleChanged(m.args[2],
+ ((GeometryAtom[]) m.args[3]));
+ break;
+ }
+
+ lock.writeUnlock();
+ m.decRefcount();
+ }
+
+ if (transformMsg) {
+ targets = universe.transformStructure.getTargetList();
+ lock.writeLock();
+
+ processTransformChanged(targets);
+
+ lock.writeUnlock();
+
+ transformMsg = false;
+ targets = null;
+ }
+
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ processCollisionDetection();
+ }
+
+
+ private int getBHTreeIndex(Locale locale) {
+ int i;
+
+ for (i=0; i< bhTreeCount; i++) {
+ if (bhTreeArr[i].locale == locale)
+ return i;
+ }
+ // Can't find will return -1 so that other
+ // program know this
+ return -1;
+ }
+
+ private int getOrAddBHTreeIndex(Locale locale) {
+ int i;
+
+ for (i=0; i< bhTreeCount; i++) {
+ if (bhTreeArr[i].locale == locale)
+ return i;
+ }
+
+ if (bhTreeCount >= bhTreeMax) {
+ // allocate a bigger array here....
+ if (J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2,
+ "Expanding bhTreeArr array ...\n");
+ bhTreeMax += bhTreeBlockSize;
+ BHTree[] oldBhTreeArr = bhTreeArr;
+
+ bhTreeArr = new BHTree[bhTreeMax];
+ System.arraycopy(oldBhTreeArr, 0, bhTreeArr, 0, oldBhTreeArr.length);
+ }
+
+ bhTreeArr[bhTreeCount] = new BHTree(locale);
+ bhTreeCount++;
+ return i;
+ }
+
+ private void clearBhNodeArr() {
+ bhNodeCount = 0;
+ }
+
+ private void addToBhNodeArr(BHNode bhNode) {
+
+ // Add to bhNodeArr.
+ if (bhNodeCount >= bhNodeMax) {
+ bhNodeMax += bhNodeBlockSize;
+ BHNode[] oldbhNodeArr = bhNodeArr;
+
+ bhNodeArr = new BHNode[bhNodeMax];
+ System.arraycopy(oldbhNodeArr, 0, bhNodeArr, 0, oldbhNodeArr.length);
+ }
+
+ bhNodeArr[bhNodeCount] = bhNode;
+ bhNodeCount++;
+ }
+
+ private void processVisibleChanged(Object valueObj, GeometryAtom[] gaArr) {
+ boolean visible = true; // Default is true.
+ int i, treeIndex;
+
+ if ((gaArr == null) || (gaArr.length < 1))
+ return;
+
+ treeIndex = getBHTreeIndex(gaArr[0].locale);
+
+ visible = ((Boolean)valueObj).booleanValue();
+
+ for ( i=gaArr.length-1; i>=0; i--) {
+ gaArr[i].visible = visible;
+ }
+
+ }
+
+ private void insertNodes(Object[] nodes) {
+ Object node;
+ GeometryAtom geomAtom;
+ BHTree currTree = null;
+
+ clearBhNodeArr();
+
+ // System.out.println("GS : nodes.length is " + nodes.length);
+
+ for (int i=0; i<nodes.length; i++) {
+ node = nodes[i];
+ if (node instanceof GeometryAtom) {
+ synchronized (node) {
+ geomAtom = (GeometryAtom) node;
+ if (geomAtom.source.inBackgroundGroup) {
+ geomAtom.source.geometryBackground.
+ addBgGeometryAtomList(geomAtom);
+ continue;
+ }
+ BHLeafNode bhLeafNode = (BHLeafNode)
+ VirtualUniverse.mc.getBHNode(BHNode.BH_TYPE_LEAF);
+ bhLeafNode.leafIF = geomAtom;
+ geomAtom.bhLeafNode = bhLeafNode;
+ bhLeafNode.computeBoundingHull();
+ // System.out.println("bhLeafNode.bHull is " + bhLeafNode.bHull);
+ addToBhNodeArr(bhLeafNode);
+ }
+ } else if (node instanceof GroupRetained) {
+ synchronized (node) {
+ GroupRetained group = (GroupRetained) node;
+ BHLeafNode bhLeafNode = (BHLeafNode)
+ VirtualUniverse.mc.getBHNode(BHNode.BH_TYPE_LEAF);
+ bhLeafNode.leafIF = group;
+ group.bhLeafNode = bhLeafNode;
+ bhLeafNode.computeBoundingHull();
+ addToBhNodeArr(bhLeafNode);
+ }
+ }
+ }
+
+ if (bhNodeCount < 1) {
+ return;
+ }
+
+ // Look for the right BHTree to insert to.
+ if (currTree == null) {
+ // We must separate the following two calls
+ // since the first Call will allocate storage bhTreeArr
+ // for the second index operation. (see bug 4361998)
+ int idx = getOrAddBHTreeIndex(((BHLeafNode)bhNodeArr[0]).getLocale());
+ currTree = bhTreeArr[idx];
+
+ }
+
+ currTree.insert(bhNodeArr, bhNodeCount);
+
+ // currTree.gatherTreeStatistics();
+ }
+
+ void removeNodes(J3dMessage m) {
+ Object[] nodes = (Object[]) m.args[0];
+ BHTree currTree = null;
+ Object node;
+ int index;
+
+ clearBhNodeArr();
+ for (int i=0; i<nodes.length; i++) {
+ node = nodes[i];
+ if (node instanceof GeometryAtom) {
+ synchronized (node) {
+ GeometryAtom geomAtom = (GeometryAtom) node;
+ if ((geomAtom.source != null) &&
+ (geomAtom.source.inBackgroundGroup)) {
+ geomAtom.source.geometryBackground.
+ removeBgGeometryAtomList(geomAtom);
+ continue;
+ }
+ if (geomAtom.bhLeafNode != null) {
+ addToBhNodeArr(geomAtom.bhLeafNode);
+ // Dereference BHLeafNode in GeometryAtom.
+ geomAtom.bhLeafNode = null;
+ }
+
+ }
+ } else if (node instanceof GroupRetained) {
+ if (((NodeRetained)node).nodeType != NodeRetained.ORDEREDGROUP) {
+ synchronized (node) {
+ GroupRetained group = (GroupRetained) node;
+ if (group.bhLeafNode != null) {
+ addToBhNodeArr(group.bhLeafNode);
+ // Dereference BHLeafNode in GroupRetained
+ group.bhLeafNode = null;
+ }
+ }
+ }
+ } else if (node instanceof BehaviorRetained) {
+ synchronized (node) {
+ BehaviorRetained behav = (BehaviorRetained) node;
+ // cleanup collideEntryList & collideExitList
+ // since we didn't remove
+ // it on remove in removeWakeupOnCollision()
+
+ // Note that GeometryStructure may run in
+ // parallel with BehaviorStructure when
+ // BS invoke activateBehaviors() to buildTree()
+ // which in turn call addWakeupOnCollision()
+ // to modify collideEntryList at the same time.
+
+ WakeupOnCollisionEntry wentry;
+ WakeupOnCollisionEntry wentryArr[] =
+ (WakeupOnCollisionEntry []) collideEntryList.toArray();
+ for (int j=collideEntryList.arraySize()-1; j>=0; j--) {
+ wentry = wentryArr[j];
+ if (wentry.behav == behav) {
+ collideEntryList.remove(wentry);
+ }
+ }
+ WakeupOnCollisionExit wexit;
+ WakeupOnCollisionExit wexitArr[] =
+ (WakeupOnCollisionExit []) collideExitList.toArray();
+ for (int j=collideExitList.arraySize()-1; j>=0; j--) {
+ wexit = wexitArr[j];
+ if (wexit.behav == behav) {
+ collideExitList.remove(wexit);
+ }
+ }
+ }
+ }
+ }
+
+ if (bhNodeCount < 1) {
+ return;
+ }
+
+ if (currTree == null) {
+ index = getBHTreeIndex(((BHLeafNode)bhNodeArr[0]).getLocale());
+ if (index<0)
+ return;
+ currTree = bhTreeArr[index];
+ }
+ currTree.delete(bhNodeArr, bhNodeCount);
+
+ // It is safe to do it here since only GeometryStructure
+ // thread invoke wakeupOnCollisionEntry/Exit .toArray()
+
+
+ wakeupOnCollisionEntry.clearMirror();
+ wakeupOnCollisionMovement.clearMirror();
+ wakeupOnCollisionExit.clearMirror();
+
+ synchronized (collideListLock) {
+ collideEntryList.clearMirror();
+ collideExitList.clearMirror();
+ }
+ }
+
+
+ private void processBoundsChanged(Object[] nodes, boolean transformChanged) {
+
+ int index;
+ Object node;
+
+ clearBhNodeArr();
+ for (int i = 0; i < nodes.length; i++) {
+ node = nodes[i];
+ if (node instanceof GeometryAtom) {
+ synchronized (node) {
+
+ GeometryAtom geomAtom = (GeometryAtom) node;
+ if (geomAtom.bhLeafNode != null) {
+ addToBhNodeArr(geomAtom.bhLeafNode);
+ }
+ }
+ } else if (node instanceof GroupRetained) {
+
+ GroupRetained group = (GroupRetained) node;
+ if (group.nodeType != NodeRetained.SWITCH) {
+ synchronized (node) {
+ if (group.bhLeafNode != null) {
+ addToBhNodeArr(group.bhLeafNode);
+ }
+ }
+ }
+ }
+ }
+
+ if (bhNodeCount < 1) {
+ return;
+ }
+
+ index = getBHTreeIndex(((BHLeafNode)bhNodeArr[0]).getLocale());
+
+ if (index >= 0) {
+ bhTreeArr[index].boundsChanged(bhNodeArr, bhNodeCount);
+
+ }
+ }
+
+ private void processTransformChanged(UpdateTargets targets) {
+
+ int i, j, index;
+ Object[] nodes, nodesArr;
+ UnorderList arrList;
+ int size;
+
+ clearBhNodeArr();
+
+ arrList = targets.targetList[Targets.GEO_TARGETS];
+
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (j = 0; j < size; j++) {
+ nodes = (Object[])nodesArr[j];
+ for (i = 0; i < nodes.length; i++) {
+ GeometryAtom geomAtom = (GeometryAtom) nodes[i];
+ synchronized (geomAtom) {
+ if (geomAtom.bhLeafNode != null) {
+ addToBhNodeArr(geomAtom.bhLeafNode);
+ }
+ }
+ }
+ }
+ }
+
+
+ arrList = targets.targetList[Targets.GRP_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+ for ( j = 0; j < size; j++) {
+ nodes = (Object[])nodesArr[j];
+ for ( i = 0; i < nodes.length; i++) {
+ GroupRetained group = (GroupRetained) nodes[i];
+ if (group.nodeType != NodeRetained.SWITCH) {
+ synchronized (group) {
+ if (group.bhLeafNode != null) {
+ addToBhNodeArr(group.bhLeafNode);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (bhNodeCount < 1) {
+ return;
+ }
+
+ index = getBHTreeIndex(((BHLeafNode)bhNodeArr[0]).getLocale());
+
+ if (index >= 0) {
+ bhTreeArr[index].boundsChanged(bhNodeArr, bhNodeCount);
+
+ }
+ }
+
+ // This method is called by RenderBin to get a array of possibly visible
+ // sub-trees.
+ // bhTrees mustn't be null.
+ // Return true if bhTree's root in encompass by frustumBBox.
+
+ boolean getVisibleBHTrees(RenderBin rBin, ArrayList bhTrees,
+ BoundingBox frustumBBox,
+ Locale locale, long referenceTime,
+ boolean stateChanged,
+ int visibilityPolicy) {
+
+ int i, j;
+ boolean unviInFB = true;
+
+ // System.out.println("GeometryStructure : view's locale is " + locale);
+ lock.readLock();
+
+ bhTrees.clear();
+ if (bhTreeCount == 1) {
+ // For debugging only.
+ if (J3dDebug.devPhase) {
+ if (J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2)) {
+ System.out.println("GeometryStructure : In simple case");
+ System.out.println("GeometryStructure : view's locale is " +
+ locale);
+ System.out.println("GeometryStructure : bhTreeArr[0].locale is " +
+ bhTreeArr[0].locale);
+ }
+ }
+ // One locale case - Lets make the simple case fast.
+ synchronized(visLock) {
+ unviInFB = bhTreeArr[0].getVisibleBHTrees(rBin, bhTrees, frustumBBox,
+ referenceTime,
+ stateChanged,
+ visibilityPolicy, true);
+ }
+ }
+ else {
+ // Multiple locale case.
+
+ // For debugging only.
+ if (J3dDebug.devPhase)
+ J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2,
+ "GeometryStructure : bhTreeCount is " +
+ universe.geometryStructure.bhTreeCount +
+ " view's locale is " + locale + "\n");
+
+ BoundingBox localeFrustumBBox = new BoundingBox();
+
+ synchronized(visLock) {
+
+ for (j=0; j<bhTreeCount; j++) {
+ if (J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2,
+ "GeometryStructure : bhTreeArr[" + j +
+ "] is " +
+ bhTreeArr[j].locale + "\n");
+ }
+ if (!locale.hiRes.equals(bhTreeArr[j].locale.hiRes)) {
+ bhTreeArr[j].locale.hiRes.difference(locale.hiRes, localeTrans);
+
+ if (J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.geometryStructure,
+ J3dDebug.LEVEL_2,
+ "localeTrans is " + localeTrans +
+ "GeometryStructure : localeFrustumBBox " +
+ localeFrustumBBox + "\n" );
+ }
+
+ // Need to translate view frustumBBox here.
+ localeFrustumBBox.lower.x = frustumBBox.lower.x + localeTrans.x;
+ localeFrustumBBox.lower.y = frustumBBox.lower.y + localeTrans.y;
+ localeFrustumBBox.lower.z = frustumBBox.lower.z + localeTrans.z;
+ localeFrustumBBox.upper.x = frustumBBox.upper.x + localeTrans.x;
+ localeFrustumBBox.upper.y = frustumBBox.upper.y + localeTrans.y;
+ localeFrustumBBox.upper.z = frustumBBox.upper.z + localeTrans.z;
+ }
+ else {
+ frustumBBox.copy(localeFrustumBBox);
+ }
+
+ if(!(bhTreeArr[j].getVisibleBHTrees(rBin, bhTrees,
+ localeFrustumBBox,
+ referenceTime,
+ stateChanged,
+ visibilityPolicy,
+ false))) {
+ unviInFB = false;
+ }
+ }
+ }
+ }
+
+ lock.readUnlock();
+ return unviInFB;
+ }
+
+ GeometryAtom[] pickAll(Locale locale, PickShape shape) {
+
+ int i;
+ UnorderList hitList = new UnorderList(BHNode.class);
+ hitList.clear();
+
+ lock.readLock();
+
+ i = getBHTreeIndex(locale);
+ if (i < 0) {
+ lock.readUnlock();
+ return null;
+ }
+
+ bhTreeArr[i].select(shape, hitList);
+ lock.readUnlock();
+
+ int size = hitList.size();
+
+ if (size < 1)
+ return null;
+
+ BHNode[] hitArr = (BHNode []) hitList.toArray(false);
+
+ GeometryAtom[] geometryAtoms = new GeometryAtom[size];
+ for (i=0; i<size; i++) {
+ geometryAtoms[i] = (GeometryAtom)(((BHLeafNode)hitArr[i]).leafIF);
+ }
+
+ return geometryAtoms;
+ }
+
+ GeometryAtom pickAny(Locale locale, PickShape shape) {
+
+ int i;
+
+ BHNode hitNode = null;
+
+ lock.readLock();
+
+ i = getBHTreeIndex(locale);
+ if (i < 0) {
+ lock.readUnlock();
+ return null;
+ }
+
+ hitNode = bhTreeArr[i].selectAny(shape);
+
+ lock.readUnlock();
+
+ if (hitNode == null)
+ return null;
+
+ return (GeometryAtom)(((BHLeafNode)hitNode).leafIF);
+
+ }
+
+
+ void addWakeupOnCollision(WakeupOnCollisionEntry w) {
+
+ boolean needTrigger = true;
+
+ // Cleanup, since collideEntryList did not remove
+ // its condition in removeWakeupOnCollision
+ synchronized (collideListLock) {
+ WakeupOnCollisionEntry collideEntryArr[] =
+ (WakeupOnCollisionEntry []) collideEntryList.toArray();
+ WakeupOnCollisionEntry wentry;
+ for (int i=collideEntryList.arraySize()-1; i>=0; i--) {
+ wentry = collideEntryArr[i];
+ if ((wentry.behav == w.behav) &&
+ (wentry.geometryAtoms == w.geometryAtoms)) {
+ collideEntryList.remove(i);
+ needTrigger = false;
+ break;
+ }
+ }
+ }
+
+ // add to wakeup list
+ wakeupOnCollisionEntry.add(w);
+ w.updateCollisionBounds(false);
+ // check for collision and triggered event
+ BHLeafInterface target = collide(w.behav.locale,
+ w.accuracyMode,
+ w.geometryAtoms,
+ w.vwcBounds,
+ w.boundingLeaf,
+ w.armingNode,
+ null);
+
+ if (target != null) {
+ collideEntryList.add(w);
+ w.setTarget(target);
+ }
+
+ if ((target != null) && (needTrigger)) {
+ w.setTriggered();
+ }
+ }
+
+
+ void addWakeupOnCollision(WakeupOnCollisionExit w) {
+
+ // Cleanup, since collideExitList did not remove
+ // its condition in removeWakeupOnCollision
+ boolean needTrigger = true;
+
+ synchronized (collideListLock) {
+ WakeupOnCollisionExit collideExitArr[] =
+ (WakeupOnCollisionExit []) collideExitList.toArray();
+ WakeupOnCollisionExit wexit;
+ for (int i=collideExitList.arraySize()-1; i>=0; i--) {
+ wexit = collideExitArr[i];
+ if ((wexit.behav == w.behav) &&
+ (wexit.geometryAtoms == w.geometryAtoms)) {
+ collideExitList.remove(i);
+ needTrigger = false;
+ break;
+ }
+ }
+ }
+
+ // add condition
+ wakeupOnCollisionExit.add(w);
+ w.updateCollisionBounds(false);
+ BHLeafInterface target = collide(w.behav.locale,
+ w.accuracyMode,
+ w.geometryAtoms,
+ w.vwcBounds,
+ w.boundingLeaf,
+ w.armingNode,
+ null);
+
+ if (target != null) {
+ // store the target that cause this condition to collide
+ // this is used when this condition is triggered.
+ w.setTarget(target);
+ collideExitList.add(w);
+ }
+
+ if (!needTrigger) {
+ return;
+ }
+ // see if the matching wakeupOnCollisionEntry
+ // condition exists
+
+ synchronized (collideListLock) {
+ WakeupOnCollisionEntry collideEntryArr[] =
+ (WakeupOnCollisionEntry []) collideEntryList.toArray();
+ WakeupOnCollisionEntry wentry;
+
+ for (int i=collideEntryList.arraySize()-1; i>=0; i--) {
+ wentry = collideEntryArr[i];
+ if ((wentry.behav == w.behav) &&
+ (wentry.geometryAtoms == w.geometryAtoms)) {
+ // Should not call collideEntryList.remove(i);
+ // Otherwise wakeupOn for Entry case may call several
+ // time at when initialize if collide
+ if (target == null) {
+ w.setTriggered();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ void addWakeupOnCollision(WakeupOnCollisionMovement w) {
+ wakeupOnCollisionMovement.add(w);
+ w.updateCollisionBounds(false);
+ BHLeafInterface target = collide(w.behav.locale,
+ w.accuracyMode,
+ w.geometryAtoms,
+ w.vwcBounds,
+ w.boundingLeaf,
+ w.armingNode,
+ w);
+ if (target != null) {
+ w.setTarget(target);
+ collideMovementList.add(w);
+ }
+ }
+
+ void removeWakeupOnCollision(WakeupOnCollisionEntry wentry) {
+ wakeupOnCollisionEntry.remove(wentry);
+ // No need to remove collideEntry, it is used next time
+ // when WakeupOnExitCollision is added to determine
+ // whether to trigger it.
+ }
+
+ void removeWakeupOnCollision(WakeupOnCollisionExit wexit) {
+ wakeupOnCollisionExit.remove(wexit);
+ // No need to remove collideExit, it is used next time
+ // when WakeupOnExitCollision is added to determine
+ // whether to trigger it.
+ }
+
+
+ void removeWakeupOnCollision(WakeupOnCollisionMovement wmovement) {
+ wakeupOnCollisionMovement.remove(wmovement);
+ collideMovementList.remove(wmovement); // remove if exists
+ }
+
+ /**
+ * This method test all wakeupOnCollision list and trigger the
+ * condition if collision occurs.
+ */
+ void processCollisionDetection() {
+ int i, idx;
+ BHLeafInterface target;
+
+ // handle WakeupOnCollisionEntry
+ WakeupOnCollisionEntry wentry;
+ WakeupOnCollisionEntry wentryArr[] = (WakeupOnCollisionEntry [])
+ wakeupOnCollisionEntry.toArray();
+
+ for (i = wakeupOnCollisionEntry.arraySize()-1; i >=0; i--) {
+ wentry = wentryArr[i];
+ wentry.updateCollisionBounds(reEvaluateWakeupCollisionGAs);
+ target = collide(wentry.behav.locale,
+ wentry.accuracyMode,
+ wentry.geometryAtoms,
+ wentry.vwcBounds,
+ wentry.boundingLeaf,
+ wentry.armingNode,
+ null);
+ idx = collideEntryList.indexOf(wentry);
+
+ if (target != null) {
+ if (idx < 0) {
+ collideEntryList.add(wentry);
+ wentry.setTarget(target);
+ wentry.setTriggered();
+ }
+ } else {
+ if (idx >= 0) {
+ collideEntryList.remove(idx);
+ }
+ }
+ }
+
+ // handle WakeupOnCollisionMovement
+
+ WakeupOnCollisionMovement wmove;
+ WakeupOnCollisionMovement wmoveArr[] = (WakeupOnCollisionMovement [])
+ wakeupOnCollisionMovement.toArray();
+
+ for (i = wakeupOnCollisionMovement.arraySize()-1; i >=0; i--) {
+ wmove = wmoveArr[i];
+ wmove.updateCollisionBounds(reEvaluateWakeupCollisionGAs);
+ target = collide(wmove.behav.locale,
+ wmove.accuracyMode,
+ wmove.geometryAtoms,
+ wmove.vwcBounds,
+ wmove.boundingLeaf,
+ wmove.armingNode,
+ wmove);
+ idx = collideMovementList.indexOf(wmove);
+ if (target != null) {
+ if (idx < 0) {
+ collideMovementList.add(wmove);
+ wmove.setTarget(target);
+ } else {
+ if (!wmove.duplicateEvent) {
+ wmove.setTriggered();
+ }
+ }
+ } else {
+ if (idx >= 0) {
+ collideMovementList.remove(idx);
+ wmove.lastSrcBounds = null;
+ wmove.lastDstBounds = null;
+ }
+ }
+ }
+
+
+ // Finally, handle WakeupOnCollisionExit
+
+ WakeupOnCollisionExit wexit;
+ WakeupOnCollisionExit wexitArr[] = (WakeupOnCollisionExit [])
+ wakeupOnCollisionExit.toArray();
+
+ for (i = wakeupOnCollisionExit.arraySize()-1; i >=0; i--) {
+ wexit = wexitArr[i];
+ wexit.updateCollisionBounds(reEvaluateWakeupCollisionGAs);
+ target = collide(wexit.behav.locale,
+ wexit.accuracyMode,
+ wexit.geometryAtoms,
+ wexit.vwcBounds,
+ wexit.boundingLeaf,
+ wexit.armingNode,
+ null);
+ idx = collideExitList.indexOf(wexit);
+ if (target != null) {
+ if (idx < 0) {
+ collideExitList.add(wexit);
+ wexit.setTarget(target);
+ }
+ } else {
+ if (idx >= 0) {
+ collideExitList.remove(idx);
+ wexit.setTriggered();
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * Check for duplicate WakeupOnCollisionMovement event.
+ * We don't want to continue deliver event even though the
+ * two colliding object did not move but this Geometry update
+ * thread continue to run due to transform change in others
+ * shape not in collision.
+ */
+ void checkDuplicateEvent(WakeupOnCollisionMovement wmove,
+ Bounds bound,
+ BHLeafInterface hitNode) {
+ Bounds hitBound;
+
+ if ((wmove.lastSrcBounds != null) &&
+ wmove.lastSrcBounds.equals(bound)) {
+ if (hitNode instanceof GeometryAtom) {
+ hitBound = ((GeometryAtom) hitNode).source.vwcBounds;
+ } else {
+ hitBound = ((GroupRetained) hitNode).collisionVwcBounds;
+ }
+ if ((wmove.lastDstBounds != null) &&
+ wmove.lastDstBounds.equals(hitBound)) {
+ wmove.duplicateEvent = true;
+ } else {
+ wmove.duplicateEvent = false;
+ wmove.lastDstBounds = (Bounds) hitBound.clone();
+ }
+ } else {
+ wmove.duplicateEvent = false;
+ wmove.lastSrcBounds = (Bounds) bound.clone();
+ }
+ }
+
+
+ /**
+ * check if either the geomAtoms[] or
+ * bound or boundingLeaf collide with BHTree.
+ * Only one of geomAtoms, bound, boundingLeaf is non-null.
+ * If accurancyMode is USE_GEOMETRY, object geometry is used,
+ * otherwise object bounding box is used for collision
+ * detection.
+ * In case of GROUP & BOUND, the armingNode is used
+ * to tell whether the colliding Group is itself or not.
+ * Also in case GROUP, geomAtoms is non-null if USE_GEOMETRY.
+ * If cond != null, it must be instanceof WakeupOnCollisionMovement
+ */
+ BHLeafInterface collide(Locale locale,
+ int accurancyMode,
+ UnorderList geomAtoms,
+ Bounds bound,
+ BoundingLeafRetained boundingLeaf,
+ NodeRetained armingNode,
+ WakeupCriterion cond) {
+
+ lock.readLock();
+ int idx = getBHTreeIndex(locale);
+
+ if (idx < 0) {
+ lock.readUnlock();
+ return null;
+ }
+ BHLeafInterface hitNode;
+
+ if (geomAtoms != null) {
+ synchronized (bhTreeArr[idx]) {
+ if ((bound != null) &&
+ (armingNode instanceof GroupRetained)) {
+ // Check Bound intersect first before process
+ // to individual Shape3D geometryAtoms
+ hitNode = bhTreeArr[idx].selectAny(bound,
+ accurancyMode,
+ (GroupRetained)
+ armingNode);
+ if (hitNode == null) {
+ lock.readUnlock();
+ return null;
+ }
+ GeometryAtom galist[] = (GeometryAtom [])
+ geomAtoms.toArray(false);
+
+ hitNode = bhTreeArr[idx].selectAny(galist,
+ geomAtoms.arraySize(),
+ accurancyMode);
+
+ if (hitNode != null) {
+ lock.readUnlock();
+ if (cond != null) {
+ checkDuplicateEvent((WakeupOnCollisionMovement) cond,
+ bound, hitNode);
+ }
+ return hitNode;
+ }
+ } else {
+ GeometryAtom ga = (GeometryAtom) geomAtoms.get(0);
+ hitNode = bhTreeArr[idx].selectAny(ga, accurancyMode);
+
+ if (hitNode != null) {
+ lock.readUnlock();
+ if (cond != null) {
+ checkDuplicateEvent((WakeupOnCollisionMovement) cond,
+ ga.source.vwcBounds,
+ hitNode);
+ }
+ return hitNode;
+ }
+ }
+ }
+ } else {
+ if (bound == null) {
+ if (boundingLeaf == null) {
+ lock.readUnlock();
+ return null;
+ }
+ bound = boundingLeaf.transformedRegion;
+ }
+ if (bound == null) {
+ lock.readUnlock();
+ return null;
+ }
+ if (armingNode instanceof GroupRetained) {
+ synchronized (bhTreeArr[idx]) {
+ hitNode = bhTreeArr[idx].selectAny(bound,
+ accurancyMode,
+ (GroupRetained)
+ armingNode);
+ lock.readUnlock();
+ if ((hitNode != null) && (cond != null)) {
+ checkDuplicateEvent((WakeupOnCollisionMovement) cond,
+ bound, hitNode);
+ }
+ return hitNode;
+ }
+ } else {
+ synchronized (bhTreeArr[idx]) {
+ hitNode = bhTreeArr[idx].selectAny(bound, accurancyMode,
+ armingNode);
+ lock.readUnlock();
+ if ((hitNode != null) && (cond != null)) {
+ checkDuplicateEvent((WakeupOnCollisionMovement) cond,
+ bound, hitNode);
+ }
+ return hitNode;
+ }
+ }
+ }
+ lock.readUnlock();
+ return null;
+ }
+
+
+ /**
+ * This prevents wakeupCondition sent out message and set
+ * conditionMet to true but the
+ * BehaviorStructure/BehaviorScheduler is not fast enough to
+ * process the message and reset conditionMet to false
+ * when view deactivate/unregister.
+ */
+ void resetConditionMet() {
+ BehaviorStructure.resetConditionMet(wakeupOnCollisionEntry);
+ BehaviorStructure.resetConditionMet(wakeupOnCollisionExit);
+ BehaviorStructure.resetConditionMet(wakeupOnCollisionMovement);
+ }
+
+ /**
+ * This processes a switch change.
+ */
+ private void processSwitchChanged(J3dMessage m) {
+
+ int i;
+ UnorderList arrList;
+ int size, treeIndex;
+ Object[] nodes;
+ LeafRetained leaf;
+
+/* is now a NOOP
+
+ UpdateTargets targets = (UpdateTargets)m.args[0];
+
+ arrList = targets.targetList[Targets.GEO_TARGETS];
+
+ if (arrList != null) {
+ size = arrList.size();
+ nodes = arrList.toArray(false);
+
+ treeIndex = getBHTreeIndex(((LeafRetained)nodes[0]).locale);
+
+ for (i=0; i<size; i++) {
+ leaf = (LeafRetained)nodes[i];
+ }
+ }
+*/
+ }
+
+ void cleanup() {
+ collideEntryList.clear();
+ collideExitList.clear();
+ collideMovementList.clear();
+ wakeupOnCollisionEntry.clear();
+ wakeupOnCollisionExit.clear();
+ wakeupOnCollisionMovement.clear();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GeometryUpdater.java b/src/classes/share/javax/media/j3d/GeometryUpdater.java
new file mode 100644
index 0000000..8056463
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GeometryUpdater.java
@@ -0,0 +1,43 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The GeometryUpdater interface is used in updating geometry data
+ * that is accessed by reference from a live or compiled GeometryArray
+ * object. Applications that wish to modify such data must define a
+ * class that implements this interface. An instance of that class is
+ * then passed to the <code>updateData</code> method of the
+ * GeometryArray object to be modified.
+ *
+ * @since Java 3D 1.2
+ */
+
+public interface GeometryUpdater {
+ /**
+ * Updates geometry data that is accessed by reference.
+ * This method is called by the updateData method of a
+ * GeometryArray object to effect
+ * safe updates to vertex data that
+ * is referenced by that object. Applications that wish to modify
+ * such data must implement this method and perform all updates
+ * within it.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ *
+ * @param geometry the Geometry object being updated.
+ * @see GeometryArray#updateData
+ */
+ public void updateData(Geometry geometry);
+}
diff --git a/src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java b/src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java
new file mode 100644
index 0000000..8799c1a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java
@@ -0,0 +1,372 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsConfigTemplate;
+
+/**
+ * This class is used to obtain a valid GraphicsConfiguration that can be used by Java 3D.
+ * A user instantiates one of these objects and then sets all
+ * non-default attributes as desired. The getBestConfiguration()
+ * method in the GraphicsDevice class is then called with this
+ * GraphicsConfigTemplate and the "best" GraphicsConfiguration is returned. The "best"
+ * GraphicsConfiguration means that this GraphicsConfiguration is supported and it
+ * meets or exceeds what was requested in the GraphicsConfigTemplate.
+ * Null is returned if no such "best" GraphicsConfiguration is found.
+ *
+ * @see GraphicsConfigTemplate
+ * @see GraphicsDevice
+ * @see GraphicsConfiguration
+ */
+public class GraphicsConfigTemplate3D extends GraphicsConfigTemplate {
+
+ int depthSize;
+ int doubleBuffer;
+ int blueSize;
+ int greenSize;
+ int redSize;
+ int sceneAntialiasing;
+ int stereo;
+
+ // Temporary variables use for passing argument to/from Request Renderer
+ Object testCfg;
+
+ static Object globalLock = new Object();
+ static Object monitorLock = new Object();
+ static boolean threadWaiting = false;
+
+ /**
+ * The platform dependent template. Since there is no
+ * template-specific instance data in the NativeConfigTemplate3D
+ * class, we can create one statically.
+ */
+ static NativeConfigTemplate3D nativeTemplate =
+ new NativeConfigTemplate3D();
+
+ /**
+ * Constructs a GraphicsConfigTemplate3D object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * depthSize : 16<br>
+ * doubleBuffer : REQUIRED<br>
+ * sceneAntialiasing : UNNECESSARY<br>
+ * stereo : UNNECESSARY<br>
+ * redSize : 2<br>
+ * greenSize : 2<br>
+ * blueSize : 2<br>
+ * </ul>
+ */
+ public GraphicsConfigTemplate3D() {
+ doubleBuffer = REQUIRED;
+ stereo = UNNECESSARY;
+ depthSize = 16;
+ redSize = greenSize = blueSize = 2;
+ sceneAntialiasing = UNNECESSARY;
+ }
+
+ /**
+ * Sets the double buffering requirement. It should be
+ * GraphicsConfigTemplate.REQUIRED, GraphicsConfigTemplate.PREFERRED,
+ * or GraphicsConfigTemplate.UNNECESSARY.
+ * If an invalid value is passed in, it is ignored.
+ * If the value of double buffering is
+ * GraphicsConfigTemplate.REQUIRED, and no GraphicsConfiguration is found
+ * that meets this requirement, null will be returned in getBestConfiguration().
+ * @param value the value to set this field to
+ */
+ public void setDoubleBuffer(int value) {
+ if (value < REQUIRED && value > UNNECESSARY)
+ return;
+
+ doubleBuffer = value;
+ }
+
+ /**
+ * Retrieves the double buffering value.
+ * @return the current value of the doubleBuffer attribute
+ */
+ public int getDoubleBuffer() {
+ return doubleBuffer;
+ }
+
+ /**
+ * Sets the stereo requirement. It should be
+ * GraphicsConfigTemplate.REQUIRED, GraphicsConfigTemplate.PREFERRED,
+ * or GraphicsConfigTemplate.UNNECESSARY. If an invalid value
+ * is passed in, it is ignored. If the value of stereo requirement is
+ * GraphicsConfigTemplate.REQUIRED, and no GraphicsConfiguration is found
+ * that meets this requirement, null will be returned in getBestConfiguration().
+ * @param value the value to set this field to
+ */
+ public void setStereo(int value) {
+ if (value < REQUIRED && value > UNNECESSARY)
+ return;
+
+ stereo = value;
+ }
+
+ /**
+ * Retrieves the stereo value.
+ * @return the current value of the stereo attribute.
+ */
+ public int getStereo() {
+ return stereo;
+ }
+
+ /**
+ * Sets the scene antialiasing requirement. It should be
+ * GraphicsConfigTemplate.REQUIRED, GraphicsConfigTemplate.PREFERRED,
+ * or GraphicsConfigTemplate.UNNECESSARY. If an invalid value
+ * is passed in, it is ignored. If the value of scene antialiasing is
+ * GraphicsConfigTemplate.REQUIRED, and no GraphicsConfiguration is found
+ * that meets this requirement, null will be returned in getBestConfiguration().
+ * @param value the value to set this field to
+ */
+ public void setSceneAntialiasing(int value) {
+ if (value < REQUIRED && value > UNNECESSARY)
+ return;
+
+ sceneAntialiasing = value;
+ }
+
+ /**
+ * Retrieves the scene antialiasing value.
+ * @return the current value of the scene antialiasing attribute.
+ */
+ public int getSceneAntialiasing() {
+ return sceneAntialiasing;
+ }
+
+ /**
+ * Sets the depth buffer size requirement. This is the minimum requirement.
+ * If no GraphicsConfiguration is found that meets or
+ * exceeds this minimum requirement, null will be returned in
+ * getBestConfiguration().
+ * @param value the value to set this field to
+ */
+ public void setDepthSize(int value) {
+ if (value < 0)
+ return;
+
+ depthSize = value;
+ }
+
+ /**
+ * Retrieves the size of the depth buffer.
+ * @return the current value of the depthSize attribute
+ */
+ public int getDepthSize() {
+ return depthSize;
+ }
+
+ /**
+ * Sets the number of red bits required. This is the minimum requirement.
+ * If no GraphicsConfiguration is found that meets or
+ * exceeds this minimum requirement, null will be returned in
+ * getBestConfiguration().
+ * @param value the value to set this field to
+ */
+ public void setRedSize(int value) {
+ if (value < 0)
+ return;
+
+ redSize = value;
+ }
+
+ /**
+ * Retrieves the number of red bits requested by this template.
+ * @return the current value of the redSize attribute.
+ */
+ public int getRedSize() {
+ return redSize;
+ }
+
+
+ /**
+ * Sets the number of green bits required. This is the minimum requirement.
+ * If no GraphicsConfiguration is found that meets or
+ * exceeds this minimum requirement, null will be returned in
+ * getBestConfiguration().
+ * @param value the value to set this field to
+ */
+ public void setGreenSize(int value) {
+ if (value < 0)
+ return;
+
+ greenSize = value;
+ }
+
+ /**
+ * Retrieves the number of green bits requested by this template.
+ * @return the current value of the greenSize attribute.
+ */
+ public int getGreenSize() {
+ return greenSize;
+ }
+
+ /**
+ * Sets the number of blue bits required. This is the minimum requirement.
+ * If no GraphicsConfiguration is found that meets or
+ * exceeds this minimum requirement, null will be returned in
+ * getBestConfiguration().
+ * @param value the value to set this field to
+ */
+ public void setBlueSize(int value) {
+ if (value < 0)
+ return;
+
+ blueSize = value;
+ }
+
+ /**
+ * Retrieves the number of blue bits requested by this template.
+ * @return the current value of the blueSize attribute.
+ */
+ public int getBlueSize() {
+ return blueSize;
+ }
+
+ /**
+ * Implement the abstract function of getBestConfiguration() in GraphicsConfigTemplate.
+ * Usually this function is not directly called by the user. It is
+ * implicitly called by getBestConfiguration() in GraphicsDevice.
+ * The method getBestConfiguration() in GraphicsDevice will return whatever this function returns.
+ * This function will return the "best" GraphicsConfiguration. The "best" GraphicsConfiguration
+ * means that this GraphicsConfiguration is supported and it meets or exceeds what was requested in the
+ * GraphicsConfigTemplate. If no such "best" GraphicsConfiguration is found, null is returned.
+ * @param gc the array of GraphicsConfigurations to choose from
+ *
+ * @return the best GraphicsConfiguration
+ *
+ * @see GraphicsDevice
+ */
+ public GraphicsConfiguration
+ getBestConfiguration(GraphicsConfiguration[] gc) {
+ if ((gc == null) || (gc.length == 0) || (gc[0] == null)) {
+ return null;
+ }
+
+ synchronized (globalLock) {
+ testCfg = gc;
+
+ // It is possible that the followign postRequest will
+ // cause request renderer run immediately before
+ // runMonitor(WAIT). So we need to set
+ // threadWaiting to true.
+ threadWaiting = true;
+
+ // Prevent deadlock if invoke from Behavior callback since
+ // this thread has to wait Renderer thread to finish but
+ // MC can only handle postRequest and put it in Renderer
+ // queue when free.
+ if (Thread.currentThread() instanceof BehaviorScheduler) {
+ VirtualUniverse.mc.sendRenderMessage(gc[0], this,
+ MasterControl.GETBESTCONFIG);
+ } else {
+ VirtualUniverse.mc.postRequest(MasterControl.GETBESTCONFIG, this);
+ }
+ runMonitor(J3dThread.WAIT);
+ return (GraphicsConfiguration) testCfg;
+ }
+ }
+
+ /**
+ * Returns a boolean indicating whether or not the given
+ * GraphicsConfiguration can be used to create a drawing
+ * surface that can be rendered to.
+ *
+ * @param gc the GraphicsConfiguration object to test
+ *
+ * @return <code>true</code> if this GraphicsConfiguration object
+ * can be used to create surfaces that can be rendered to,
+ * <code>false</code> if the GraphicsConfiguration can not be used
+ * to create a drawing surface usable by this API.
+ */
+ public boolean isGraphicsConfigSupported(GraphicsConfiguration gc) {
+ if (gc == null) {
+ return false;
+ }
+ synchronized (globalLock) {
+ testCfg = gc;
+ threadWaiting = true;
+ if (Thread.currentThread() instanceof BehaviorScheduler) {
+ VirtualUniverse.mc.sendRenderMessage(gc, this, MasterControl.ISCONFIGSUPPORT);
+ } else {
+ VirtualUniverse.mc.postRequest(MasterControl.ISCONFIGSUPPORT, this);
+ }
+ runMonitor(J3dThread.WAIT);
+ return ((Boolean) testCfg).booleanValue();
+ }
+ }
+
+ /**
+ * Set the stereo/doubleBuffer/sceneAntialiasingAccum
+ * and hasSceneAntialiasingMultiSamples flags in Canvas3D
+ */
+ static void getGraphicsConfigFeatures(Canvas3D c) {
+ synchronized (globalLock) {
+ threadWaiting = true;
+ if (Thread.currentThread() instanceof BehaviorScheduler) {
+ VirtualUniverse.mc.sendRenderMessage(c.graphicsConfiguration, c,
+ MasterControl.SET_GRAPHICSCONFIG_FEATURES);
+ } else {
+ VirtualUniverse.mc.postRequest(MasterControl.SET_GRAPHICSCONFIG_FEATURES, c);
+ }
+ runMonitor(J3dThread.WAIT);
+ }
+ }
+
+
+
+ /**
+ * Set the queryProperties() map in Canvas3D
+ */
+ static void setQueryProps(Canvas3D c) {
+ synchronized (globalLock) {
+ threadWaiting = true;
+ if (Thread.currentThread() instanceof BehaviorScheduler) {
+ VirtualUniverse.mc.sendRenderMessage(c.graphicsConfiguration, c,
+ MasterControl.SET_QUERYPROPERTIES);
+ } else {
+ VirtualUniverse.mc.postRequest(MasterControl.SET_QUERYPROPERTIES, c);
+ }
+ runMonitor(J3dThread.WAIT);
+ }
+ }
+
+
+ static void runMonitor(int action) {
+ // user thread will locked the globalLock when Renderer
+ // thread invoke this function so we can't use
+ // the same lock.
+ synchronized (monitorLock) {
+ switch (action) {
+ case J3dThread.WAIT:
+ if (threadWaiting) {
+ try {
+ monitorLock.wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ }
+ break;
+ case J3dThread.NOTIFY:
+ monitorLock.notify();
+ threadWaiting = false;
+ break;
+ }
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GraphicsContext3D.java b/src/classes/share/javax/media/j3d/GraphicsContext3D.java
new file mode 100644
index 0000000..22d5fc0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GraphicsContext3D.java
@@ -0,0 +1,2999 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.awt.Dimension;
+
+/**
+ * A GraphicsContext3D object is used for immediate mode rendering into
+ * a 3D canvas. It is created by, and associated with, a specific
+ * Canvas3D object. A GraphicsContext3D defines methods to set 3D graphics
+ * state and draw 3D geometric primitives. There are no public
+ * constructors of GraphicsContext3D. An application obtains a 3D graphics
+ * context object from the Canvas3D object that the application wishes
+ * to render into by using the getGraphicsContext3D method. A new graphics
+ * context is created if one does not already exist. A new GraphicsContext3D
+ * initializes its state variables to the following defaults:
+ * <UL>
+ * <LI> Background object: null </LI>
+ * <LI> Fog object: null </LI>
+ * <LI> ModelClip object: null </LI>
+ * <LI> Appearance object: null </LI>
+ * <LI> List of Light objects: empty </LI>
+ * <LI> high-res coordinate: (0, 0, 0) </LI>
+ * <LI> modelTransform: identity </LI>
+ * <LI> AuralAttributes object: null </LI>
+ * <LI> List of Sound objects: empty </LI>
+ * <LI> buffer override: false </LI>
+ * <LI> front buffer rendering: false </LI>
+ * <LI> stereo mode: <code>STEREO_BOTH</code> </LI>
+ * </UL>
+ *
+ * <p>
+ * Note that the drawing methods in this class are not necessarily
+ * executed immediately. They may be buffered up for future
+ * execution. Applications must call the
+ * <code><a href="#flush(boolean)">flush</a>(boolean)</code>
+ * method to ensure that the rendering actually happens. The flush
+ * method is implicitly called in the following cases:
+ *
+ * <ul>
+ * <li>The <code>readRaster</code> method calls
+ * <code>flush(true)</code></li>
+ * <li>The <code>Canvas3D.swap</code> method calls
+ * <code>flush(true)</code></li>
+ * <li>The Java 3D renderer calls <code>flush(true)</code> prior to
+ * swapping the buffer for a double buffered on-screen Canvas3D</li>
+ * <li>The Java 3D renderer calls <code>flush(true)</code> prior to
+ * copying into the off-screen buffer of an off-screen Canvas3D</li>
+ * <li>The Java 3D renderer calls <code>flush(false)</code> after
+ * calling the preRender, renderField, postRender, and postSwap
+ * Canvas3D callback methods.</li>
+ * </ul>
+ *
+ * <p>
+ * A single-buffered, pure-immediate mode application must explicitly
+ * call flush to ensure that the graphics will be rendered to the
+ * Canvas3D.
+ *
+ * @see Canvas3D#getGraphicsContext3D
+ */
+public class GraphicsContext3D extends Object {
+ /**
+ * Specifies that rendering is done to the left eye.
+ * @see #setStereoMode
+ * @since Java 3D 1.2
+ */
+ public static final int STEREO_LEFT = 0;
+
+ /**
+ * Specifies that rendering is done to the right eye.
+ * @see #setStereoMode
+ * @since Java 3D 1.2
+ */
+ public static final int STEREO_RIGHT = 1;
+
+ /**
+ * Specifies that rendering is done to both eyes. This is the
+ * default.
+ * @see #setStereoMode
+ * @since Java 3D 1.2
+ */
+ public static final int STEREO_BOTH = 2;
+
+
+ /**
+ * Canvas3D in which this GraphicsContext3D will render.
+ */
+ Canvas3D canvas3d = null;
+
+ int objectId = -1;
+
+//
+// Graphics state
+//
+// current user specified graphics state
+ Background uBackground = null;
+ Fog uFog = null;
+ Appearance uAppearance = null;
+ Vector uLights = new Vector();
+ HiResCoord uHiRes = new HiResCoord();
+ Vector uSounds = new Vector();
+ AuralAttributes uAuralAttributes = null;
+ boolean uBufferOverride = false;
+ boolean uFrontBufferRendering = false;
+ int uStereoMode = STEREO_BOTH;
+ ModelClip uModelClip = null;
+
+// Current rendering graphics state
+ // Current background
+ Background background = null;
+
+ // Background to use if background is null;
+ BackgroundRetained black = new BackgroundRetained();
+
+ // Current fog
+ Fog fog = null;
+
+ // Current modelClip
+ ModelClip modelClip = null;
+
+ // Current appearance object
+ Appearance appearance = null;
+
+ // default appearance retained object
+ AppearanceRetained defaultAppearanceRetained = new AppearanceRetained();
+
+ // The vector of lights
+ Vector lights = new Vector();
+
+ // Current High resolution coordinate
+ HiResCoord hiRes = new HiResCoord();
+
+ // Current modeling transform
+ Transform3D modelTransform = new Transform3D();
+ Transform3D identityTransform = new Transform3D();
+
+ Transform3D modelClipTransform = null;
+ Transform3D normalTransform = null;
+ boolean normalTransformNeedToUpdate = true;
+
+ // The vector of sounds
+ Vector sounds = new Vector();
+
+ // Current AuralAttributes state parameters
+ AuralAttributes auralAttributes = null;
+
+ // The render object associated with this context
+ LightSet ls = null;
+
+ // The current list of lights
+ LightRetained[] lightlist = null;
+
+ // Ambient lights
+ Color3f sceneAmbient = new Color3f(0.0f, 0.0f, 0.0f);
+
+ // The current number of lights, may be less than lightlist.length
+ int numLights = 0;
+
+ // Current composite transform: hi-res + modelTransform
+ Transform3D compTransform = new Transform3D();
+
+ // Draw transform: hi-res + modelTransform + view
+ Transform3D drawTransform = new Transform3D();
+
+ // The view transform (VPC to EC).
+ // NOTE that this is *read-only*
+ Transform3D vpcToEc;
+
+ // A boolean that indicates the lights have changed
+ boolean lightsChanged = false;
+
+ // A boolean that indicates the sounds have changed
+ // TODO: the soundsChanged flag are set like lights methods set
+ // lightsChanged? but where is this supposed to be check???
+ // lightsChanged tested in 'draw'; but Sound are not processed
+ // in draw.
+ boolean soundsChanged = false;
+
+ // Buffer override flag; enables frontBufferRendering and stereoMode
+ // attributes.
+ boolean bufferOverride = false;
+
+ // Forces rendering to the front buffer (if bufferOverride is true)
+ boolean frontBufferRendering = false;
+
+ // Stereo mode for this buffer (if bufferOverride is true)
+ int stereoMode = STEREO_BOTH;
+
+ // Read Buffer for reading raster of color image
+ byte[] byteBuffer = new byte[1];
+
+ // Read Buffer for reading floating depth image
+ float[] floatBuffer = new float[1];
+
+ // Read Buffer for reading integer depth image
+ int[] intBuffer = new int[1];
+
+ /**
+ * The cached ColoringAttributes color value. It is
+ * 1.0, 1.0, 1.0 if there is no ColoringAttributes.
+ */
+ float red = 1.0f;
+ float green = 1.0f;
+ float blue = 1.0f;
+
+
+ /**
+ * Cached diffuse color value
+ */
+ float dRed = 1.0f;
+ float dGreen = 1.0f;
+ float dBlue = 1.0f;
+
+ /**
+ * The cached TransparencyAttributes transparency value. It is
+ * 0.0 if there is no TransparencyAttributes.
+ */
+ float alpha = 0.0f;
+
+ /**
+ * The cached visible flag for geometry.
+ */
+ boolean visible = true;
+
+ /**
+ * Cached values for polygonMode, line antialiasing, and point antialiasing
+ */
+ int polygonMode = PolygonAttributes.POLYGON_FILL;
+ boolean lineAA = false;
+ boolean pointAA = false;
+
+
+ /**
+ /**
+ * A boolean indicating whether or not lighting should be on.
+ */
+ boolean enableLighting = false;
+
+ private Appearance defaultAppearance = null;
+
+ private boolean geometryIsLocked = false;
+
+ private boolean ignoreVertexColors = false;
+
+ static final int CLEAR = 0;
+ static final int DRAW = 1;
+ static final int SWAP = 2;
+ static final int READ_RASTER = 3;
+ static final int SET_APPEARANCE = 4;
+ static final int SET_BACKGROUND = 5;
+ static final int SET_FOG = 6;
+ static final int SET_LIGHT = 7;
+ static final int INSERT_LIGHT = 8;
+ static final int REMOVE_LIGHT = 9;
+ static final int ADD_LIGHT = 10;
+ static final int SET_HI_RES = 11;
+ static final int SET_MODEL_TRANSFORM = 12;
+ static final int MULTIPLY_MODEL_TRANSFORM = 13;
+ static final int SET_SOUND = 14;
+ static final int INSERT_SOUND = 15;
+ static final int REMOVE_SOUND = 16;
+ static final int ADD_SOUND = 17;
+ static final int SET_AURAL_ATTRIBUTES = 18;
+ static final int SET_BUFFER_OVERRIDE = 19;
+ static final int SET_FRONT_BUFFER_RENDERING = 20;
+ static final int SET_STEREO_MODE = 21;
+ static final int FLUSH = 22;
+ static final int FLUSH2D = 23;
+ static final int DRAWANDFLUSH2D = 24;
+ static final int SET_MODELCLIP = 25;
+ static final int NCOMMANDS = 26; // needs to be incremented
+ // when a new command is to be
+ // added to the list
+
+ static Integer commands[] = new Integer[NCOMMANDS];
+ static Integer stereoModes[] = {new Integer(STEREO_LEFT),
+ new Integer(STEREO_RIGHT),
+ new Integer(STEREO_BOTH)};
+
+ // dirty bits
+ static final int BUFFER_MODE = 0x1;
+ private int dirtyMask = 0;
+
+
+ // multi-texture
+ int numActiveTexUnit = 0;
+ int lastActiveTexUnitIndex = 0;
+ boolean toSimulateMultiTex = true;
+
+ // for read raster
+ volatile boolean readRasterReady = false;
+
+ // for runMonitor
+ boolean gcReady = false;
+ int waiting = 0;
+
+
+ /**
+ * Constructs and creates a GraphicsContext3D object with default
+ * values. Users do not call this directly, rather they get a
+ * graphics context from a Canvas3D.
+ */
+ GraphicsContext3D(Canvas3D canvas3d) {
+ this.canvas3d = canvas3d;
+ }
+
+ /**
+ * Gets the Canvas3D that created this GraphicsContext3D.
+ * @return the Canvas3D that created this GraphicsContext3D
+ */
+ public Canvas3D getCanvas3D() {
+ return this.canvas3d;
+ }
+
+//
+// Methods to set/get graphics state
+//
+
+ /**
+ * Sets the current Appearance object to the specified
+ * Appearance component object.
+ * The graphics context stores a reference to the specified
+ * Appearance object. This means that the application may modify
+ * individual appearance attributes by using the appropriate
+ * methods on the Appearance object.
+ * If the Appearance object is null, default values will be used
+ * for all appearance attributes - it is as if an
+ * Appearance node were created using the default constructor.
+ * @param appearance the new Appearance object
+ */
+ public void setAppearance(Appearance appearance) {
+
+ if(appearance == null) {
+ if(defaultAppearance == null) {
+ defaultAppearance = new Appearance();
+ }
+ appearance = defaultAppearance;
+ }
+
+ NodeComponentRetained nc = ((AppearanceRetained)appearance.retained).material;
+ uAppearance = appearance;
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetAppearance(appearance);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_APPEARANCE, appearance, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_APPEARANCE, appearance, null);
+ }
+ }
+
+ void doSetAppearance(Appearance appearance) {
+
+ if (appearance != null) {
+ NodeComponentRetained nc;
+ nc = ((AppearanceRetained)appearance.retained).material;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ enableLighting = ((MaterialRetained) nc).lightingEnable;
+ dRed = ((MaterialRetained) nc).diffuseColor.x;
+ dGreen = ((MaterialRetained) nc).diffuseColor.y;
+ dBlue = ((MaterialRetained) nc).diffuseColor.z;
+ }
+ else {
+ enableLighting = false;
+ }
+
+ if (((AppearanceRetained)appearance.retained).texUnitState != null) {
+ TextureUnitStateRetained[] texUnitState =
+ ((AppearanceRetained)appearance.retained).texUnitState;
+
+ for (int i = 0 ; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null) {
+ texUnitState[i].setInImmCtx(true);
+ }
+ }
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).texture;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).texCoordGeneration;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).textureAttributes;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).coloringAttributes;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ red = ((ColoringAttributesRetained)nc).color.x;
+ green = ((ColoringAttributesRetained)nc).color.y;
+ blue = ((ColoringAttributesRetained)nc).color.z;
+ }
+ else {
+ red = 1.0f;
+ green = 1.0f;
+ blue = 1.0f;
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).transparencyAttributes;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ alpha = 1.0f - ((TransparencyAttributesRetained) nc).transparency;
+ } else {
+ alpha = 1.0f;
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).renderingAttributes;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ visible = ((RenderingAttributesRetained)nc).visible;
+ }
+ else
+ visible = true;
+
+ nc = ((AppearanceRetained)appearance.retained).polygonAttributes;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ polygonMode = ((PolygonAttributesRetained)nc).polygonMode;
+ }
+ else {
+ polygonMode = PolygonAttributes.POLYGON_FILL;
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).lineAttributes;
+ if (nc != null) {
+ nc.setInImmCtx(true);
+ lineAA = ((LineAttributesRetained)nc).lineAntialiasing;
+
+ }
+ else {
+ lineAA = false;
+ }
+
+ nc = ((AppearanceRetained)appearance.retained).pointAttributes;
+ if (nc != null) {
+ if (nc.source.isLive())
+ nc.setInImmCtx(true);
+ pointAA = ((PointAttributesRetained)nc).pointAntialiasing;
+ }
+ else {
+ pointAA = false;
+ }
+
+
+ if (this.appearance != null) {
+ AppearanceRetained app = (AppearanceRetained)this.appearance.retained;
+ app.setInImmCtx(false);
+ if (app.material != null) {
+ app.material.setInImmCtx(false);
+ }
+ if (app.texUnitState != null) {
+ for (int i = 0; i < app.texUnitState.length; i++) {
+ if (app.texUnitState[0] != null)
+ app.texUnitState[0].setInImmCtx(false);
+ }
+ }
+ if (app.texture != null) {
+ app.texture.setInImmCtx(false);
+ }
+ if (app.texCoordGeneration != null) {
+ app.texCoordGeneration.setInImmCtx(false);
+ }
+ if (app.textureAttributes != null) {
+ app.textureAttributes.setInImmCtx(false);
+ }
+ if (app.coloringAttributes != null) {
+ app.coloringAttributes.setInImmCtx(false);
+ }
+ if (app.transparencyAttributes != null) {
+ app.transparencyAttributes.setInImmCtx(false);
+ }
+ if (app.renderingAttributes != null) {
+ app.renderingAttributes.setInImmCtx(false);
+ }
+ if (app.polygonAttributes != null) {
+ app.polygonAttributes.setInImmCtx(false);
+ }
+ if (app.lineAttributes != null) {
+ app.lineAttributes.setInImmCtx(false);
+ }
+ if (app.pointAttributes != null) {
+ app.pointAttributes.setInImmCtx(false);
+ }
+ }
+ ((AppearanceRetained)appearance.retained).setInImmCtx(true);
+ }
+ this.appearance = appearance;
+ }
+
+ /**
+ * Retrieves the current Appearance component object.
+ * @return the current Appearance object
+ */
+ public Appearance getAppearance() {
+ return this.uAppearance;
+ }
+
+ /**
+ * Sets the current Background to the specified Background
+ * leaf node object.
+ * The graphics context stores a reference to the specified
+ * Background node. This means that the application may modify
+ * the background color or image by using the appropriate
+ * methods on the Background node. The Background node must
+ * not be part of a live scene graph, nor may it subsequently
+ * be made part of a live scene graph-an IllegalSharingException
+ * is thrown in such cases. If the Background object is null,
+ * the default background color of black (0,0,0) is used to clear
+ * the canvas prior to rendering a new frame. The Background
+ * node's application region is ignored for immediate-mode
+ * rendering.
+ * @param background the new Background object
+ * @exception IllegalSharingException if the Background node
+ * is part of or is subsequently made part of a live scene graph.
+ */
+ public void setBackground(Background background) {
+ if (background.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D11"));
+ }
+ if (((BackgroundRetained)background.retained).geometryBranch != null)
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D22"));
+ uBackground = background;
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetBackground(background);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_BACKGROUND, background, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_BACKGROUND, background, null);
+ }
+ }
+
+ void doSetBackground(Background background) {
+ BackgroundRetained bg;
+
+ if (this.background != null) {
+ bg = (BackgroundRetained)this.background.retained;
+ bg.setInImmCtx(false);
+ if (bg.image != null) {
+ bg.image.freeSurface();
+ }
+ }
+ bg = (BackgroundRetained)background.retained;
+ bg.setInImmCtx(true);
+ if (bg.image != null) {
+ bg.image.freeSurface();
+ }
+
+ this.background = background;
+ }
+
+ /**
+ * Retrieves the current Background leaf node object.
+ * @return the current Background object
+ */
+ public Background getBackground() {
+ return this.uBackground;
+ }
+
+ /**
+ * Sets the current Fog to the specified Fog
+ * leaf node object.
+ * The graphics context stores a reference to the specified
+ * Fog node. This means that the application may modify the
+ * fog attributes using the appropriate methods on the Fog
+ * node object. The Fog node must not be part of a live
+ * scene graph, nor may it subsequently be made part of a
+ * live scene graph-an IllegalSharingException is thrown in
+ * such cases. If the Fog object is null, fog is disabled.
+ * Both the region of influence and the hierarchical scope
+ * of the Fog node are ignored for immediate-mode rendering.
+ * @param fog the new Fog object
+ * @exception IllegalSharingException if the Fog node
+ * is part of or is subsequently made part of a live scene graph.
+ */
+ public void setFog(Fog fog) {
+ if (fog != null && fog.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D12"));
+ }
+ uFog = fog;
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetFog(fog);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_FOG, fog, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_FOG, fog, null);
+ }
+ }
+
+ void doSetFog(Fog fog) {
+ if (this.fog != null) {
+ ((FogRetained)this.fog.retained).setInImmCtx(false);
+ }
+ this.fog = fog;
+ if (fog != null) {
+ ((FogRetained)fog.retained).setInImmCtx(true);
+
+
+ if (fog.retained instanceof LinearFogRetained)
+ updateFogState((LinearFogRetained)fog.retained);
+ }
+ }
+
+ /**
+ * Retrieves the current Fog leaf node object.
+ * @return the current Fog object
+ */
+ public Fog getFog() {
+ return this.uFog;
+ }
+
+
+ /**
+ * Sets the current ModelClip leaf node to the specified object.
+ * The graphics context stores a reference to the specified
+ * ModelClip node. This means that the application may modify the
+ * model clipping attributes using the appropriate methods on the
+ * ModelClip node object. The ModelClip node must not be part of a
+ * live scene graph, nor may it subsequently be made part of a
+ * live scene graph-an IllegalSharingException is thrown in such
+ * cases. If the ModelClip object is null, model clipping is
+ * disabled. Both the region of influence and the hierarchical
+ * scope of the ModelClip node are ignored for immediate-mode
+ * rendering.
+ *
+ * @param modelClip the new ModelClip node
+ *
+ * @exception IllegalSharingException if the ModelClip node
+ * is part of or is subsequently made part of a live scene graph.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setModelClip(ModelClip modelClip) {
+ if ((modelClip != null) && modelClip.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D25"));
+ }
+ uModelClip = modelClip;
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetModelClip(modelClip);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_MODELCLIP,
+ modelClip, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_MODELCLIP,
+ modelClip, null);
+ }
+ }
+
+ void doSetModelClip(ModelClip modelClip) {
+ ModelClipRetained mc = null;
+
+ this.modelClip = modelClip;
+
+ if (this.modelClip != null) {
+ mc = (ModelClipRetained)this.modelClip.retained;
+ mc.setInImmCtx(true);
+
+ if (modelClipTransform == null)
+ modelClipTransform = new Transform3D();
+
+ // save the current model Transform
+ modelClipTransform.set(compTransform);
+ }
+ }
+
+ /**
+ * Retrieves the current ModelClip leaf node object.
+ * @return the current ModelClip object
+ *
+ * @since Java 3D 1.2
+ */
+ public ModelClip getModelClip() {
+ return this.uModelClip;
+ }
+
+
+ /**
+ * Replaces the specified light with the light provided.
+ * The graphics context stores a reference to each light
+ * object in the list of lights. This means that the
+ * application may modify the light attributes for
+ * any of the lights using the appropriate methods on that
+ * Light node object. None of the Light nodes in the list
+ * of lights may be part of a live scene graph, nor may
+ * they subsequently be made part of a live scene graph -
+ * an IllegalSharingException is thrown in such cases.
+ * @param light the new light
+ * @param index which light to replace
+ * @exception IllegalSharingException if the Light node
+ * is part of or is subsequently made part of a live scene graph.
+ * @exception NullPointerException if the Light object is null.
+ */
+ public void setLight(Light light, int index) {
+ if (light == null) {
+ throw new NullPointerException(J3dI18N.getString("GraphicsContext3D13"));
+ }
+ if (light.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D14"));
+ }
+ uLights.setElementAt(light, index);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetLight(light, index);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_LIGHT, light,
+ new Integer(index));
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_LIGHT, light,
+ new Integer(index));
+ }
+ }
+
+ void doSetLight(Light light, int index) {
+
+ Light oldlight;
+ oldlight = (Light)this.lights.elementAt(index);
+ if (oldlight != null) {
+ ((LightRetained)oldlight.retained).setInImmCtx(false);
+ }
+ ((LightRetained)light.retained).setInImmCtx(true);
+ updateLightState((LightRetained)light.retained);
+ this.lights.setElementAt(light, index);
+ this.lightsChanged = true;
+ }
+
+ /**
+ * Inserts the specified light at the specified index location.
+ * @param light the new light
+ * @param index at which location to insert
+ * @exception IllegalSharingException if the Light node
+ * is part of or is subsequently made part of a live scene graph.
+ * @exception NullPointerException if the Light object is null.
+ */
+ public void insertLight(Light light, int index) {
+ if (light == null) {
+ throw new NullPointerException(J3dI18N.getString("GraphicsContext3D13"));
+ }
+ if (light.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D14"));
+ }
+ uLights.insertElementAt(light, index);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doInsertLight(light, index);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.INSERT_LIGHT, light,
+ new Integer(index));
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.INSERT_LIGHT, light,
+ new Integer(index));
+ }
+ }
+
+ void doInsertLight(Light light, int index) {
+ ((LightRetained)light.retained).setInImmCtx(true);
+ updateLightState((LightRetained)light.retained);
+ this.lights.insertElementAt(light, index);
+ this.lightsChanged = true;
+ }
+
+ /**
+ * Removes the light at the specified index location.
+ * @param index which light to remove
+ */
+ public void removeLight(int index) {
+ uLights.removeElementAt(index);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doRemoveLight(index);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.REMOVE_LIGHT,
+ new Integer(index), null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.REMOVE_LIGHT,
+ new Integer(index), null);
+ }
+ }
+
+ void doRemoveLight(int index) {
+ Light light = (Light) this.lights.elementAt(index);
+
+ ((LightRetained)light.retained).setInImmCtx(false);
+ this.lights.removeElementAt(index);
+ this.lightsChanged = true;
+ }
+
+ /**
+ * Retrieves the index selected light.
+ * @param index which light to return
+ * @return the light at location index
+ */
+ public Light getLight(int index) {
+ return (Light) uLights.elementAt(index);
+ }
+
+ /**
+ * Retrieves the enumeration object of all the lights.
+ * @return the enumeration object of all the lights
+ */
+ public Enumeration getAllLights() {
+ return uLights.elements();
+ }
+
+ /**
+ * Appends the specified light to this graphics context's list of lights.
+ * Adding a null Light object to the list will result
+ * in a NullPointerException. Both the region of influence
+ * and the hierarchical scope of all lights in the list
+ * are ignored for immediate-mode rendering.
+ * @param light the light to add
+ * @exception IllegalSharingException if the Light node
+ * is part of or is subsequently made part of a live scene graph.
+ * @exception NullPointerException if the Light object is null.
+ */
+ public void addLight(Light light) {
+ if (light == null) {
+ throw new NullPointerException(J3dI18N.getString("GraphicsContext3D13"));
+ }
+
+ if (light.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D14"));
+ }
+ uLights.addElement(light);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doAddLight(light);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.ADD_LIGHT, light, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.ADD_LIGHT, light, null);
+ }
+ }
+
+ void doAddLight(Light light) {
+
+ ((LightRetained)light.retained).setInImmCtx(true);
+ updateLightState((LightRetained)light.retained);
+ this.lights.addElement(light);
+ this.lightsChanged = true;
+ }
+
+ /**
+ * Retrieves the current number of lights in this graphics context.
+ * @return the current number of lights
+ */
+ public int numLights() {
+ return this.uLights.size();
+ }
+
+
+ private Transform3D getNormalTransform() {
+ if (compTransform.isRigid()) {
+ return compTransform;
+ }
+ if (normalTransform == null) {
+ normalTransform = new Transform3D();
+ }
+
+ if (normalTransformNeedToUpdate) {
+ normalTransform.invert(compTransform);
+ normalTransform.transpose();
+ normalTransformNeedToUpdate = false;
+ }
+ return normalTransform;
+ }
+
+
+ void updateFogState(LinearFogRetained lfog) {
+ lfog.localToVworldScale = modelTransform.getDistanceScale();
+ }
+
+
+ void updateLightState(LightRetained light) {
+
+ if (light instanceof DirectionalLightRetained) {
+ DirectionalLightRetained dl = (DirectionalLightRetained) light;
+
+ Transform3D xform = getNormalTransform();
+ xform.transform(dl.direction, dl.xformDirection);
+ dl.xformDirection.normalize();
+
+ } else if (light instanceof SpotLightRetained) {
+ SpotLightRetained sl = (SpotLightRetained) light;
+
+ Transform3D xform = getNormalTransform();
+ xform.transform(sl.direction, sl.xformDirection);
+ sl.xformDirection.normalize();
+ this.modelTransform.transform(sl.position, sl.xformPosition);
+
+ } else if (light instanceof PointLightRetained) {
+ PointLightRetained pl = (PointLightRetained) light;
+
+ this.modelTransform.transform(pl.position,pl.xformPosition);
+
+ pl.localToVworldScale = modelTransform.getDistanceScale();
+
+ }
+ }
+
+ /**
+ * Sets the HiRes coordinate of this context to the location
+ * specified by the parameters provided.
+ * The parameters x, y, and z are arrays of eight 32-bit
+ * integers that specify the high-resolution coordinates point.
+ * @param x an eight element array specifying the x position
+ * @param y an eight element array specifying the y position
+ * @param z an eight element array specifying the z position
+ * @see HiResCoord
+ */
+ public void setHiRes(int[] x, int[] y, int[] z) {
+ HiResCoord hiRes = new HiResCoord(x, y, z);
+ setHiRes(hiRes);
+ }
+
+ /**
+ * Sets the HiRes coordinate of this context
+ * to the location specified by the HiRes argument.
+ * @param hiRes the HiRes coordinate specifying the a new location
+ */
+ public void setHiRes(HiResCoord hiRes) {
+ uHiRes.setHiResCoord(hiRes);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetHiRes(hiRes);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_HI_RES, hiRes, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_HI_RES, hiRes, null);
+ }
+ }
+
+ void doSetHiRes(HiResCoord hiRes) {
+ this.hiRes.setHiResCoord(hiRes);
+ computeCompositeTransform();
+ }
+
+ /**
+ * Retrieves the current HiRes coordinate of this context.
+ * @param hiRes a HiResCoord object that will receive the
+ * HiRes coordinate of this context
+ */
+ public void getHiRes(HiResCoord hiRes) {
+ uHiRes.getHiResCoord(hiRes);
+ }
+
+ /**
+ * Sets the current model transform to a copy of the specified
+ * transform.
+ * A BadTransformException is thrown if an attempt is made
+ * to specify an illegal Transform3D.
+ * @param t the new model transform
+ * @exception BadTransformException if the transform is not affine.
+ */
+ public void setModelTransform(Transform3D t) {
+
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetModelTransform(t);
+ }
+ else {
+ Transform3D uModelTransform = VirtualUniverse.mc.getTransform3D(t);
+ //Transform3D uModelTransform = t;
+ if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_MODEL_TRANSFORM,
+ uModelTransform, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_MODEL_TRANSFORM,
+ uModelTransform, null);
+ }
+ }
+ }
+
+ void doSetModelTransform(Transform3D t) {
+ this.modelTransform.set(t);
+ computeCompositeTransform();
+ normalTransformNeedToUpdate = true;
+ }
+
+ /**
+ * Multiplies the current model transform by the specified
+ * transform and stores the result back into the current
+ * transform. The specified transformation must be affine.
+ * @param t the model transform to be concatenated with the
+ * current model transform
+ * @exception BadTransformException if the transform is not affine.
+ */
+ public void multiplyModelTransform(Transform3D t) {
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doMultiplyModelTransform(t);
+ } else {
+ Transform3D tt = VirtualUniverse.mc.getTransform3D(t);
+ if (Thread.currentThread() == canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.MULTIPLY_MODEL_TRANSFORM,
+ tt, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.MULTIPLY_MODEL_TRANSFORM,
+ tt, null);
+ }
+ }
+ }
+
+ void doMultiplyModelTransform(Transform3D t) {
+ this.modelTransform.mul(t);
+ computeCompositeTransform();
+ normalTransformNeedToUpdate = true;
+ }
+
+ /**
+ * Retrieves the current model transform.
+ * @param t the model transform that will receive the current
+ * model transform
+ */
+ public void getModelTransform(Transform3D t) {
+ t.set(modelTransform);
+ }
+
+ /**
+ * Replaces the specified sound with the sound provided.
+ * The graphics context stores a reference to each sound
+ * object in the list of sounds. This means that the
+ * application may modify the sound attributes for
+ * any of the sounds by using the appropriate methods on
+ * that Sound node object.
+ * @param sound the new sound
+ * @param index which sound to replace
+ * @exception IllegalSharingException if the Sound node
+ * is part of or is subsequently made part of a live scene graph.
+ * @exception NullPointerException if the Sound object is null.
+ */
+ public void setSound(Sound sound, int index) {
+ if (sound == null) {
+ throw new NullPointerException(J3dI18N.getString("GraphicsContext3D17"));
+ }
+ if (sound.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D23"));
+ }
+ uSounds.setElementAt(sound, index);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetSound(sound, index);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_SOUND, sound,
+ new Integer(index));
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_SOUND, sound,
+ new Integer(index));
+ }
+ }
+
+ void doSetSound(Sound sound, int index) {
+ Sound oldSound;
+ oldSound = (Sound)(this.sounds.elementAt(index));
+ ((SoundRetained)sound.retained).setInImmCtx(true);
+ if (oldSound != null) {
+ ((SoundRetained)oldSound.retained).setInImmCtx(false);
+ }
+ ((SoundRetained)sound.retained).setInImmCtx(true);
+ updateSoundState((SoundRetained)(sound.retained));
+ this.sounds.setElementAt(sound, index);
+ this.soundsChanged = true;
+
+ sendSoundMessage(GraphicsContext3D.SET_SOUND, sound, oldSound);
+ }
+
+ /**
+ * Inserts the specified sound at the specified index location.
+ * Inserting a sound to the list of sounds implicitly starts the
+ * sound playing. Once a sound is finished playing, it can be
+ * restarted by setting the sound's enable flag to true.
+ * The scheduling region of all sounds in the list is ignored
+ * for immediate-mode rendering.
+ * @param sound the new sound
+ * @param index at which location to insert
+ * @exception IllegalSharingException if the Sound node
+ * is part or is subsequently made part of a live scene graph.
+ * @exception NullPointerException if the Sound object is null.
+ */
+ public void insertSound(Sound sound, int index) {
+ if (sound == null) {
+ throw new NullPointerException(J3dI18N.getString("GraphicsContext3D17")); }
+
+ if (sound.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D23"));
+ }
+ uSounds.insertElementAt(sound, index);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doInsertSound(sound, index);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.INSERT_SOUND, sound,
+ new Integer(index));
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.INSERT_SOUND, sound,
+ new Integer(index));
+ }
+ }
+
+ void doInsertSound(Sound sound, int index) {
+ updateSoundState((SoundRetained)sound.retained);
+ this.sounds.insertElementAt(sound, index);
+ this.soundsChanged = true;
+ sendSoundMessage(GraphicsContext3D.INSERT_SOUND, sound, null);
+ }
+
+ /**
+ * Removes the sound at the specified index location.
+ * @param index which sound to remove
+ */
+ public void removeSound(int index) {
+ uSounds.removeElementAt(index);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doRemoveSound(index);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.REMOVE_SOUND,
+ new Integer(index), null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.REMOVE_SOUND,
+ new Integer(index), null);
+ }
+ }
+
+ void doRemoveSound(int index) {
+ Sound sound = (Sound)(this.sounds.elementAt(index));
+ SoundScheduler soundScheduler = getSoundScheduler();
+ ((SoundRetained)(sound.retained)).setInImmCtx(false);
+ this.sounds.removeElementAt(index);
+ this.soundsChanged = true;
+ // stop sound if playing on audioDevice
+ sendSoundMessage(GraphicsContext3D.REMOVE_SOUND, null, sound);
+ }
+
+ /**
+ * Retrieves the index selected sound.
+ * @param index which sound to return
+ * @return the sound at location index
+ */
+ public Sound getSound(int index) {
+ Sound sound = (Sound)(uSounds.elementAt(index));
+ return sound;
+ }
+
+ /**
+ * Retrieves the enumeration object of all the sounds.
+ * @return the enumeration object of all the sounds
+ */
+ public Enumeration getAllSounds() {
+ return uSounds.elements();
+ }
+
+ /**
+ * Appends the specified sound to this graphics context's list of sounds.
+ * Adding a sound to the list of sounds implicitly starts the
+ * sound playing. Once a sound is finished playing, it can be
+ * restarted by setting the sound's enable flag to true.
+ * The scheduling region of all sounds in the list is ignored
+ * for immediate-mode rendering.
+ * @param sound the sound to add
+ * @exception IllegalSharingException if the Sound node
+ * is part of or is subsequently made part of a live scene graph.
+ * @exception NullPointerException if the Sound object is null.
+ */
+ public void addSound(Sound sound) {
+ if (sound == null) {
+ throw new NullPointerException(J3dI18N.getString("GraphicsContext3D17")); }
+
+ if (sound.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D23"));
+
+ }
+ uSounds.addElement(sound);
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doAddSound(sound);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.ADD_SOUND, sound, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.ADD_SOUND, sound, null);
+ }
+ }
+
+ void doAddSound(Sound sound) {
+ ((SoundRetained)(sound.retained)).setInImmCtx(true);
+ updateSoundState((SoundRetained)(sound.retained));
+ this.sounds.addElement(sound);
+ this.soundsChanged = true;
+ sendSoundMessage(GraphicsContext3D.ADD_SOUND, sound, null);
+ }
+
+ /**
+ * Retrieves the current number of sounds in this graphics context.
+ * @return the current number of sounds
+ */
+ public int numSounds() {
+ return uSounds.size();
+ }
+
+ SoundScheduler getSoundScheduler() {
+ if (canvas3d != null && canvas3d.view != null)
+ return canvas3d.view.soundScheduler; // could be null as well
+ else
+ return (SoundScheduler)null;
+ }
+
+ void updateSoundState(SoundRetained sound) {
+ View view = null;
+ if (canvas3d != null)
+ view = canvas3d.view;
+ // Make sure that:
+ // . Current view is not null
+ // . The sound scheduler running (reference to it is not null)
+ if (view != null) {
+ SoundScheduler soundScheduler = getSoundScheduler();
+ if (soundScheduler == null) {
+ // TODO: Re-implement
+ // start up SoundScheduler since it hasn't already been started
+ }
+ }
+
+ // Update sound fields related to transforms
+ if (sound instanceof ConeSoundRetained) {
+ ConeSoundRetained cs = (ConeSoundRetained) sound;
+ this.modelTransform.transform(cs.direction, cs.xformDirection);
+ cs.xformDirection.normalize();
+ this.modelTransform.transform(cs.position, cs.xformPosition);
+ // TODO (Question) Is drawTranform equivalent to Vworld-to-Local?
+ cs.trans.setWithLock(drawTransform);
+
+ } else if (sound instanceof PointSoundRetained) {
+ PointSoundRetained ps = (PointSoundRetained) sound;
+ this.modelTransform.transform(ps.position, ps.xformPosition);
+ // TODO (Question) Is drawTranform equivalent to Vworld-to-Local?
+ ps.trans.setWithLock(drawTransform);
+ }
+ }
+
+ /**
+ * Retrieves the sound playing flag.
+ * @param index which sound
+ * @return flag denoting if sound is currently playing
+ */
+ public boolean isSoundPlaying(int index) {
+ Sound sound;
+ // uSounds isPlaying field is NOT updated, sounds elements are used
+ sound = (Sound)(this.sounds.elementAt(index));
+ return sound.isPlaying();
+ }
+
+ /**
+ * Sets the current AuralAttributes object to the specified
+ * AuralAttributes component object.
+ * This means that the application may modify individual
+ * audio attributes by using the appropriate methods in
+ * the Aural-Attributes object.
+ * @param attributes the new AuralAttributes object
+ */
+ public void setAuralAttributes(AuralAttributes attributes) {
+ uAuralAttributes = attributes;
+
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetAuralAttributes(attributes);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_AURAL_ATTRIBUTES,
+ attributes, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_AURAL_ATTRIBUTES,
+ attributes, null);
+ }
+ }
+
+ void doSetAuralAttributes(AuralAttributes attributes) {
+ this.auralAttributes = attributes;
+ sendSoundMessage(GraphicsContext3D.SET_AURAL_ATTRIBUTES, attributes, null);
+ }
+ /**
+ * Retrieves the current AuralAttributes component object.
+ * @return the current AuralAttributes object
+ */
+ public AuralAttributes getAuralAttributes() {
+ return uAuralAttributes;
+ }
+
+
+ /**
+ * Sets a flag that specifies whether the double buffering and
+ * stereo mode from the Canvas3D are overridden. When set to
+ * true, this attribute enables the
+ * <code>frontBufferRendering</code> and <code>stereoMode</code>
+ * attributes.
+ *
+ * @param bufferOverride the new buffer override flag
+ *
+ * @see #setFrontBufferRendering
+ * @see #setStereoMode
+ *
+ * @since Java 3D 1.2
+ */
+ public void setBufferOverride(boolean bufferOverride) {
+ uBufferOverride = bufferOverride;
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetBufferOverride(bufferOverride);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_BUFFER_OVERRIDE,
+ new Boolean(bufferOverride), null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_BUFFER_OVERRIDE,
+ new Boolean(bufferOverride), null);
+ }
+ }
+
+ void doSetBufferOverride(boolean bufferOverride) {
+ if (bufferOverride != this.bufferOverride) {
+ this.bufferOverride = bufferOverride;
+ dirtyMask |= BUFFER_MODE;
+ }
+ }
+
+
+ /**
+ * Returns the current buffer override flag.
+ * @return true if buffer override is enabled; otherwise,
+ * false is returned
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getBufferOverride() {
+ return uBufferOverride;
+ }
+
+
+ /**
+ * Sets a flag that enables or disables immediate mode rendering
+ * into the front buffer of a double buffered Canvas3D.
+ * This attribute is only used when the
+ * <code>bufferOverride</code> flag is enabled.
+ * <p>
+ * Note that this attribute has no effect if double buffering
+ * is disabled or is not available on the Canvas3D.
+ *
+ * @param frontBufferRendering the new front buffer rendering flag
+ *
+ * @see #setBufferOverride
+ *
+ * @since Java 3D 1.2
+ */
+ public void setFrontBufferRendering(boolean frontBufferRendering) {
+ uFrontBufferRendering = frontBufferRendering;
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetFrontBufferRendering(frontBufferRendering);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_FRONT_BUFFER_RENDERING,
+ new Boolean(frontBufferRendering), null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_FRONT_BUFFER_RENDERING,
+ new Boolean(frontBufferRendering), null);
+ }
+ }
+
+ void doSetFrontBufferRendering(boolean frontBufferRendering) {
+ if (frontBufferRendering != this.frontBufferRendering) {
+ this.frontBufferRendering = frontBufferRendering;
+ dirtyMask |= BUFFER_MODE;
+ }
+ }
+
+
+ /**
+ * Returns the current front buffer rendering flag.
+ * @return true if front buffer rendering is enabled; otherwise,
+ * false is returned
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getFrontBufferRendering() {
+ return uFrontBufferRendering;
+ }
+
+
+ /**
+ * Sets the stereo mode for immediate mode rendering. The
+ * parameter specifies which stereo buffer or buffers is rendered
+ * into. This attribute is only used when the
+ * <code>bufferOverride</code> flag is enabled.
+ * <ul>
+ * <li>
+ * <code>STEREO_LEFT</code> specifies that rendering is done into
+ * the left eye.
+ * </li>
+ * <li>
+ * <code>STEREO_RIGHT</code> specifies that rendering is done into
+ * the right eye.
+ * </li>
+ * <li>
+ * <code>STEREO_BOTH</code> specifies that rendering is done into
+ * both eyes. This is the default.
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * Note that this attribute has no effect if stereo is disabled or
+ * is not available on the Canvas3D.
+ *
+ * @param stereoMode the new stereo mode
+ *
+ * @see #setBufferOverride
+ *
+ * @since Java 3D 1.2
+ */
+ public void setStereoMode(int stereoMode) {
+ uStereoMode = stereoMode;
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doSetStereoMode(stereoMode);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.SET_STEREO_MODE,
+ stereoModes[stereoMode], null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.SET_STEREO_MODE,
+ stereoModes[stereoMode], null);
+ }
+ }
+
+ void doSetStereoMode(int stereoMode) {
+ if (stereoMode != this.stereoMode) {
+ this.stereoMode = stereoMode;
+ dirtyMask |= BUFFER_MODE;
+ }
+ }
+
+
+ /**
+ * Returns the current stereo mode.
+ * @return the stereo mode, one of <code>STEREO_LEFT</code>,
+ * <code>STEREO_RIGHT</code>, or <code>STEREO_BOTH</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public int getStereoMode() {
+ return uStereoMode;
+ }
+
+
+//
+// Methods to draw graphics objects
+//
+
+ /**
+ * Clear the Canvas3D to the color or image specified by the
+ * current background node.
+ */
+ public void clear() {
+ if ((canvas3d.view == null) || (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active)) {
+ return;
+ } else if (Thread.currentThread() == canvas3d.screen.renderer) {
+ doClear();
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.CLEAR, null, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.CLEAR, null, null);
+ }
+ }
+
+ void doClear() {
+
+ if (!canvas3d.firstPaintCalled)
+ return;
+
+ RenderBin rb = canvas3d.view.renderBin;
+ BackgroundRetained back = null;
+
+
+ if (this.background != null)
+ back = (BackgroundRetained)this.background.retained;
+ else
+ back = this.black;
+
+ // TODO: This should ideally be done by the renderer (or by the
+ // canvas itself) when the canvas is first added to a view.
+ /*
+ if ((canvas3d.screen.renderer != null) &&
+ (canvas3d.screen.renderer.renderBin == null))
+ canvas3d.screen.renderer.renderBin = rb;
+ */
+ // If we are in pure immediate mode, update the view cache
+ if (!canvas3d.isRunning)
+ updateViewCache(rb);
+
+ // We need to catch NullPointerException when the dsi
+ // gets yanked from us during a remove.
+
+ try {
+ if (canvas3d.drawingSurfaceObject.renderLock()) {
+ // TODO : Fix texture
+ /*
+ if (canvas3d.useSharedCtx) {
+ if (canvas3d.screen.renderer.sharedCtx == 0) {
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ canvas3d.screen.renderer.sharedCtx = canvas3d.createContext(
+ canvas3d.screen.display,
+ canvas3d.window, canvas3d.vid, 0, true,
+ canvas3d.offScreen);
+ canvas3d.screen.renderer.sharedCtxTimeStamp =
+ VirtualUniverse.mc.getContextTimeStamp();
+ canvas3d.screen.renderer.needToRebuildDisplayList = true;
+ }
+ }
+ }
+ */
+
+ if (canvas3d.ctx == 0) {
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ canvas3d.ctx =
+ canvas3d.createContext(canvas3d.screen.display,
+ canvas3d.window,
+ canvas3d.vid,
+ canvas3d.visInfo,
+ 0, false,
+ canvas3d.offScreen);
+ if (canvas3d.ctx == 0) {
+ canvas3d.drawingSurfaceObject.unLock();
+ return;
+ }
+
+ canvas3d.ctxTimeStamp =
+ VirtualUniverse.mc.getContextTimeStamp();
+ canvas3d.screen.renderer.listOfCtxs.add(
+ new Long(canvas3d.ctx));
+ canvas3d.screen.renderer.listOfCanvases.add(canvas3d);
+
+ canvas3d.beginScene();
+
+ if (canvas3d.graphics2D != null) {
+ canvas3d.graphics2D.init();
+ }
+
+ // query for the number of texture units
+ // supported
+ if (canvas3d.multiTexAccelerated) {
+ canvas3d.numTexUnitSupported =
+ canvas3d.getTextureUnitCount(canvas3d.ctx);
+ }
+
+ // enable separate specular color
+ canvas3d.enableSeparateSpecularColor();
+ }
+
+ // create the cache texture state in canvas
+ // for state download checking purpose
+
+ if (canvas3d.texUnitState == null) {
+ canvas3d.texUnitState =
+ new TextureUnitStateRetained[
+ canvas3d.numTexUnitSupported];
+ for (int t = 0; t < canvas3d.numTexUnitSupported; t++) {
+ canvas3d.texUnitState[t] =
+ new TextureUnitStateRetained();
+ canvas3d.texUnitState[t].texture = null;
+ canvas3d.texUnitState[t].mirror = null;
+ }
+ }
+
+
+ // also create the texture unit state map
+ // which is a mapping from texture unit state to
+ // the actual underlying texture unit
+
+ if (canvas3d.texUnitStateMap == null) {
+ canvas3d.texUnitStateMap =
+ new int[canvas3d.numTexUnitSupported];
+ }
+
+
+ canvas3d.drawingSurfaceObject.contextValidated();
+ canvas3d.screen.renderer.currentCtx = canvas3d.ctx;
+ initializeState();
+ canvas3d.ctxChanged = true;
+ canvas3d.canvasDirty = 0xffff;
+ // Update Appearance
+ updateState(rb, RenderMolecule.SURFACE);
+
+ canvas3d.currentLights = new
+ LightRetained[canvas3d.getNumCtxLights(canvas3d.ctx)];
+
+ for (int j=0; j<canvas3d.currentLights.length; j++) {
+ canvas3d.currentLights[j] = null;
+ }
+ }
+
+
+ canvas3d.makeCtxCurrent();
+
+ if ((dirtyMask & BUFFER_MODE) != 0) {
+ if (bufferOverride) {
+ canvas3d.setRenderMode(canvas3d.ctx, stereoMode,
+ canvas3d.useDoubleBuffer && !frontBufferRendering);
+ } else {
+ if (!canvas3d.isRunning) {
+ canvas3d.setRenderMode(canvas3d.ctx,
+ Canvas3D.FIELD_ALL,
+ canvas3d.useDoubleBuffer);
+ }
+ }
+ dirtyMask &= ~BUFFER_MODE;
+ }
+
+ Dimension size = canvas3d.getSize();
+ int winWidth = size.width;
+ int winHeight = size.height;
+
+ if (back.image != null && back.image.isByReference()) {
+ back.image.geomLock.getLock();
+ back.image.evaluateExtensions(canvas3d.extensionsSupported);
+ if (!VirtualUniverse.mc.isBackgroundTexture) {
+ canvas3d.clear(canvas3d.ctx,
+ back.color.x, back.color.y,
+ back.color.z, winWidth, winHeight, back.image,
+ back.imageScaleMode,
+ back.image != null?back.image.imageYdown[0]:null);
+ }
+ else {
+
+ // this is if the background image resizes with the canvas
+// Dimension size = null;
+// canvas3d.getSize(size);
+// int xmax = size.width;
+// int ymax = size.height;
+ if (objectId == -1) {
+ objectId = VirtualUniverse.mc.getTexture2DId();
+ }
+
+ canvas3d.textureclear(canvas3d.ctx,
+ back.xmax, back.ymax,
+ back.color.x, back.color.y,
+ back.color.z, winWidth, winHeight,
+ objectId, back.imageScaleMode, back.texImage, true);
+ }
+ back.image.geomLock.unLock();
+ }
+ else {
+ if (!VirtualUniverse.mc.isBackgroundTexture) {
+ canvas3d.clear(canvas3d.ctx,
+ back.color.x, back.color.y,
+ back.color.z, winWidth, winHeight, back.image,
+ back.imageScaleMode,
+ back.image != null?back.image.imageYdown[0]:null);
+ }
+ else {
+
+ // this is if the background image resizes with the canvas
+// Dimension size = null;
+// canvas3d.getSize(size);
+// int xmax = size.width;
+// int ymax = size.height;
+ if (objectId == -1) {
+ objectId = VirtualUniverse.mc.getTexture2DId();
+ }
+
+ canvas3d.textureclear(canvas3d.ctx,
+ back.xmax, back.ymax,
+ back.color.x, back.color.y,
+ back.color.z,
+ winWidth, winHeight,
+ objectId, back.imageScaleMode, back.texImage, true);
+ }
+ }
+
+ // Set the viewport and view matrices
+ if (!canvas3d.isRunning) {
+ CanvasViewCache cvCache = canvas3d.canvasViewCache;
+ canvas3d.setViewport(canvas3d.ctx,
+ 0, 0,
+ cvCache.getCanvasWidth(),
+ cvCache.getCanvasHeight());
+ if (bufferOverride && (stereoMode == STEREO_RIGHT)) {
+ canvas3d.setProjectionMatrix(canvas3d.ctx,
+ cvCache.getRightProjection().mat);
+ canvas3d.setModelViewMatrix(canvas3d.ctx,
+ cvCache.getRightVpcToEc().mat,
+ rb.vworldToVpc);
+ }
+ else {
+ canvas3d.setProjectionMatrix(canvas3d.ctx,
+ cvCache.getLeftProjection().mat);
+ canvas3d.setModelViewMatrix(canvas3d.ctx,
+ cvCache.getLeftVpcToEc().mat,
+ rb.vworldToVpc);
+ }
+ }
+
+ canvas3d.drawingSurfaceObject.unLock();
+ }
+ } catch (NullPointerException ne) {
+ canvas3d.drawingSurfaceObject.unLock();
+ throw ne;
+ }
+ }
+
+ // Method to update compTransform.
+ private void computeCompositeTransform() {
+ ViewPlatform vp;
+
+ if ((canvas3d == null) ||
+ (canvas3d.view == null) ||
+ (((vp = canvas3d.view.getViewPlatform()) == null)) ||
+ (((ViewPlatformRetained)(vp.retained)) == null)) {
+ compTransform.set(modelTransform);
+ return;
+ }
+
+ ViewPlatformRetained vpR = (ViewPlatformRetained)vp.retained;
+ if ((vpR == null) || (vpR.locale == null)) {
+ compTransform.set(modelTransform);
+ return;
+ }
+
+ HiResCoord localeHiRes = vpR.locale.hiRes;
+
+ if (localeHiRes.equals(hiRes)) {
+ compTransform.set(modelTransform);
+ } else {
+ Transform3D trans = new Transform3D();
+ Vector3d localeTrans = new Vector3d();
+ localeHiRes.difference( hiRes, localeTrans );
+ trans.setTranslation( localeTrans );
+ compTransform.mul(trans, modelTransform);
+ }
+ }
+
+ // Method to update the view cache in pure immediate mode
+ private void updateViewCache(RenderBin rb) {
+
+ ViewPlatform vp = canvas3d.view.getViewPlatform();
+
+ if (vp == null)
+ return;
+
+ ViewPlatformRetained vpR = (ViewPlatformRetained)vp.retained;
+
+ if (!canvas3d.isRunning) {
+ // in pure immediate mode, notify platform transform change
+ vpR.evaluateInitViewPlatformTransform();
+ }
+
+
+ // rb.setVworldToVpc(vp.getVworldToVpc());
+ // rb.setVpcToVworld(vp.getVpcToVworld());
+
+ // TODO: Fix this
+ rb.vpcToVworld = vpR.getVpcToVworld();
+ rb.vworldToVpc = vpR.getVworldToVpc();
+
+ canvas3d.updateViewCache(true, null, null, false);
+ }
+
+ void doDraw(Geometry geometry) {
+
+ boolean useAlpha;
+ GeometryRetained drawGeo;
+ GeometryArrayRetained geoRetained = null;
+
+
+ if (!canvas3d.firstPaintCalled || !visible) {
+ return;
+ }
+
+ RenderBin rb = canvas3d.view.renderBin;
+ int i, nlights, activeLights;
+ LightRetained light;
+ boolean lightingOn = true;
+
+ if (canvas3d.ctx == 0) {
+ // Force an initial clear if one has not yet been done
+ doClear();
+ }
+
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ J3dDebug.doAssert(canvas3d.ctx != 0, "canvas3d.ctx != 0");
+ }
+
+ // We need to catch NullPointerException when the dsi
+ // gets yanked from us during a remove.
+ try {
+ if (canvas3d.drawingSurfaceObject.renderLock()) {
+
+ // Make the context current
+ canvas3d.makeCtxCurrent();
+
+ if ((dirtyMask & BUFFER_MODE) != 0) {
+ if (bufferOverride) {
+ canvas3d.setRenderMode(canvas3d.ctx, stereoMode,
+ canvas3d.useDoubleBuffer && !frontBufferRendering);
+ } else {
+ canvas3d.setRenderMode(canvas3d.ctx, Canvas3D.FIELD_ALL,
+ canvas3d.useDoubleBuffer);
+ }
+ dirtyMask &= ~BUFFER_MODE;
+ }
+
+ CanvasViewCache cvCache = canvas3d.canvasViewCache;
+ Transform3D proj;
+
+// vpcToEc = cvCache.getLeftVpcToEc();
+ if (bufferOverride) {
+ switch(stereoMode) {
+ case STEREO_RIGHT:
+ vpcToEc = cvCache.getRightVpcToEc();
+ // TODO: move this under check for
+ // (dirtyMask & BUFFER_MODE) above after testing
+ // PureImmediate mode
+ canvas3d.setProjectionMatrix(canvas3d.ctx,
+ cvCache.getRightProjection().
+ mat);
+ break;
+ case STEREO_LEFT:
+ case STEREO_BOTH:
+ default:
+ vpcToEc = cvCache.getLeftVpcToEc();
+ // TODO: move this under check for
+ // (dirtyMask & BUFFER_MODE) above after testing
+ // PureImmediate mode
+ canvas3d.setProjectionMatrix(canvas3d.ctx,
+ cvCache.getLeftProjection().
+ mat);
+ }
+ }
+ else if (!canvas3d.isRunning ||
+ // vpcToEc is not set in the first frame
+ // of preRender() callback
+ (canvas3d.vpcToEc == null)) {
+ vpcToEc = cvCache.getLeftVpcToEc();
+ }
+ else {
+ vpcToEc = canvas3d.vpcToEc;
+ }
+
+ // referred by RenderQueue.updateState
+ // canvas3d.screen.renderer.vpcToEc = vpcToEc;
+ // rb.updateState(canvas3d.screen.renderer.rId, ro, canvas3d, true);
+
+
+ // this.drawTransform.mul(rb.vworldToVpc,
+ // this.compTransform);
+
+ boolean isNonUniformScale = !drawTransform.isCongruent();
+
+ int geometryType = 0;
+ switch (((GeometryRetained)geometry.retained).geoType) {
+ case GeometryRetained.GEO_TYPE_POINT_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET:
+ geometryType = RenderMolecule.POINT;
+ break;
+ case GeometryRetained.GEO_TYPE_LINE_SET:
+ case GeometryRetained.GEO_TYPE_LINE_STRIP_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ geometryType = RenderMolecule.LINE;
+ break;
+ case GeometryRetained.GEO_TYPE_RASTER:
+ geometryType = RenderMolecule.RASTER;
+ break;
+ case GeometryRetained.GEO_TYPE_COMPRESSED:
+ geometryType = RenderMolecule.COMPRESSED;
+
+ switch (((CompressedGeometryRetained)geometry.retained).getBufferType()) {
+ case CompressedGeometryHeader.POINT_BUFFER:
+ geometryType |= RenderMolecule.POINT ;
+ break ;
+ case CompressedGeometryHeader.LINE_BUFFER:
+ geometryType |= RenderMolecule.LINE ;
+ break ;
+ default:
+ case CompressedGeometryHeader.TRIANGLE_BUFFER:
+ geometryType |= RenderMolecule.SURFACE ;
+ break ;
+ }
+ break;
+ default:
+ geometryType = RenderMolecule.SURFACE;
+ break;
+ }
+
+ useAlpha = updateState(rb, geometryType);
+
+ canvas3d.setModelViewMatrix(canvas3d.ctx,
+ vpcToEc.mat,
+ rb.vworldToVpc);
+ updateLightAndFog();
+
+ updateModelClip(rb.vworldToVpc);
+
+ this.drawTransform.mul(rb.vworldToVpc, this.compTransform);
+ canvas3d.setModelViewMatrix(canvas3d.ctx,
+ vpcToEc.mat, this.drawTransform);
+
+ if (geometry.retained instanceof GeometryArrayRetained) {
+ geoRetained = (GeometryArrayRetained)geometry.retained;
+
+ geoRetained.geomLock.getLock();
+ // If the geometry is by refernence, then see if we are using alpha
+ // and that there is no global alpha sun extension defined ..
+ if ((( geoRetained.vertexFormat & GeometryArray.BY_REFERENCE)!=0) &&
+ (geoRetained.c4fAllocated == 0) &&
+ ((geoRetained.vertexFormat & GeometryArray.COLOR) != 0) &&
+ useAlpha && (canvas3d.extensionsSupported &Canvas3D.SUN_GLOBAL_ALPHA) == 0 ) {
+
+ if ((geoRetained.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ geoRetained.setupMirrorInterleavedColorPointer(true);
+ }
+ else {
+ geoRetained.setupMirrorColorPointer((geoRetained.vertexType & GeometryArrayRetained.COLOR_DEFINED),true);
+ }
+ }
+
+ if ((geometry.retained instanceof IndexedGeometryArrayRetained) &&
+ ((((GeometryArrayRetained)geometry.retained).vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0)) {
+ if (geoRetained.dirtyFlag != 0) {
+ geoRetained.mirrorGeometry = (GeometryRetained)
+ ((IndexedGeometryArrayRetained)geoRetained).cloneNonIndexedGeometry();
+ // Change the source geometry dirtyFlag
+ // drawGeo.execute() will change the
+ // destination geometry dirtyFlag only.
+ geoRetained.dirtyFlag = 0;
+ }
+ drawGeo = (GeometryRetained)geoRetained.mirrorGeometry;
+ } else {
+ drawGeo = geoRetained;
+ }
+
+ geoRetained.setVertexFormat(false, ignoreVertexColors, canvas3d.ctx );
+
+ } else if (geometry.retained instanceof Text3DRetained) {
+ ((Text3DRetained)geometry.retained).setModelViewMatrix(
+ vpcToEc, this.drawTransform);
+ drawGeo = (GeometryRetained)geometry.retained;
+ } else if (geometry.retained instanceof RasterRetained) {
+ ImageComponent2DRetained img = ((RasterRetained)geometry.retained).image;
+ if (img != null && img.isByReference()) {
+ img.geomLock.getLock();
+ img.evaluateExtensions(canvas3d.extensionsSupported);
+ img.geomLock.unLock();
+ }
+ drawGeo = (GeometryRetained)geometry.retained;
+ } else {
+ drawGeo = (GeometryRetained)geometry.retained;
+ }
+
+ if (!toSimulateMultiTex) {
+ drawGeo.execute(canvas3d, null, isNonUniformScale,
+ false, alpha,
+ ((canvas3d.view.getScreens()).length > 1),
+ canvas3d.screen.screen,
+ ignoreVertexColors,
+ -1);
+ } else {
+ // TODO: need to leverage the code in textureBin
+ boolean startToSimulate = false;
+ if (numActiveTexUnit < 1) {
+ // no active texture unit
+ drawGeo.execute(canvas3d, null, isNonUniformScale,
+ false, alpha,
+ ((canvas3d.view.getScreens()).length > 1),
+ canvas3d.screen.screen,
+ ignoreVertexColors,
+ 0);
+ } else if (numActiveTexUnit == 1) {
+ // one active texture unit
+ drawGeo.execute(canvas3d, null, isNonUniformScale,
+ false, alpha,
+ ((canvas3d.view.getScreens()).length > 1),
+ canvas3d.screen.screen,
+ ignoreVertexColors,
+ lastActiveTexUnitIndex);
+ } else {
+ // simulate multiple texture units
+ AppearanceRetained app =
+ (AppearanceRetained)appearance.retained;
+
+ // first turn off fog
+ if (fog != null)
+ canvas3d.setFogEnableFlag(canvas3d.ctx, false);
+
+ for (i = 0; i < app.texUnitState.length; i++) {
+ if (app.texUnitState[i] != null &&
+ app.texUnitState[i].isTextureEnabled()) {
+
+ // turn on fog for the last pass
+ if (i == lastActiveTexUnitIndex)
+ canvas3d.setFogEnableFlag(canvas3d.ctx, true);
+
+ app.texUnitState[i].updateNative(-1, canvas3d,
+ false, startToSimulate);
+
+ startToSimulate = true;
+ drawGeo.execute(canvas3d, null,
+ isNonUniformScale, false, alpha,
+ ((canvas3d.view.getScreens()).length > 1),
+ canvas3d.screen.screen,
+ ignoreVertexColors,
+ i);
+ }
+ }
+
+ // adjust the depth test back to what it was
+ // and adjust the blend func to what it it was
+ if (startToSimulate) {
+ app.transparencyAttributes.updateNative(
+ canvas3d.ctx, alpha, geometryType,
+ polygonMode, lineAA, pointAA);
+ }
+
+ if (fog != null) {
+ canvas3d.setFogEnableFlag(canvas3d.ctx, true);
+ }
+ }
+ }
+ if (geoRetained != null)
+ geoRetained.geomLock.unLock();
+
+ canvas3d.drawingSurfaceObject.unLock();
+ }
+ } catch (NullPointerException ne) {
+ canvas3d.drawingSurfaceObject.unLock();
+ throw ne;
+ }
+ }
+
+ /**
+ * Draw the specified Geometry component object.
+ * @param geometry the Geometry object to draw.
+ */
+ public void draw(Geometry geometry) {
+ if ((canvas3d.view == null) || (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active)) {
+ return;
+ } else if (Thread.currentThread() == canvas3d.screen.renderer) {
+ doDraw(geometry);
+ } else {
+ if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.DRAW,
+ geometry, null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.DRAW, geometry,
+ null);
+ }
+ }
+ }
+
+ /**
+ * Draw the specified Shape3D leaf node object. This is
+ * a convenience method that is identical to calling the
+ * setAppearance(Appearance) and draw(Geometry) methods
+ * passing the appearance and geometry component objects of
+ * the specified shape node as arguments.
+ * @param shape the Shape3D node containing the Appearance component
+ * object to set and Geometry component object to draw
+ * @exception IllegalSharingException if the Shape3D node
+ * is part of or is subsequently made part of a live scene graph.
+ */
+ public void draw(Shape3D shape) {
+ if (shape.isLive()) {
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D26"));
+ }
+ ((Shape3DRetained)shape.retained).setInImmCtx(true);
+ setAppearance(shape.getAppearance());
+ draw(shape.getGeometry());
+ }
+
+ /**
+ * Native method for readRaster
+ */
+ native void readRasterNative(long d3dctx,
+ int type, int xSrcOffset, int ySrcOffset,
+ int width, int height, int hCanvas, int format,
+ ImageComponentRetained image,
+ DepthComponentRetained depth,
+ GraphicsContext3D ctx);
+
+ /**
+ * Read an image from the frame buffer and copy it into the
+ * ImageComponent and/or DepthComponent
+ * objects referenced by the specified Raster object.
+ * All parameters of the Raster object and the component ImageComponent
+ * and/or DepthComponentImage objects must be set to the desired values
+ * prior to calling this method. These values determine the location,
+ * size, and format of the pixel data that is read.
+ * This method calls <code>flush(true)</code> prior to reading the
+ * frame buffer.
+ *
+ * @param raster the Raster object used to read the
+ * contents of the frame buffer
+ *
+ * @exception IllegalArgumentException if the Raster's
+ * ImageComponent2D is in by-reference mode and its RenderedImage
+ * is not an instance of a BufferedImage.
+ *
+ * @exception IllegalSharingException if the Raster object
+ * is part of a live scene graph.
+ *
+ * @see #flush
+ * @see ImageComponent
+ * @see DepthComponent
+ */
+ public void readRaster(Raster raster) {
+ if ((canvas3d.view == null) || (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active)) {
+ return;
+ } else if (Thread.currentThread() == canvas3d.screen.renderer) {
+ doReadRaster(raster);
+ } else if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ readRasterReady = false;
+ sendRenderMessage(false, GraphicsContext3D.READ_RASTER, raster, null);
+ while (!readRasterReady) {
+ MasterControl.threadYield();
+ }
+ } else {
+ // call from user thread
+ readRasterReady = false;
+ sendRenderMessage(true, GraphicsContext3D.READ_RASTER, raster, null);
+ while (!readRasterReady) {
+ MasterControl.threadYield();
+ }
+ }
+ }
+
+
+
+ void doReadRaster(Raster raster) {
+
+
+ if (!canvas3d.firstPaintCalled) {
+ readRasterReady = true;
+ return;
+ }
+
+ RasterRetained ras = (RasterRetained)raster.retained;
+ Dimension canvasSize = canvas3d.getSize();
+ int format = 0; // Not use in case of DepthComponent read
+
+ if (raster.isLive()) {
+ readRasterReady = true;
+ throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D21"));
+ }
+
+ // TODO: implement illegal argument exception
+ /*
+ if (ras.image.byReference &&
+ !(ras.image.imageReference instanceof BufferedImage)) {
+
+ throw new IllegalArgumentException(...);
+ }
+ */
+
+ if (canvas3d.ctx == 0) {
+ // Force an initial clear if one has not yet been done
+ doClear();
+ }
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ J3dDebug.doAssert(canvas3d.ctx != 0, "canvas3d.ctx != 0");
+ }
+
+
+ // allocate read buffer space
+ if ( (ras.type & Raster.RASTER_COLOR) != 0) {
+ int bpp = ras.image.getEffectiveBytesPerPixel();
+ int size = ras.image.height * ras.image.width
+ * bpp;
+ format = ras.image.getEffectiveFormat();
+ if ((ras.width > ras.image.width) ||
+ (ras.height > ras.image.height)) {
+ throw new RuntimeException(J3dI18N.getString("GraphicsContext3D27"));
+ }
+ if (byteBuffer.length < size)
+ byteBuffer = new byte[size];
+ }
+
+ if ( (ras.type & Raster.RASTER_DEPTH) != 0) {
+ int size = ras.depthComponent.height * ras.depthComponent.width;
+ if (ras.depthComponent.type
+ == DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT) {
+ if (floatBuffer.length < size)
+ floatBuffer = new float[size];
+ } else { // type INT or NATIVE
+ if (intBuffer.length < size)
+ intBuffer = new int[size];
+ }
+ if ((ras.width > ras.depthComponent.width) ||
+ (ras.height > ras.depthComponent.height)) {
+ throw new RuntimeException(J3dI18N.getString("GraphicsContext3D28"));
+ }
+ }
+
+ if ( (ras.type & Raster.RASTER_COLOR) != 0) {
+ // If by reference, check if a copy needs to be made
+ // and also evaluate the storedFormat ..
+ if (ras.image.isByReference()) {
+ ras.image.geomLock.getLock();
+ ras.image.evaluateExtensions(canvas3d.extensionsSupported);
+ ras.image.geomLock.unLock();
+ }
+ }
+
+ // We need to catch NullPointerException when the dsi
+ // gets yanked from us during a remove.
+ try {
+ if (canvas3d.drawingSurfaceObject.renderLock()) {
+ // Make the context current and read the raster information
+ canvas3d.makeCtxCurrent();
+ canvas3d.syncRender(canvas3d.ctx, true);
+ readRasterNative(canvas3d.ctx,
+ ras.type, ras.xSrcOffset, ras.ySrcOffset,
+ ras.width, ras.height, canvasSize.height, format,
+ ras.image, ras.depthComponent, this);
+ canvas3d.drawingSurfaceObject.unLock();
+ }
+ } catch (NullPointerException ne) {
+ canvas3d.drawingSurfaceObject.unLock();
+ throw ne;
+ }
+
+ // flip color image: yUp -> yDown and convert to BufferedImage
+ if ( (ras.type & Raster.RASTER_COLOR) != 0) {
+ ras.image.retrieveImage(byteBuffer, ras.width, ras.height);
+ }
+
+ if ( (ras.type & Raster.RASTER_DEPTH) != 0) {
+ if (ras.depthComponent.type ==
+ DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT)
+ ((DepthComponentFloatRetained)ras.depthComponent).retrieveDepth(
+ floatBuffer, ras.width, ras.height);
+ else if (ras.depthComponent.type ==
+ DepthComponentRetained.DEPTH_COMPONENT_TYPE_INT)
+ ((DepthComponentIntRetained)ras.depthComponent).retrieveDepth(
+ intBuffer, ras.width, ras.height);
+ else if (ras.depthComponent.type ==
+ DepthComponentRetained.DEPTH_COMPONENT_TYPE_NATIVE)
+ ((DepthComponentNativeRetained)ras.depthComponent).retrieveDepth(
+ intBuffer, ras.width, ras.height);
+ }
+ readRasterReady = true;
+ }
+
+ /**
+ * Flushes all previously executed rendering operations to the
+ * drawing buffer for this 3D graphics context.
+ *
+ * @param wait flag indicating whether or not to wait for the
+ * rendering to be complete before returning from this call.
+ *
+ * @since Java 3D 1.2
+ */
+ public void flush(boolean wait) {
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ||
+ (!canvas3d.view.active) ||
+ (Thread.currentThread() == canvas3d.screen.renderer)) {
+ doFlush(wait);
+ } else {
+ Boolean waitArg = (wait ? Boolean.TRUE : Boolean.FALSE);
+
+ if (Thread.currentThread() ==
+ canvas3d.view.universe.behaviorScheduler) {
+ sendRenderMessage(false, GraphicsContext3D.FLUSH, waitArg,
+ null);
+ } else {
+ sendRenderMessage(true, GraphicsContext3D.FLUSH, waitArg,
+ null);
+ }
+ if (wait && canvas3d.active && canvas3d.isRunningStatus &&
+ !canvas3d.offScreen) {
+ // No need to wait if renderer thread is not schedule
+ runMonitor(J3dThread.WAIT);
+ }
+ }
+ }
+
+ void doFlush(boolean wait) {
+ try {
+ if (canvas3d.drawingSurfaceObject.renderLock()) {
+ canvas3d.syncRender(canvas3d.ctx, wait);
+ canvas3d.drawingSurfaceObject.unLock();
+ if (wait) {
+ runMonitor(J3dThread.NOTIFY);
+ }
+ }
+ } catch (NullPointerException ne) {
+ canvas3d.drawingSurfaceObject.unLock();
+ throw ne;
+ }
+ }
+
+ void updateLightAndFog() {
+ int enableMask = 0;
+ int i;
+ sceneAmbient.x = 0.0f;
+ sceneAmbient.y = 0.0f;
+ sceneAmbient.z = 0.0f;
+
+ int n = 0;
+ int nLight = lights.size();;
+ for (i = 0; i < nLight;i++) {
+ LightRetained lt = (LightRetained)((Light)lights.get(i)).retained;
+ if (lt instanceof AmbientLightRetained) {
+ sceneAmbient.x += lt.color.x;
+ sceneAmbient.y += lt.color.y;
+ sceneAmbient.z += lt.color.z;
+ continue;
+ }
+
+ lt.update(canvas3d.ctx, n,
+ canvas3d.canvasViewCache.getVworldToCoexistenceScale());
+ if (lt.lightOn)
+ enableMask |= (1 << n);
+ n++;
+ }
+ if (sceneAmbient.x > 1.0f) {
+ sceneAmbient.x = 1.0f;
+ }
+ if (sceneAmbient.y > 1.0f) {
+ sceneAmbient.y = 1.0f;
+ }
+ if (sceneAmbient.z > 1.0f) {
+ sceneAmbient.z = 1.0f;
+ }
+
+ canvas3d.canvasDirty |= Canvas3D.AMBIENTLIGHT_DIRTY;
+ canvas3d.setSceneAmbient(canvas3d.ctx, sceneAmbient.x,
+ sceneAmbient.y, sceneAmbient.z);
+
+ if (canvas3d.enableMask != enableMask) {
+ canvas3d.canvasDirty |= Canvas3D.LIGHTENABLES_DIRTY;
+ // TODO: 32 => renderBin.maxLights
+ canvas3d.setLightEnables(canvas3d.ctx, enableMask, 32);
+ canvas3d.enableMask = enableMask;
+ }
+
+ canvas3d.lightBin = null;
+
+
+ // Mark the envset as dirty for the canvas for scene graph rendering
+ canvas3d.environmentSet = null;
+
+ if (fog != null) {
+ if (fog.retained != canvas3d.fog) {
+ ((FogRetained)fog.retained).update(canvas3d.ctx,
+ canvas3d.canvasViewCache.getVworldToCoexistenceScale());
+ canvas3d.fog = (FogRetained) fog.retained;
+ canvas3d.canvasDirty |= Canvas3D.FOG_DIRTY;
+ }
+ } else { // Turn off fog
+ if (canvas3d.fog != null) {
+ canvas3d.setFogEnableFlag(canvas3d.ctx, false);
+ canvas3d.fog = null;
+ canvas3d.canvasDirty |= Canvas3D.FOG_DIRTY;
+ }
+ }
+ }
+
+ void updateModelClip(Transform3D vworldToVpc) {
+ if (modelClip != null) {
+ int enableMask = 0;
+ for (int i = 0; i < 6; i++) {
+ if (((ModelClipRetained)modelClip.retained).enables[i])
+ enableMask |= 1 << i;
+ }
+ // planes are already transformed to eye coordinates
+ // in immediate mode
+ if (enableMask != 0) {
+ this.drawTransform.mul(vworldToVpc, this.modelClipTransform);
+ canvas3d.setModelViewMatrix(canvas3d.ctx, vpcToEc.mat,
+ this.drawTransform);
+ }
+ ((ModelClipRetained)modelClip.retained).update(
+ canvas3d.ctx, enableMask,
+ this.drawTransform);
+ canvas3d.canvasDirty |= Canvas3D.MODELCLIP_DIRTY;
+ canvas3d.modelClip = (ModelClipRetained) modelClip.retained;
+ } else {
+ if (canvas3d.modelClip != null) {
+ canvas3d.disableModelClip(canvas3d.ctx);
+ canvas3d.modelClip = null;
+ canvas3d.canvasDirty |= Canvas3D.MODELCLIP_DIRTY;
+ }
+ }
+ }
+
+
+
+ boolean updateState(RenderBin rb, int geometryType) {
+
+ boolean useAlpha = false;;
+ toSimulateMultiTex = true;
+ numActiveTexUnit = 0;
+ lastActiveTexUnitIndex = 0;
+
+ // Update Appearance
+ if (appearance != null) {
+ AppearanceRetained app = (AppearanceRetained) appearance.retained;
+
+ // If the material is not null then check if the one in the canvas
+ // is equivalent to the one being sent down. If Yes, do nothing
+ // Otherwise, cache the sent down material and mark the canvas
+ // dirty flag so that the compiled/compiled-retained rendering
+ // catches the change
+ // if material != null, we will need to load the material
+ // parameter again, because the apps could have changed
+ // the material parameter
+
+ if (app.material != null) {
+ app.material.updateNative(canvas3d.ctx,
+ red,green,blue,
+ alpha,enableLighting);
+ canvas3d.material = app.material;
+ canvas3d.canvasDirty |= Canvas3D.MATERIAL_DIRTY;
+ } else {
+ if (canvas3d.material != null) {
+ canvas3d.updateMaterial(canvas3d.ctx,
+ red, green, blue, alpha);
+ canvas3d.material = null;
+ canvas3d.canvasDirty |= Canvas3D.MATERIAL_DIRTY;
+ }
+ }
+
+ int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit();
+
+ if (app.texUnitState != null) {
+ boolean d3dBlendMode = false;
+
+ TextureUnitStateRetained tus;
+
+ for (int i = 0; i < app.texUnitState.length; i++) {
+ tus = app.texUnitState[i];
+ if (tus != null && tus.isTextureEnabled()) {
+ numActiveTexUnit++;
+ lastActiveTexUnitIndex = i;
+ useAlpha = useAlpha ||
+ (tus.texAttrs.textureMode ==
+ TextureAttributes.BLEND);
+ if (tus.needBlend2Pass(canvas3d)) {
+ // use multi-pass if one of the stage use blend mode
+ d3dBlendMode = true;
+ }
+ }
+ }
+
+ if (canvas3d.numTexUnitSupported >= numActiveTexUnit &&
+ canvas3d.multiTexAccelerated && !d3dBlendMode) {
+
+ int j = 0;
+
+ // update all active texture unit states
+
+ for (int i = 0; i < app.texUnitState.length; i++) {
+ if ((app.texUnitState[i] != null) &&
+ app.texUnitState[i].isTextureEnabled()) {
+ app.texUnitState[i].updateNative(j, canvas3d,
+ false, false);
+ canvas3d.setTexUnitStateMap(i, j++);
+ }
+ }
+
+ // reset the remaining texture units
+
+ for (int i = j; i < prevNumActiveTexUnit; i++) {
+ if (canvas3d.texUnitState[i].texture != null) {
+ canvas3d.resetTexture(canvas3d.ctx, i);
+ canvas3d.texUnitState[i].texture = null;
+ }
+ }
+
+ // set the number active texture unit in Canvas3D
+ canvas3d.setNumActiveTexUnit(numActiveTexUnit);
+
+ // set the active texture unit back to 0
+ canvas3d.activeTextureUnit(canvas3d.ctx, 0);
+
+ toSimulateMultiTex = false;
+
+ } else {
+
+ // will fall back to the multi-pass case;
+ // reset all the texture units first
+
+ for (int i = 0; i < prevNumActiveTexUnit; i++) {
+ if (canvas3d.texUnitState[i].texture != null) {
+ canvas3d.resetTexture(canvas3d.ctx, i);
+ canvas3d.texUnitState[i].texture = null;
+ }
+ }
+ }
+ } else {
+ // if texUnitState is null, let's disable
+ // all texture units first
+ if (canvas3d.multiTexAccelerated) {
+ if (canvas3d.texUnitState != null) {
+ for (int i = 0; i < prevNumActiveTexUnit; i++) {
+ TextureUnitStateRetained tur = canvas3d.texUnitState[i];
+ if ((tur != null) && (tur.texture != null)) {
+ canvas3d.resetTexture(canvas3d.ctx, i);
+ canvas3d.texUnitState[i].texture = null;
+ }
+ }
+ }
+
+ // set the active texture unit back to 0
+ canvas3d.activeTextureUnit(canvas3d.ctx, 0);
+ }
+
+ if ((canvas3d.texUnitState != null) &&
+ (canvas3d.texUnitState[0] != null) &&
+ (canvas3d.texUnitState[0].texture != app.texture)) {
+
+ // If the image is by reference, check if the image
+ // should be processed
+ if (app.texture != null) {
+ for (int f = 0; f < app.texture.numFaces; f++) {
+ for (int k = 0; k < app.texture.maxLevels; k++) {
+ if (app.texture.images[f][k].isByReference()) {
+ app.texture.images[f][k].geomLock.getLock();
+ app.texture.images[f][k].evaluateExtensions(canvas3d.extensionsSupported);
+ app.texture.images[f][k].geomLock.unLock();
+ }
+ }
+ }
+ app.texture.updateNative(canvas3d);
+ canvas3d.setTexUnitStateMap(0, 0);
+ canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ numActiveTexUnit = 1;
+ lastActiveTexUnitIndex = 0;
+ }
+ else {
+ numActiveTexUnit = 0;
+ canvas3d.resetTexture(canvas3d.ctx, -1);
+ canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ }
+
+ canvas3d.texUnitState[0].texture = app.texture;
+ }
+
+ // set the number active texture unit in Canvas3D
+ canvas3d.setNumActiveTexUnit(numActiveTexUnit);
+
+ if (app.texCoordGeneration != null) {
+ app.texCoordGeneration.updateNative(canvas3d);
+ canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ if ((canvas3d.texUnitState != null) &&
+ (canvas3d.texUnitState[0] != null)) {
+ canvas3d.texUnitState[0].texGen = app.texCoordGeneration;
+ }
+ }
+ else {
+ // If the canvas does not alreadt have a null texCoordGeneration
+ // load the default
+ if ((canvas3d.texUnitState != null) &&
+ (canvas3d.texUnitState[0] != null) &&
+ (canvas3d.texUnitState[0].texGen != null)) {
+ canvas3d.resetTexCoordGeneration(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ canvas3d.texUnitState[0].texGen = app.texCoordGeneration;
+ }
+ }
+
+
+ if (app.textureAttributes != null) {
+ if ((canvas3d.texUnitState != null) &&
+ (canvas3d.texUnitState[0] != null)) {
+
+ if (canvas3d.texUnitState[0].texture != null) {
+ app.textureAttributes.updateNative(canvas3d, false,
+ canvas3d.texUnitState[0].texture.format);
+ } else {
+ app.textureAttributes.updateNative(canvas3d, false,
+ Texture.RGBA);
+ }
+ canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ canvas3d.texUnitState[0].texAttrs =
+ app.textureAttributes;
+ }
+ }
+ else {
+ // If the canvas does not already have a null texAttribute
+ // load the default if necessary
+ if ((canvas3d.texUnitState != null) &&
+ (canvas3d.texUnitState[0] != null) &&
+ (canvas3d.texUnitState[0].texAttrs != null)) {
+ canvas3d.resetTextureAttributes(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ canvas3d.texUnitState[0].texAttrs = null;
+ }
+ }
+ }
+
+ if (app.coloringAttributes != null) {
+ app.coloringAttributes.updateNative(canvas3d.ctx,
+ dRed, dBlue,
+ dGreen,
+ alpha, enableLighting);
+ canvas3d.canvasDirty |= Canvas3D.COLORINGATTRS_DIRTY;
+ canvas3d.coloringAttributes = app.coloringAttributes;
+ }
+ else {
+ if (canvas3d.coloringAttributes != null) {
+ canvas3d.resetColoringAttributes(canvas3d.ctx,
+ red, green, blue, alpha,
+ enableLighting);
+ canvas3d.canvasDirty |= Canvas3D.COLORINGATTRS_DIRTY;
+ canvas3d.coloringAttributes = null;
+ }
+ }
+
+
+ if (app.transparencyAttributes != null) {
+ app.transparencyAttributes.updateNative(canvas3d.ctx,
+ alpha, geometryType,
+ polygonMode,
+ lineAA, pointAA);
+ canvas3d.canvasDirty |= Canvas3D.TRANSPARENCYATTRS_DIRTY;
+ canvas3d.transparency = app.transparencyAttributes;
+
+ useAlpha = useAlpha || ((app.transparencyAttributes.transparencyMode !=
+ TransparencyAttributes.NONE)
+ &&
+ (VirtualUniverse.mc.isD3D()
+ ||
+ (!VirtualUniverse.mc.isD3D() &&
+ (app.transparencyAttributes. transparencyMode !=
+ TransparencyAttributes.SCREEN_DOOR))));
+ } else {
+ canvas3d.resetTransparency(canvas3d.ctx, geometryType,
+ polygonMode, lineAA, pointAA);
+ canvas3d.canvasDirty |= Canvas3D.TRANSPARENCYATTRS_DIRTY;
+ canvas3d.transparency = null;
+ }
+
+
+ if (app.renderingAttributes != null) {
+ ignoreVertexColors =app.renderingAttributes.ignoreVertexColors;
+ app.renderingAttributes.updateNative(canvas3d.ctx,
+ canvas3d.depthBufferWriteEnableOverride,
+ canvas3d.depthBufferEnableOverride);
+ canvas3d.canvasDirty |= Canvas3D.ATTRIBUTEBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ canvas3d.renderingAttrs = app.renderingAttributes;
+
+ useAlpha = useAlpha ||
+ (app.renderingAttributes.alphaTestFunction
+ != RenderingAttributes.ALWAYS);
+ } else {
+ // If the canvas does not alreadt have a null renderingAttrs
+ // load the default
+ ignoreVertexColors = false;
+ if (canvas3d.renderingAttrs != null) {
+ canvas3d.resetRenderingAttributes(canvas3d.ctx,
+ canvas3d.depthBufferWriteEnableOverride,
+ canvas3d.depthBufferEnableOverride);
+ canvas3d.canvasDirty |= Canvas3D.ATTRIBUTEBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ canvas3d.renderingAttrs = null;
+ }
+ }
+
+
+ if (app.polygonAttributes != null) {
+ app.polygonAttributes.updateNative(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.POLYGONATTRS_DIRTY;
+ canvas3d.polygonAttributes = app.polygonAttributes;
+ } else {
+ // If the canvas does not alreadt have a null polygonAttr
+ // load the default
+ if (canvas3d.polygonAttributes != null) {
+ canvas3d.resetPolygonAttributes(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.POLYGONATTRS_DIRTY;
+ canvas3d.polygonAttributes = null;
+ }
+ }
+
+
+
+ if (app.lineAttributes != null) {
+ app.lineAttributes.updateNative(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.LINEATTRS_DIRTY;
+ canvas3d.lineAttributes = app.lineAttributes;
+ } else {
+ // If the canvas does not already have a null lineAttr
+ // load the default
+ if (canvas3d.lineAttributes != null) {
+ canvas3d.resetLineAttributes(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.LINEATTRS_DIRTY;
+ canvas3d.lineAttributes = null;
+ }
+ }
+
+
+
+ if (app.pointAttributes != null) {
+ app.pointAttributes.updateNative(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.POINTATTRS_DIRTY;
+ canvas3d.pointAttributes = app.pointAttributes;
+ } else {
+ // If the canvas does not already have a null pointAttr
+ // load the default
+ if (canvas3d.pointAttributes != null) {
+ canvas3d.resetPointAttributes(canvas3d.ctx);
+ canvas3d.canvasDirty |= Canvas3D.POINTATTRS_DIRTY;
+ canvas3d.pointAttributes = null;
+ }
+ }
+
+ canvas3d.appearance = app;
+
+ } else {
+ if (canvas3d.appearance != null) {
+ resetAppearance();
+ canvas3d.appearance = null;
+ }
+ }
+
+
+ return (useAlpha );
+ }
+
+ void initializeState() {
+
+ canvas3d.setSceneAmbient(canvas3d.ctx, 0.0f, 0.0f, 0.0f);
+ canvas3d.disableFog(canvas3d.ctx);
+ canvas3d.resetRenderingAttributes(canvas3d.ctx,false, false);
+
+ // reset the previously enabled texture units
+
+ int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit();
+
+ if (prevNumActiveTexUnit > 0) {
+ for (int i = 0; i < prevNumActiveTexUnit; i++) {
+ if (canvas3d.texUnitState[i].texture != null) {
+ canvas3d.resetTexture(canvas3d.ctx, i);
+ canvas3d.texUnitState[i].texture = null;
+ }
+ if (canvas3d.texUnitState[i].texAttrs != null) {
+ canvas3d.resetTextureAttributes(canvas3d.ctx);
+ canvas3d.texUnitState[i].texAttrs = null;
+ }
+ if (canvas3d.texUnitState[i].texGen != null) {
+ canvas3d.resetTexCoordGeneration(canvas3d.ctx);
+ canvas3d.texUnitState[i].texGen = null;
+ }
+ canvas3d.texUnitState[i].mirror = null;
+ }
+ canvas3d.setNumActiveTexUnit(0);
+ }
+
+ canvas3d.resetPolygonAttributes(canvas3d.ctx);
+ canvas3d.resetLineAttributes(canvas3d.ctx);
+ canvas3d.resetPointAttributes(canvas3d.ctx);
+ canvas3d.resetTransparency(canvas3d.ctx, RenderMolecule.SURFACE,
+ PolygonAttributes.POLYGON_FILL,
+ false, false);
+ canvas3d.resetColoringAttributes(canvas3d.ctx,1.0f, 1.0f, 1.0f, 1.0f, false);
+ canvas3d.updateMaterial(canvas3d.ctx, 1.0f, 1.0f, 1.0f, 1.0f);
+ }
+
+
+ void resetAppearance() {
+
+ if (canvas3d.material != null) {
+ canvas3d.updateMaterial(canvas3d.ctx,
+ red, green, blue, alpha);
+ canvas3d.material = null;
+ canvas3d.canvasDirty |= Canvas3D.MATERIAL_DIRTY;
+ }
+
+ // reset the previously enabled texture units
+
+ int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit();
+
+ if (prevNumActiveTexUnit > 0) {
+ for (int i = 0; i < prevNumActiveTexUnit; i++) {
+ if (canvas3d.texUnitState[i].texture != null) {
+ canvas3d.resetTexture(canvas3d.ctx, i);
+ canvas3d.texUnitState[i].texture = null;
+ }
+ if (canvas3d.texUnitState[i].texAttrs != null) {
+ canvas3d.resetTextureAttributes(canvas3d.ctx);
+ canvas3d.texUnitState[i].texAttrs = null;
+ }
+ if (canvas3d.texUnitState[i].texGen != null) {
+ canvas3d.resetTexCoordGeneration(canvas3d.ctx);
+ canvas3d.texUnitState[i].texGen = null;
+ }
+ canvas3d.texUnitState[i].mirror = null;
+ }
+ canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ canvas3d.setNumActiveTexUnit(0);
+ }
+
+ if (canvas3d.coloringAttributes != null) {
+ canvas3d.resetColoringAttributes(canvas3d.ctx,
+ red, green, blue, alpha, enableLighting);
+ canvas3d.coloringAttributes = null;
+ canvas3d.canvasDirty |= Canvas3D.COLORINGATTRS_DIRTY;
+ }
+
+ if (canvas3d.transparency != null) {
+ canvas3d.resetTransparency(canvas3d.ctx, RenderMolecule.SURFACE,
+ PolygonAttributes.POLYGON_FILL, lineAA, pointAA);
+ canvas3d.transparency = null;
+ canvas3d.canvasDirty |= Canvas3D.TRANSPARENCYATTRS_DIRTY;
+ }
+
+ if (canvas3d.renderingAttrs != null) {
+ ignoreVertexColors = false;
+ canvas3d.resetRenderingAttributes(canvas3d.ctx,
+ canvas3d.depthBufferWriteEnableOverride,
+ canvas3d.depthBufferEnableOverride);
+ canvas3d.renderingAttrs = null;
+ canvas3d.canvasDirty |= Canvas3D.ATTRIBUTEBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ }
+
+ if (canvas3d.polygonAttributes != null) {
+ canvas3d.resetPolygonAttributes(canvas3d.ctx);
+ canvas3d.polygonAttributes = null;
+ canvas3d.canvasDirty |= Canvas3D.POLYGONATTRS_DIRTY;
+ }
+
+ if (canvas3d.lineAttributes != null) {
+ canvas3d.resetLineAttributes(canvas3d.ctx);
+ canvas3d.lineAttributes = null;
+ canvas3d.canvasDirty |= Canvas3D.LINEATTRS_DIRTY;
+ }
+
+ if (canvas3d.pointAttributes != null) {
+ canvas3d.resetPointAttributes(canvas3d.ctx);
+ canvas3d.pointAttributes = null;
+ canvas3d.canvasDirty |= Canvas3D.POINTATTRS_DIRTY;
+ }
+ }
+
+ void sendRenderMessage(boolean renderRun, int command,
+ Object arg1, Object arg2) {
+
+ // send a message to the request renderer
+
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = canvas3d;
+ renderMessage.args[1] = getImmCommand(command);
+ renderMessage.args[2] = arg1;
+ renderMessage.args[3] = arg2;
+
+ while (!canvas3d.view.inRenderThreadData) {
+ // wait until the renderer thread data in added in
+ // MC:RenderThreadData array ready to receive message
+ MasterControl.threadYield();
+ }
+
+ canvas3d.screen.renderer.rendererStructure.addMessage(renderMessage);
+
+ if (renderRun) {
+ // notify mc that there is work to do
+ VirtualUniverse.mc.sendRunMessage(canvas3d.view, J3dThread.RENDER_THREAD);
+ } else {
+ // notify mc that there is work for the request renderer
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+ }
+
+ void sendSoundMessage(int command, Object arg1, Object arg2) {
+ if ((canvas3d.view == null) ||
+ (canvas3d.view.universe == null) ) {
+ return;
+ }
+ // send a message to the request sound scheduling
+ J3dMessage soundMessage = VirtualUniverse.mc.getMessage();
+ soundMessage.threads = J3dThread.SOUND_SCHEDULER;
+ soundMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ soundMessage.universe = canvas3d.view.universe;
+ soundMessage.view = canvas3d.view;
+ soundMessage.args[0] = getImmCommand(command);
+ soundMessage.args[1] = arg1;
+ soundMessage.args[2] = arg2;
+ // notify mc that there is work to do
+ VirtualUniverse.mc.processMessage(soundMessage);
+ }
+
+ static Integer getImmCommand(int command) {
+ if (commands[command] == null) {
+ commands[command] = new Integer(command);
+ }
+ return commands[command];
+ }
+
+ synchronized void runMonitor(int action) {
+ if (action == J3dThread.WAIT) {
+ while (!gcReady) {
+ waiting++;
+ try {
+ wait();
+ } catch (InterruptedException e){}
+ waiting--;
+ }
+ gcReady = false;
+ } else {
+ gcReady = true;
+ if (waiting > 0) {
+ notify();
+ }
+ }
+ }
+
+}
+
diff --git a/src/classes/share/javax/media/j3d/Group.java b/src/classes/share/javax/media/j3d/Group.java
new file mode 100644
index 0000000..01cb671
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Group.java
@@ -0,0 +1,532 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.BitSet;
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+ * The Group node object is a general-purpose grouping node. Group
+ * nodes have exactly one parent and an arbitrary number of children
+ * that are rendered in an unspecified order (or in parallel). Null
+ * children are allowed; no operation is performed on a null child
+ * node. Operations on Group node objects include adding, removing,
+ * and enumerating the children of the Group node. The subclasses of
+ * Group node add additional semantics.
+ */
+
+public class Group extends Node {
+ /**
+ * Specifies that this Group node allows reading its children.
+ */
+ public static final int
+ ALLOW_CHILDREN_READ = CapabilityBits.GROUP_ALLOW_CHILDREN_READ;
+
+ /**
+ * Specifies that this Group node allows writing its children.
+ */
+ public static final int
+ ALLOW_CHILDREN_WRITE = CapabilityBits.GROUP_ALLOW_CHILDREN_WRITE;
+
+ /**
+ * Specifies that this Group node allows adding new children.
+ */
+ public static final int
+ ALLOW_CHILDREN_EXTEND = CapabilityBits.GROUP_ALLOW_CHILDREN_EXTEND;
+
+ /**
+ * Specifies that this Group node allows reading its collision Bounds
+ */
+ public static final int
+ ALLOW_COLLISION_BOUNDS_READ =
+ CapabilityBits.GROUP_ALLOW_COLLISION_BOUNDS_READ;
+
+ /**
+ * Specifies that this Group node allows writing its collision Bounds
+ */
+ public static final int
+ ALLOW_COLLISION_BOUNDS_WRITE =
+ CapabilityBits.GROUP_ALLOW_COLLISION_BOUNDS_WRITE;
+
+ /**
+ * Creates the retained mode GroupRetained object that this
+ * Group component object will point to.
+ */
+ void createRetained() {
+ retained = new GroupRetained();
+ retained.setSource(this);
+ }
+
+
+ /**
+ * Sets the collision bounds of a node.
+ * @param bounds the collision bounding object for a node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setCollisionBounds(Bounds bounds) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLLISION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group0"));
+
+ ((GroupRetained)this.retained).setCollisionBounds(bounds);
+ }
+
+ /**
+ * Returns the collision bounding object of this node.
+ * @return the node's collision bounding object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getCollisionBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLLISION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group1"));
+
+ return ((GroupRetained)this.retained).getCollisionBounds();
+ }
+
+ /**
+ * Replaces the child node at the specified index in this
+ * group node's list of children with the specified child.
+ * @param child the new child
+ * @param index which child to replace. The <code>index</code> must
+ * be a value
+ * greater than or equal to 0 and less than <code>numChildren()</code>.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live or compiled scene graph and the child node being set is not
+ * a BranchGroup node
+ * @exception MultipleParentException if <code>child</code> has already
+ * been added as a child of another group node
+ * @exception IndexOutOfBoundsException if <code>index</code> is invalid
+ */
+ public void setChild(Node child, int index) {
+ if (child instanceof SharedGroup) {
+ throw new IllegalArgumentException(J3dI18N.getString("Group2"));
+ }
+
+ if (isLiveOrCompiled()) {
+ Node oldchild =
+ (Node) ((GroupRetained)this.retained).getChild(index);
+ if (! (child instanceof BranchGroup))
+ throw new RestrictedAccessException(J3dI18N.getString("Group3"));
+
+ if (!getCapability(ALLOW_CHILDREN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group13"));
+
+ if ((oldchild != null) &&
+ (! ((BranchGroup)oldchild).getCapability(BranchGroup.ALLOW_DETACH))) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Group4"));
+ }
+ }
+
+ ((GroupRetained)retained).setChild(child, index);
+ }
+
+ /**
+ * Inserts the specified child node in this group node's list of
+ * children at the specified index.
+ * @param child the new child
+ * @param index at which location to insert. The <code>index</code>
+ * must be a value
+ * greater than or equal to 0 and less than or equal to
+ * <code>numChildren()</code>.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live
+ * or compiled scene graph and the child node being inserted is not
+ * a BranchGroup node
+ * @exception MultipleParentException if <code>child</code> has already
+ * been added as a child of another group node.
+ * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
+ */
+ public void insertChild(Node child, int index) {
+ if (child instanceof SharedGroup) {
+ throw new IllegalArgumentException(J3dI18N.getString("Group2"));
+ }
+
+ if (isLiveOrCompiled()) {
+ if (! (child instanceof BranchGroup))
+ throw new RestrictedAccessException(J3dI18N.getString("Group6"));
+
+ if (!this.getCapability(ALLOW_CHILDREN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group14"));
+ }
+
+ ((GroupRetained)this.retained).insertChild(child, index);
+ }
+
+ /**
+ * Removes the child node at the specified index from this group node's
+ * list of children.
+ * @param index which child to remove. The <code>index</code>
+ * must be a value
+ * greater than or equal to 0 and less than <code>numChildren()</code>.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live or compiled scene graph and the child node being removed is not
+ * a BranchGroup node
+ * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
+ */
+ public void removeChild(int index) {
+ if (isLiveOrCompiled()) {
+ Node child = ((GroupRetained)this.retained).getChild(index);
+ if (!(child instanceof BranchGroup)) {
+ throw new RestrictedAccessException(J3dI18N.getString("Group7"));
+ }
+
+ if (!this.getCapability(ALLOW_CHILDREN_WRITE)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Group15"));
+ }
+
+ if (!((BranchGroup)child).getCapability(BranchGroup.ALLOW_DETACH)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Group4"));
+ }
+ }
+
+ ((GroupRetained)this.retained).removeChild(index);
+ }
+
+ /**
+ * Retrieves the child node at the specified index in
+ * this group node's list of children.
+ * @param index which child to return.
+ * @return the children at location index. The <code>index</code>
+ * must be a value
+ * greater than or equal to 0 and less than <code>numChildren()</code>.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
+ */
+ public Node getChild(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHILDREN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group9"));
+
+ return (Node) ((GroupRetained)this.retained).getChild(index);
+ }
+
+ /**
+ * Returns an Enumeration object of this group node's list of children.
+ * @return an Enumeration object of all the children
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ */
+ public Enumeration getAllChildren() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHILDREN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group9"));
+
+ return (Enumeration)((GroupRetained)this.retained).getAllChildren();
+ }
+
+ /**
+ * Appends the specified child node to this group node's list of children.
+ * @param child the child to add to this node's list of children
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part
+ * of live
+ * or compiled scene graph and the child node being added is not
+ * a BranchGroup node
+ * @exception MultipleParentException if <code>child</code> has already
+ * been added as a child of another group node.
+ */
+ public void addChild(Node child) {
+ if (child instanceof SharedGroup) {
+ throw new IllegalArgumentException(J3dI18N.getString("Group2"));
+ }
+
+ if (isLiveOrCompiled()) {
+ if (! (child instanceof BranchGroup))
+ throw new RestrictedAccessException(J3dI18N.getString("Group12"));
+
+ if(!this.getCapability(ALLOW_CHILDREN_EXTEND))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group16"));
+ }
+
+ ((GroupRetained)this.retained).addChild(child);
+ }
+
+ /**
+ * Moves the specified branch group node from its existing location to
+ * the end of this group node's list of children.
+ * @param branchGroup the branch group node to move to this node's list
+ * of children
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ */
+ public void moveTo(BranchGroup branchGroup) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_CHILDREN_EXTEND))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group16"));
+
+ if (! branchGroup.getCapability(BranchGroup.ALLOW_DETACH)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Group4"));
+ }
+ }
+
+ ((GroupRetained)this.retained).moveTo(branchGroup);
+ }
+
+ /**
+ * Returns a count of this group node's children.
+ * @return the number of children descendant from this node.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ */
+ public int numChildren() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHILDREN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group9"));
+
+ return ((GroupRetained)this.retained).numChildren();
+ }
+
+
+ /**
+ * Retrieves the index of the specified child node in
+ * this group node's list of children.
+ *
+ * @param child the child node to be looked up.
+ * @return the index of the specified child node;
+ * returns -1 if the object is not in the list.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfChild(Node child) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHILDREN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group9"));
+
+ return ((GroupRetained)this.retained).indexOfChild(child);
+ }
+
+
+ /**
+ * Removes the specified child node from this group node's
+ * list of children.
+ * If the specified object is not in the list, the list is not modified.
+ *
+ * @param child the child node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live or compiled scene graph and the child node being removed is not
+ * a BranchGroup node
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeChild(Node child) {
+
+ if (isLiveOrCompiled()) {
+ if (!(child instanceof BranchGroup)) {
+ throw new RestrictedAccessException(J3dI18N.getString("Group7"));
+ }
+
+ if (!this.getCapability(ALLOW_CHILDREN_WRITE)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Group15"));
+ }
+
+ if (!((BranchGroup)child).getCapability(BranchGroup.ALLOW_DETACH)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Group4"));
+ }
+ }
+
+ ((GroupRetained)retained).removeChild(child);
+ }
+
+
+ /**
+ * Removes all children from this Group node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live or compiled scene graph and any of the children being removed are
+ * not BranchGroup nodes
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllChildren() {
+
+ if (isLiveOrCompiled()) {
+ GroupRetained groupR = (GroupRetained)this.retained;
+ for (int index = groupR.numChildren() - 1; index >= 0; index--) {
+ Node child = groupR.getChild(index);
+ if (! (child instanceof BranchGroup))
+ throw new RestrictedAccessException(J3dI18N.getString("Group7"));
+
+ if (!this.getCapability(ALLOW_CHILDREN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group15"));
+
+ if (!((BranchGroup)child).getCapability(BranchGroup.ALLOW_DETACH)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Group4"));
+ }
+ }
+ }
+
+ ((GroupRetained)retained).removeAllChildren();
+ }
+
+
+ /**
+ * Causes this Group node to be reported as the collision target when
+ * collision is being used and this node or any of its children is in
+ * a collision. The default value is false. For collision with
+ * USE_GEOMETRY set, the collision traverser will check the geometry
+ * of all the Group node's leaf descendants; for collision with
+ * USE_BOUNDS set, the collision traverser will only check the bounds
+ * at this Group node. In both cases, if there is a collision, this
+ * Group node will be reported as the colliding object in the
+ * SceneGraphPath. This reporting is done regardless of whether
+ * ENABLE_COLLISION_REPORTING
+ * is set for this group node (setting alternate collision target to
+ * true implies collision reporting).
+ * @param target Indicates whether this Group node can be the target
+ * of a collision.
+ * @see WakeupOnCollisionEntry
+ * @see WakeupOnCollisionMovement
+ * @see WakeupOnCollisionExit
+ */
+ public void setAlternateCollisionTarget(boolean target) {
+ ((GroupRetained)this.retained).setAlternateCollisionTarget(target);
+ }
+
+ /**
+ * Returns the collision target state.
+ * @return Indicates whether this Group node can be the target of a
+ * collision.
+ */
+ public boolean getAlternateCollisionTarget() {
+ return ((GroupRetained)this.retained).getAlternateCollisionTarget();
+ }
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code> and
+ * then <code>cloneTree</code> is called for each child node. For
+ * Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ *
+ * @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> determines whether data is
+ * duplicated or copied.
+ *
+ *
+ * @return a reference to the cloned scene graph.
+ *
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ Node cloneTree(boolean forceDuplicate, Hashtable nodeHashtable) {
+ Group g = (Group) super.cloneTree(forceDuplicate, nodeHashtable);
+ GroupRetained rt = (GroupRetained) retained;
+
+ int nChildren = rt.numChildren();
+ // call cloneTree on all child nodes
+ for (int i = 0; i < nChildren; i++) {
+ Node n = rt.getChild(i);
+ Node clonedN = n.cloneTree(forceDuplicate, nodeHashtable);
+ // add the cloned child to the cloned group node
+ ((GroupRetained) g.retained).addChild(clonedN);
+
+ }
+ return g;
+ }
+
+
+
+ /**
+ * 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>
+ *
+ * @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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ GroupRetained attr = (GroupRetained) originalNode.retained;
+ GroupRetained rt = (GroupRetained) retained;
+
+ rt.setCollisionBounds(attr.getCollisionBounds());
+ rt.setAlternateCollisionTarget(attr.getAlternateCollisionTarget());
+ // throw away any child create before, since some node such as
+ // Sphere has already created its own branch
+ // Without doing this, we may end up with two branches with exactly
+ // the same content when cloneTree() is invoked.
+ rt.children.clear();
+ }
+
+
+ /**
+ * 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) {
+ Group g = new Group();
+ g.duplicateNode(this, forceDuplicate);
+ return g;
+ }
+
+
+ /**
+ * Constructs a Group node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * collision bounds : null<br>
+ * alternate collision target : false<br>
+ * </ul>
+ */
+ public Group() {
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/GroupRetained.java b/src/classes/share/javax/media/j3d/GroupRetained.java
new file mode 100644
index 0000000..5073837
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GroupRetained.java
@@ -0,0 +1,3110 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+/**
+ * Group node.
+ */
+
+class GroupRetained extends NodeRetained implements BHLeafInterface {
+ /**
+ * The Group Node's children vector.
+ */
+ ArrayList children = new ArrayList(1);
+
+ /**
+ * The Group node's collision bounds in local coordinates.
+ */
+ Bounds collisionBound = null;
+
+ // The locale that this node is decended from
+ Locale locale = null;
+
+ // The list of lights that are scoped to this node
+ // One such arraylist per path. If not in sharedGroup
+ // then only index 0 is valid
+ ArrayList lights = null;
+
+ // The list of fogs that are scoped to this node
+ // One such arraylist per path. If not in sharedGroup
+ // then only index 0 is valid
+ ArrayList fogs = null;
+
+ // The list of model clips that are scoped to this node
+ // One such arraylist per path. If not in sharedGroup
+ // then only index 0 is valid
+ ArrayList modelClips = null;
+
+
+ // The list of alternateappearance that are scoped to this node
+ // One such arraylist per path. If not in sharedGroup
+ // then only index 0 is valid
+ ArrayList altAppearances = null;
+
+
+ // indicates whether this Group node can be the target of a collision
+ boolean collisionTarget = false;
+
+ // per child switchLinks
+ ArrayList childrenSwitchLinks = null;
+
+ // the immediate childIndex of a parentSwitchLink
+ int parentSwitchLinkChildIndex = -1;
+
+ // per shared path ordered path data
+ ArrayList orderedPaths = null;
+
+ /**
+ * If collisionBound is set, this is equal to the
+ * transformed collisionBounds, otherwise it is equal
+ * to the transformed localBounds.
+ * This variable is set to null unless collisionTarget = true.
+ * This bound is only used by mirror Group.
+ */
+ BoundingBox collisionVwcBounds;
+
+ /**
+ * Mirror group of this node, it is only used when
+ * collisionTarget = true. Otherwise it is set to null.
+ * If not in shared group,
+ * only entry 0 is used.
+ *
+ */
+ ArrayList mirrorGroup;
+
+ /**
+ * key of mirror GroupRetained.
+ */
+ HashKey key;
+
+ /**
+ * sourceNode of this mirror Group
+ */
+ GroupRetained sourceNode;
+
+ /**
+ * The BHLeafNode for this GeometryAtom.
+ */
+ BHLeafNode bhLeafNode = null;
+
+ //
+ // The following variables are used during compile
+ //
+
+ // true if this is the root of the scenegraph tree
+ boolean isRoot = false;
+
+ boolean allocatedLights = false;
+
+ boolean allocatedFogs = false;
+
+ boolean allocatedMclips = false;
+
+ boolean allocatedAltApps = false;
+
+ // > 0 if this group is being used in scoping
+ int scopingRefCount = 0;
+
+
+ ArrayList compiledChildrenList = null;
+
+ boolean isInClearLive = false;
+
+ // List of viewes scoped to this Group, for all subclasses
+ // of group, except ViewSpecificGroup its a pointer to closest
+ // ViewSpecificGroup parent
+ // viewList for this node, if inSharedGroup is
+ // false then only viewList(0) is valid
+ // For VSGs, this list is an intersection of
+ // higher level VSGs
+ ArrayList viewLists = null;
+
+ // True if this Node is descendent of ViewSpecificGroup;
+ boolean inViewSpecificGroup = false;
+
+ GroupRetained() {
+ this.nodeType = NodeRetained.GROUP;
+ localBounds = new BoundingSphere();
+ ((BoundingSphere)localBounds).setRadius( -1.0 );
+ }
+
+ /**
+ * Sets the collision bounds of a node.
+ * @param bounds the bounding object for the node
+ */
+ void setCollisionBounds(Bounds bounds) {
+ if (bounds == null) {
+ this.collisionBound = null;
+ } else {
+ this.collisionBound = (Bounds)bounds.clone();
+ }
+
+ if (source.isLive()) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.COLLISION_BOUND_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY;
+ message.universe = universe;
+ message.args[0] = this;
+ VirtualUniverse.mc.processMessage(message);
+ }
+
+ }
+
+
+ /**
+ * Gets the collision bounds of a node.
+ * @return the node's bounding object
+ */
+ Bounds getCollisionBounds() {
+ return (collisionBound == null ? null : (Bounds)collisionBound.clone());
+ }
+
+ /**
+ * Replaces the specified child with the child provided.
+ * @param child the new child
+ * @param index which child to replace
+ */
+ void setChild(Node child, int index) {
+
+ checkValidChild(child, "GroupRetained0");
+ if (this.source.isLive()) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doSetChild(child, index);
+ universe.setLiveState.clear();
+ }
+ universe.waitForMC();
+
+ } else {
+ doSetChild(child, index);
+ if (universe != null) {
+ synchronized (universe.sceneGraphLock) {
+ universe.setLiveState.clear();
+ }
+ }
+ }
+ }
+
+ // The method that does the work once the lock is acquired.
+ void doSetChild(Node child, int index) {
+ NodeRetained oldchildr;
+ J3dMessage[] messages = null;
+ int numMessages = 0;
+ int attachStartIndex = 0;
+
+
+ // since we want to make sure the replacement of the child
+ // including removal of the oldChild and insertion of the newChild
+ // all happen in the same frame, we'll send all the necessary
+ // messages to masterControl for processing in one call.
+ // So let's first find out how many messages will be sent
+
+ oldchildr = (NodeRetained) children.get(index);
+
+ if (this.source.isLive()) {
+ if (oldchildr != null) {
+ numMessages+=3; // REMOVE_NODES, ORDERED_GROUP_REMOVED
+ // VIEWSPECIFICGROUP_CLEAR
+ attachStartIndex = 3;
+ }
+
+ if (child != null) {
+ numMessages+=4; // INSERT_NODES,BEHAVIOR_ACTIVATE,ORDERED_GROUP_INSERTED,
+ // VIEWSPECIFICGROUP_INIT
+ }
+
+ messages = new J3dMessage[numMessages];
+ for (int i = 0; i < numMessages; i++) {
+ messages[i] = VirtualUniverse.mc.getMessage();
+ }
+ }
+
+ if(oldchildr != null) {
+ oldchildr.setParent(null);
+ checkClearLive(oldchildr, messages, 0, index, null);
+ }
+ removeChildrenData(index);
+
+ if(child == null) {
+ children.set(index, null);
+ if (messages != null) {
+ VirtualUniverse.mc.processMessage(messages);
+ }
+ return;
+ }
+
+ NodeRetained childr = (NodeRetained) child.retained;
+ childr.setParent(this);
+ children.set(index, childr);
+
+
+ insertChildrenData(index);
+ checkSetLive(childr, index, messages, attachStartIndex, null);
+ if (this.source.isLive()) {
+ ((BranchGroupRetained)childr).isNew = true;
+ }
+
+ if (messages != null) {
+ VirtualUniverse.mc.processMessage(messages);
+ }
+ }
+
+ /**
+ * Inserts the specified child at specified index.
+ * @param child the new child
+ * @param index position to insert new child at
+ */
+ void insertChild(Node child, int index) {
+
+ checkValidChild(child, "GroupRetained1");
+ if (this.source.isLive()) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doInsertChild(child, index);
+ universe.setLiveState.clear();
+ }
+ universe.waitForMC();
+ } else {
+ doInsertChild(child, index);
+ if (universe != null) {
+ synchronized (universe.sceneGraphLock) {
+ universe.setLiveState.clear();
+ }
+ }
+ }
+ }
+
+ // The method that does the work once the lock is acquired.
+ void doInsertChild(Node child, int index) {
+ int i;
+ NodeRetained childi;
+
+ insertChildrenData(index);
+ for (i=index; i<children.size(); i++) {
+ childi = (NodeRetained) children.get(i);
+ if(childi != null)
+ childi.childIndex++;
+ }
+ if(child==null) {
+ children.add(index, null);
+ return;
+ }
+
+ NodeRetained childr = (NodeRetained) child.retained;
+ childr.setParent(this);
+ children.add(index, childr);
+ checkSetLive(childr, index, null, 0, null);
+ if (this.source.isLive()) {
+ ((BranchGroupRetained)childr).isNew = true;
+ }
+ }
+
+ /**
+ * Removes the child at specified index.
+ * @param index which child to remove
+ */
+ void removeChild(int index) {
+
+ if (this.source.isLive()) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doRemoveChild(index, null, 0);
+ universe.setLiveState.clear();
+ }
+ universe.waitForMC();
+ } else {
+ doRemoveChild(index, null, 0);
+ if (universe != null) {
+ synchronized (universe.sceneGraphLock) {
+ universe.setLiveState.clear();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the index of the specified Node in this Group's list of Nodes
+ * @param Node whose index is desired
+ * @return index of the Node
+ */
+ int indexOfChild(Node child) {
+ if(child != null)
+ return children.indexOf((NodeRetained)child.retained);
+ else
+ return children.indexOf(null);
+ }
+
+ /**
+ * Removes the specified child from this Group's list of
+ * children. If the specified child is not found, the method returns
+ * quietly
+ *
+ * @param child to be removed
+ */
+ void removeChild(Node child) {
+ int i = indexOfChild(child);
+ if(i >= 0)
+ removeChild(i);
+ }
+
+ void removeAllChildren() {
+ int n = children.size();
+ for(int i = n-1; i >= 0; i--) {
+ removeChild(i);
+ }
+ }
+
+
+ // The method that does the work once the lock is acquired.
+ void doRemoveChild(int index, J3dMessage messages[], int messageIndex) {
+ NodeRetained oldchildr, child;
+ int i;
+
+ oldchildr = (NodeRetained) children.get(index);
+
+ int size = children.size();
+ for (i=index; i<size; i++) {
+ child = (NodeRetained) children.get(i);
+ if(child != null)
+ child.childIndex--;
+ }
+
+ if(oldchildr != null) {
+ oldchildr.setParent(null);
+ checkClearLive(oldchildr, messages, messageIndex, index, null);
+ }
+
+ children.remove(index);
+ removeChildrenData(index);
+
+ if (nodeType == NodeRetained.SWITCH) {
+ // force reEvaluation of switch children
+ SwitchRetained sg = (SwitchRetained)this;
+ sg.setWhichChild(sg.whichChild, true);
+ }
+
+ }
+
+ /**
+ * Returns the child specified by the index.
+ * @param index which child to return
+ * @return the children at location index
+ */
+ Node getChild(int index) {
+
+ SceneGraphObjectRetained sgo = (SceneGraphObjectRetained) children.get(index);
+ if(sgo == null)
+ return null;
+ else
+ return (Node) sgo.source;
+ }
+
+ /**
+ * Returns an enumeration object of the children.
+ * @return an enumeration object of the children
+ */
+ Enumeration getAllChildren() {
+ Vector userChildren=new Vector(children.size());
+ SceneGraphObjectRetained sgo;
+
+ for(int i=0; i<children.size(); i++) {
+ sgo = (SceneGraphObjectRetained)children.get(i);
+ if(sgo != null)
+ userChildren.add(sgo.source);
+ else
+ userChildren.add(null);
+ }
+
+ return userChildren.elements();
+ }
+
+ void checkValidChild(Node child, String s) {
+
+ if ((child != null) &&
+ (((child instanceof BranchGroup) &&
+ (((BranchGroupRetained) child.retained).attachedToLocale)) ||
+ (((NodeRetained)child.retained).parent != null))) {
+ throw new MultipleParentException(J3dI18N.getString(s));
+ }
+ }
+
+ /**
+ * Appends the specified child to this node's list of children.
+ * @param child the child to add to this node's list of children
+ */
+ void addChild(Node child) {
+ checkValidChild(child, "GroupRetained2");
+
+ if (this.source.isLive()) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doAddChild(child, null, 0);
+ universe.setLiveState.clear();
+ }
+ universe.waitForMC();
+ } else {
+ doAddChild(child, null, 0);
+ if (universe != null) {
+ synchronized (universe.sceneGraphLock) {
+ universe.setLiveState.clear();
+ }
+ }
+ }
+ }
+
+ // The method that does the work once the lock is acquired.
+ void doAddChild(Node child, J3dMessage messages[], int messageIndex) {
+
+ appendChildrenData();
+
+ if(child == null) {
+ children.add(null);
+ return;
+ }
+
+ NodeRetained childr = (NodeRetained) child.retained;
+ childr.setParent(this);
+ children.add(childr);
+ checkSetLive(childr, children.size()-1, messages, messageIndex, null);
+ if (this.source.isLive()) {
+ ((BranchGroupRetained)childr).isNew = true;
+ }
+
+ }
+
+ void moveTo(BranchGroup bg) {
+ if (this.source.isLive()) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doMoveTo(bg);
+ universe.setLiveState.clear();
+ }
+ universe.waitForMC();
+ } else {
+ doMoveTo(bg);
+ if (universe != null) {
+ synchronized (universe.sceneGraphLock) {
+ universe.setLiveState.clear();
+ }
+ }
+ }
+ }
+
+ // The method that does the work once the lock is acquired.
+ void doMoveTo(BranchGroup branchGroup) {
+ J3dMessage messages[] = null;
+ int numMessages = 0;
+ int detachStartIndex = 0;
+ int attachStartIndex = 0;
+ if(branchGroup != null) {
+ BranchGroupRetained bg = (BranchGroupRetained) branchGroup.retained;
+ GroupRetained g = (GroupRetained)bg.parent;
+
+ // Find out how many messages to be created
+ // Note that g can be NULL if branchGroup parent is
+ // a Locale, in this case the following condition
+ // will fail.
+ // Figure out the number of messages based on whether the group
+ // from which its moving from is live and group to which its
+ // moving to is live
+ if (g != null) {
+ if (g.source.isLive()) {
+ numMessages = 3; // REMOVE_NODES, ORDERED_GROUP_REMOVED,VIEWSPECIFICGROUP_CLEAR
+ attachStartIndex = 3;
+ }
+ else {
+ numMessages = 0;
+ attachStartIndex = 0;
+ }
+
+ }
+ else { // Attached to locale
+ numMessages = 3; // REMOVE_NODES, ORDERED_GROUP_REMOVED, VIEWSPECIFICGROUP_CLEAR
+ attachStartIndex = 3;
+ }
+ // Now, do the evaluation for the group that its going to be
+ // attached to ..
+ if (this.source.isLive()) {
+ numMessages+=4; // INSERT_NODES, BEHAVIOR_ACTIVATE
+ // ORDERED_GROUP_INSERTED, VIEWSPECIFICGROUP_INIT
+
+ }
+ messages = new J3dMessage[numMessages];
+ for (int i=0; i<numMessages; i++) {
+ messages[i] = VirtualUniverse.mc.getMessage();
+ messages[i].type = J3dMessage.INVALID_TYPE;
+ }
+
+ // Remove it from it's parents state
+ if (g == null) {
+ if (bg.locale != null) {
+ bg.locale.doRemoveBranchGraph(branchGroup,
+ messages, detachStartIndex);
+ }
+ } else {
+ g.doRemoveChild(g.children.indexOf(bg),
+ messages,
+ detachStartIndex);
+ }
+ }
+
+
+ // Add it to it's new parent
+ doAddChild(branchGroup, messages, attachStartIndex);
+
+ if (numMessages > 0) {
+ int count = 0;
+ for (int i=0; i < numMessages; i++) {
+ if (messages[i].type != J3dMessage.INVALID_TYPE) {
+ count++;
+ }
+ }
+ if (count == numMessages) {
+ // in most cases
+ VirtualUniverse.mc.processMessage(messages);
+ } else {
+ J3dMessage ms[] = null;
+
+ if (count > 0) {
+ ms = new J3dMessage[count];
+ }
+
+ int k=0;
+ for (int i=0; i < numMessages; i++) {
+ if (messages[i].type != J3dMessage.INVALID_TYPE) {
+ ms[k++] = messages[i];
+ } else {
+ VirtualUniverse.mc.addMessageToFreelists(messages[i]);
+ }
+ }
+ if (ms != null) {
+ VirtualUniverse.mc.processMessage(ms);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Returns a count of this nodes' children.
+ * @return the number of children descendant from this node
+ */
+ int numChildren() {
+ return children.size();
+ }
+
+ // Remove a light from the list of lights
+ void removeLight(int numLgt, LightRetained[] removelight, HashKey key) {
+ ArrayList l;
+ int index;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)lights.get(hkIndex);
+ if (l != null) {
+ for (int i = 0; i < numLgt; i++) {
+ index = l.indexOf(removelight[i]);
+ l.remove(index);
+ }
+ }
+ }
+ else {
+ l = (ArrayList)lights.get(0);
+ for (int i = 0; i < numLgt; i++) {
+ index = l.indexOf(removelight[i]);
+ l.remove(index);
+ }
+ }
+
+ /*
+ // TODO: lights may remove twice or more during clearLive(),
+ // one from itself and one call from every LightRetained
+ // reference this. So there is case that this procedure get
+ // called when light already removed.
+ if (i >= 0)
+ lights.remove(i);
+ */
+ }
+
+
+ void addAllNodesForScopedLight(int numLgts,
+ LightRetained[] ml,
+ ArrayList list,
+ HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processAllNodesForScopedLight(numLgts, ml, list, k);
+ }
+ }
+ else {
+ processAllNodesForScopedLight(numLgts, ml, list, k);
+ }
+ }
+
+ void processAllNodesForScopedLight(int numLgts, LightRetained[] ml, ArrayList list, HashKey k) {
+ if (allocatedLights) {
+ addLight(ml, numLgts, k);
+ }
+ if (this.source.isLive() || this.isInSetLive()) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained && (child.source.isLive() || child.isInSetLive()))
+ ((GroupRetained)child).processAllNodesForScopedLight(numLgts, ml, list, k);
+ else if (child instanceof LinkRetained && (child.source.isLive()|| child.isInSetLive())) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processAllNodesForScopedLight(numLgts, ml, list, k.append("+").
+ append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive()) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ void removeAllNodesForScopedLight(int numLgts, LightRetained[] ml, ArrayList list, HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processRemoveAllNodesForScopedLight(numLgts, ml, list, k);
+ }
+ }
+ else {
+ processRemoveAllNodesForScopedLight(numLgts, ml, list, k);
+ }
+ }
+
+ void processRemoveAllNodesForScopedLight(int numLgts, LightRetained[] ml, ArrayList list, HashKey k) {
+ if (allocatedLights) {
+ removeLight(numLgts,ml, k);
+ }
+ // If the source is live, then notify the children
+ if (this.source.isLive() && !isInClearLive) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained &&(child.source.isLive() &&
+ ! ((GroupRetained)child).isInClearLive))
+ ((GroupRetained)child).processRemoveAllNodesForScopedLight(numLgts, ml,list, k);
+ else if (child instanceof LinkRetained && child.source.isLive()) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processRemoveAllNodesForScopedLight(numLgts, ml, list, k.append("+").
+ append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+
+
+ void addAllNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processAddNodesForScopedFog(mfog, list, k);
+ }
+ }
+ else {
+ processAddNodesForScopedFog(mfog, list, k);
+ }
+ }
+
+ void processAddNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
+ // If this group has it own scoping list then add ..
+ if (allocatedFogs)
+ addFog(mfog, k);
+ // If the source is live, then notify the children
+ if (this.source.isLive() || this.isInSetLive()) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained && (child.source.isLive()|| child.isInSetLive()))
+ ((GroupRetained)child).processAddNodesForScopedFog(mfog, list, k);
+ else if (child instanceof LinkRetained && (child.source.isLive()||child.isInSetLive() )) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processAddNodesForScopedFog(mfog, list, k.append("+").
+ append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive()) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ void removeAllNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processRemoveAllNodesForScopedFog(mfog, list, k);
+ }
+ }
+ else {
+ processRemoveAllNodesForScopedFog(mfog, list, k);
+ }
+ }
+ void processRemoveAllNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
+ // If the source is live, then notify the children
+ if (allocatedFogs)
+ removeFog(mfog, k);
+ if (this.source.isLive() && !isInClearLive) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained &&(child.source.isLive() &&
+ ! ((GroupRetained)child).isInClearLive))
+ ((GroupRetained)child).processRemoveAllNodesForScopedFog(mfog, list, k);
+ else if (child instanceof LinkRetained && child.source.isLive()) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processRemoveAllNodesForScopedFog(mfog, list, k.append("+").
+ append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+
+ void addAllNodesForScopedModelClip(ModelClipRetained mModelClip, ArrayList list, HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processAddNodesForScopedModelClip(mModelClip, list, k);
+ }
+ }
+ else {
+ processAddNodesForScopedModelClip(mModelClip, list, k);
+ }
+ }
+
+ void processAddNodesForScopedModelClip(ModelClipRetained mModelClip,
+ ArrayList list,
+ HashKey k) {
+ if (allocatedMclips)
+ addModelClip(mModelClip, k);
+ // If the source is live, then notify the children
+ if (this.source.isLive() || this.isInSetLive()) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained && (child.source.isLive()||child.isInSetLive() ))
+ ((GroupRetained)child).processAddNodesForScopedModelClip(
+ mModelClip, list, k);
+ else if (child instanceof LinkRetained && (child.source.isLive()||child.isInSetLive() )) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processAddNodesForScopedModelClip(mModelClip, list,
+ k.append("+").append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive()) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+ void removeAllNodesForScopedModelClip(ModelClipRetained mModelClip, ArrayList list, HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processRemoveAllNodesForScopedModelClip(mModelClip, list, k);
+ }
+ }
+ else {
+ processRemoveAllNodesForScopedModelClip(mModelClip, list, k);
+ }
+
+ }
+
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ void processRemoveAllNodesForScopedModelClip(ModelClipRetained mModelClip, ArrayList list, HashKey k) {
+ // If the source is live, then notify the children
+ if (allocatedMclips)
+ removeModelClip(mModelClip, k);
+ if (this.source.isLive() && !isInClearLive) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained &&(child.source.isLive() &&
+ ! ((GroupRetained)child).isInClearLive))
+ ((GroupRetained)child).processRemoveAllNodesForScopedModelClip(mModelClip, list, k);
+ else if (child instanceof LinkRetained && child.source.isLive()) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processRemoveAllNodesForScopedModelClip(mModelClip, list, k.append("+").
+ append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+
+ void addAllNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processAddNodesForScopedAltApp(mAltApp, list, k);
+ }
+ }
+ else {
+ processAddNodesForScopedAltApp(mAltApp, list, k);
+ }
+ }
+
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ void processAddNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
+ // If the source is live, then notify the children
+ if (allocatedAltApps)
+ addAltApp(mAltApp, k);
+ if (this.source.isLive() || this.isInSetLive()) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained && (child.source.isLive() || child.isInSetLive()))
+ ((GroupRetained)child).processAddNodesForScopedAltApp(mAltApp, list, k);
+ else if (child instanceof LinkRetained && child.source.isLive()) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processAddNodesForScopedAltApp(mAltApp, list, k.append("+").
+ append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+
+ void removeAllNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
+ if (inSharedGroup) {
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ k.set(localToVworldKeys[i]);
+ processRemoveNodesForScopedAltApp(mAltApp, list, k);
+ }
+ }
+ else {
+ processAddNodesForScopedAltApp(mAltApp, list, k);
+ }
+ }
+
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ void processRemoveNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
+ // If the source is live, then notify the children
+ if (allocatedAltApps)
+ removeAltApp(mAltApp, k);
+ if (this.source.isLive() && !isInClearLive) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null) {
+ if (child instanceof GroupRetained &&(child.source.isLive() &&
+ ! ((GroupRetained)child).isInClearLive))
+ ((GroupRetained)child).processRemoveNodesForScopedAltApp(mAltApp, list, k);
+ else if (child instanceof LinkRetained && child.source.isLive()) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ ((GroupRetained)(ln.sharedGroup)).
+ processRemoveNodesForScopedAltApp(mAltApp, list, k.append("+").
+ append(ln.nodeId));
+ k.count = lastCount;
+ } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
+ ((Shape3DRetained)child).getMirrorObjects(list, k);
+ } else if (child instanceof MorphRetained && child.source.isLive()) {
+ ((MorphRetained)child).getMirrorObjects(list, k);
+ }
+ }
+ }
+ }
+ }
+
+ synchronized void setLightScope() {
+ // Make group's own copy
+ ArrayList newLights;
+ if (!allocatedLights) {
+ allocatedLights = true;
+ if (lights != null) {
+ newLights = new ArrayList(lights.size());
+ int size = lights.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList l = (ArrayList)lights.get(i);
+ if (l != null) {
+ newLights.add(l.clone());
+ }
+ else {
+ newLights.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newLights = new ArrayList();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newLights.add(new ArrayList());
+ }
+ }
+ else {
+ newLights = new ArrayList();
+ newLights.add(new ArrayList());
+ }
+ }
+ lights = newLights;
+
+ }
+ scopingRefCount++;
+ }
+ synchronized void removeLightScope() {
+ scopingRefCount--;
+ }
+
+
+ synchronized void setFogScope() {
+ // Make group's own copy
+ ArrayList newFogs;
+ if (!allocatedFogs) {
+ allocatedFogs = true;
+ if (fogs != null) {
+ newFogs = new ArrayList(fogs.size());
+ int size = fogs.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList l = (ArrayList)fogs.get(i);
+ if (l != null) {
+ newFogs.add(l.clone());
+ }
+ else {
+ newFogs.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newFogs = new ArrayList();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newFogs.add(new ArrayList());
+ };
+ }
+ else {
+ newFogs = new ArrayList();
+ newFogs.add(new ArrayList());
+ }
+ }
+ fogs = newFogs;
+
+ }
+ scopingRefCount++;
+ }
+ synchronized void removeFogScope() {
+ scopingRefCount--;
+ }
+
+
+ synchronized void setMclipScope() {
+ // Make group's own copy
+ ArrayList newMclips;
+ if (!allocatedMclips) {
+ allocatedMclips = true;
+ if (modelClips != null) {
+ newMclips = new ArrayList(modelClips.size());
+ int size = modelClips.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList l = (ArrayList)modelClips.get(i);
+ if (l != null) {
+ newMclips.add(l.clone());
+ }
+ else {
+ newMclips.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newMclips =new ArrayList();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newMclips.add(new ArrayList());
+ }
+ }
+ else {
+ newMclips = new ArrayList();
+ newMclips.add(new ArrayList());
+ }
+ }
+ modelClips = newMclips;
+
+ }
+ scopingRefCount++;
+ }
+ synchronized void removeMclipScope() {
+ scopingRefCount--;
+ }
+
+
+ synchronized void setAltAppScope() {
+ // Make group's own copy
+ ArrayList newAltApps;
+ if (!allocatedAltApps) {
+ allocatedAltApps = true;
+ if (altAppearances != null) {
+ newAltApps = new ArrayList(altAppearances.size());
+ int size = altAppearances.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList l = (ArrayList)altAppearances.get(i);
+ if (l != null) {
+ newAltApps.add(l.clone());
+ }
+ else {
+ newAltApps.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newAltApps = new ArrayList();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newAltApps.add(new ArrayList());
+ }
+ }
+ else {
+ newAltApps = new ArrayList();
+ newAltApps.add(new ArrayList());
+ }
+ }
+ altAppearances = newAltApps;
+
+ }
+ scopingRefCount++;
+ }
+
+ synchronized void removeAltAppScope() {
+ scopingRefCount--;
+ }
+
+
+ synchronized boolean usedInScoping() {
+ return (scopingRefCount > 0);
+ }
+
+ // Add a light to the list of lights
+ void addLight(LightRetained[] addlight, int numLgts, HashKey key) {
+ ArrayList l;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)lights.get(hkIndex);
+ if (l != null) {
+ for (int i = 0; i < numLgts; i++) {
+ l.add(addlight[i]);
+ }
+ }
+ }
+ else {
+ l = (ArrayList)lights.get(0);
+ for (int i = 0; i < numLgts; i++) {
+ l.add(addlight[i]);
+ }
+ }
+
+ }
+ // Add a fog to the list of fogs
+ void addFog(FogRetained fog, HashKey key) {
+ ArrayList l;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)fogs.get(hkIndex);
+ if (l != null) {
+ l.add(fog);
+ }
+ }
+ else {
+ l = (ArrayList)fogs.get(0);
+ l.add(fog);
+ }
+
+ }
+
+ // Add a ModelClip to the list of ModelClip
+ void addModelClip(ModelClipRetained modelClip, HashKey key) {
+ ArrayList l;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)modelClips.get(hkIndex);
+ if (l != null) {
+ l.add(modelClip);
+ }
+ }
+ else {
+ l = (ArrayList)modelClips.get(0);
+ l.add(modelClip);
+ }
+
+ }
+ // Add a alt appearance to the list of alt appearance
+ void addAltApp(AlternateAppearanceRetained altApp, HashKey key) {
+ ArrayList l;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)altAppearances.get(hkIndex);
+ if (l != null) {
+ l.add(altApp);
+ }
+ }
+ else {
+ l = (ArrayList)altAppearances.get(0);
+ l.add(altApp);
+ }
+
+ }
+
+
+ // Remove a fog from the list of fogs
+ void removeFog(FogRetained fog, HashKey key) {
+ ArrayList l;
+ int index;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)fogs.get(hkIndex);
+ if (l != null) {
+ index = l.indexOf(fog);
+ l.remove(index);
+ }
+ }
+ else {
+ l = (ArrayList)fogs.get(0);
+ index = l.indexOf(fog);
+ l.remove(index);
+ }
+
+ }
+
+
+ // Remove a ModelClip from the list of ModelClip
+ void removeModelClip(ModelClipRetained modelClip, HashKey key) {
+ ArrayList l;
+ int index;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)modelClips.get(hkIndex);
+ if (l != null) {
+ index = l.indexOf(modelClip);
+ l.remove(index);
+ }
+ }
+ else {
+ l = (ArrayList)modelClips.get(0);
+ index = l.indexOf(modelClip);
+ l.remove(index);
+ }
+ }
+
+
+
+ // Remove a fog from the list of alt appearance
+ void removeAltApp(AlternateAppearanceRetained altApp, HashKey key) {
+ ArrayList l;
+ int index;
+ if (inSharedGroup) {
+ int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ l = (ArrayList)altAppearances.get(hkIndex);
+ if (l != null) {
+ index = l.indexOf(altApp);
+ l.remove(index);
+ }
+ }
+ else {
+ l = (ArrayList)altAppearances.get(0);
+ index = l.indexOf(altApp);
+ l.remove(index);
+ }
+
+ }
+
+
+ void updatePickable(HashKey keys[], boolean pick[]) {
+ int nchild = children.size()-1;
+ super.updatePickable(keys, pick);
+ int i=0;
+ NodeRetained child;
+
+ for (i = 0; i < nchild; i++) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.updatePickable(keys, (boolean []) pick.clone());
+ }
+ // No need to clone for the last value
+
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.updatePickable(keys, pick);
+
+ }
+
+
+ void updateCollidable(HashKey keys[], boolean collide[]) {
+ int nchild = children.size()-1;
+ super.updateCollidable(keys, collide);
+ int i=0;
+ NodeRetained child;
+
+ for (i = 0; i < nchild; i++) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.updateCollidable(keys, (boolean []) collide.clone());
+ }
+ // No need to clone for the last value
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.updateCollidable(keys, collide);
+ }
+
+ void setAlternateCollisionTarget(boolean target) {
+ if (collisionTarget == target)
+ return;
+
+ collisionTarget = target;
+
+ if (source.isLive()) {
+ // Notify parent TransformGroup to add itself
+ // Since we want to update collisionVwcBounds when
+ // transform change in TransformStructure.
+ TransformGroupRetained tg;
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.threads = J3dThread.UPDATE_GEOMETRY;
+ message.universe = universe;
+ // send message to GeometryStructure to add/remove this
+ // group node in BHTree as AlternateCollisionTarget
+
+ int numPath;
+ CachedTargets newCtArr[] = null;
+
+ if (target) {
+ createMirrorGroup();
+
+ TargetsInterface ti = getClosestTargetsInterface(
+ TargetsInterface.TRANSFORM_TARGETS);
+ if (ti != null) {
+
+ // update targets
+ CachedTargets ct;
+ Targets targets = new Targets();
+ numPath = mirrorGroup.size();
+ newCtArr = new CachedTargets[numPath];
+ for (int i=0; i<numPath; i++) {
+ ct = ti.getCachedTargets(TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ targets.addNode((NnuId)mirrorGroup.get(i),
+ Targets.GRP_TARGETS);
+ newCtArr[i] = targets.snapShotAdd(ct);
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+
+
+ // update target threads and propagate change to above
+ // nodes in scene graph
+ ti.updateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr);
+ ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr, -1);
+ }
+
+ message.type = J3dMessage.INSERT_NODES;
+ message.args[0] = mirrorGroup.toArray();
+ message.args[1] = ti;
+ message.args[2] = newCtArr;
+
+ } else {
+ TargetsInterface ti =
+ getClosestTargetsInterface(TargetsInterface.TRANSFORM_TARGETS);
+ if (ti != null) {
+
+ // update targets
+ Targets targets = new Targets();
+ CachedTargets ct;
+ numPath = mirrorGroup.size();
+ newCtArr = new CachedTargets[numPath];
+ for (int i=0; i<numPath; i++) {
+ ct = ti.getCachedTargets(TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ targets.addNode((NnuId)mirrorGroup.get(i),
+ Targets.GRP_TARGETS);
+ //Note snapShotRemove calls targets.clearNode()
+ newCtArr[i] = targets.snapShotRemove(ct);
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ // update target threads and propagate change to above
+ // nodes in scene graph
+ ti.updateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr);
+ ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr, -1);
+ }
+
+ message.type = J3dMessage.REMOVE_NODES;
+ message.args[0] = mirrorGroup.toArray();
+ message.args[1] = ti;
+ message.args[2] = newCtArr;
+ mirrorGroup = null; // for gc
+ }
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ boolean getAlternateCollisionTarget() {
+ return collisionTarget;
+ }
+
+
+
+ /**
+ * This checks is setLive needs to be called. If it does, it gets the
+ * needed info and calls it.
+ */
+ void checkSetLive(NodeRetained child, int childIndex, J3dMessage messages[],
+ int messageIndex, NodeRetained linkNode) {
+ checkSetLive(child, childIndex, localToVworldKeys, inSharedGroup,
+ messages, messageIndex, linkNode);
+ }
+
+
+ /**
+ * This checks is setLive needs to be called. If it does, it gets the
+ * needed info and calls it.
+ */
+ void checkSetLive(NodeRetained child, int childIndex, HashKey keys[],
+ boolean isShared, J3dMessage messages[],
+ int messageIndex, NodeRetained linkNode) {
+
+ SceneGraphObject me = this.source;
+ SetLiveState s;
+ J3dMessage createMessage;
+ boolean sendMessages = false;
+ boolean sendOGMessage = true;
+ boolean sendVSGMessage = true;
+
+ if (me.isLive()) {
+
+ s = universe.setLiveState;
+ s.reset(locale);
+ s.refCount = refCount;
+ s.inSharedGroup = isShared;
+ s.inBackgroundGroup = inBackgroundGroup;
+ s.inViewSpecificGroup = inViewSpecificGroup;
+ s.geometryBackground = geometryBackground;
+ s.keys = keys;
+ s.viewLists = viewLists;
+ s.parentBranchGroupPaths = branchGroupPaths;
+ // Note that there is no need to clone individual
+ // branchGroupArray since they will get replace (not append)
+ // by creating a new reference in child's group.
+ s.branchGroupPaths = (ArrayList) branchGroupPaths.clone();
+ s.orderedPaths = orderedPaths;
+
+ // Make the scoped fogs and lights of the child to include, the
+ // the scoped fog of this group
+ s.lights = lights;
+ s.altAppearances = altAppearances;
+ s.fogs = fogs;
+ s.modelClips = modelClips;
+
+ boolean pick[];
+ boolean collide[];
+
+ if (!inSharedGroup) {
+ pick = new boolean[1];
+ collide = new boolean[1];
+ } else {
+ pick = new boolean[localToVworldKeys.length];
+ collide = new boolean[localToVworldKeys.length];
+ }
+ findPickableFlags(pick);
+ super.updatePickable(null, pick);
+ s.pickable = pick;
+
+ findCollidableFlags(collide);
+ super.updateCollidable(null, collide);
+ s.collidable = collide;
+
+ TargetsInterface transformInterface, switchInterface;
+ transformInterface = initTransformStates(s, true);
+ switchInterface = initSwitchStates(s, this, child, linkNode, true);
+
+
+ if (s.inViewSpecificGroup &&
+ (s.changedViewGroup == null)) {
+ s.changedViewGroup = new ArrayList();
+ s.changedViewList = new ArrayList();
+ s.keyList = new int[10];
+ s.viewScopedNodeList = new ArrayList();
+ s.scopedNodesViewList = new ArrayList();
+ }
+
+ childCheckSetLive(child, childIndex, s, linkNode);
+
+ CachedTargets[] newCtArr = null;
+ newCtArr = updateTransformStates(s, transformInterface, true);
+ updateSwitchStates(s, switchInterface, true);
+
+ // We're sending multiple messages in the call, inorder to
+ // have all these messages to be process as an atomic operation.
+ // We need to create an array of messages to MasterControl, this
+ // will ensure that all these messages will get the same time stamp.
+
+ // If it is called from "moveTo", messages is not null.
+ if (messages == null) {
+ int numMessages = 2;
+ if(s.ogList.size() > 0) {
+ numMessages++;
+ }
+ else {
+ sendOGMessage = false;
+ }
+ if(s.changedViewGroup != null) {
+ numMessages++;
+ }
+ else {
+ sendVSGMessage = false;
+ }
+
+ messages = new J3dMessage[numMessages];
+ messageIndex = 0;
+ for(int mIndex=0; mIndex < numMessages; mIndex++) {
+ messages[mIndex] = VirtualUniverse.mc.getMessage();
+ }
+ sendMessages = true;
+ }
+
+ if(sendOGMessage) {
+ createMessage = messages[messageIndex++];
+ createMessage.threads = J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.type = J3dMessage.ORDERED_GROUP_INSERTED;
+ createMessage.universe = universe;
+ createMessage.args[0] = s.ogList.toArray();
+ createMessage.args[1] = s.ogChildIdList.toArray();
+ createMessage.args[2] = s.ogOrderedIdList.toArray();
+ createMessage.args[3] = s.ogCIOList.toArray();
+ createMessage.args[4] = s.ogCIOTableList.toArray();
+ }
+
+
+ if(sendVSGMessage) {
+ createMessage = messages[messageIndex++];
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.type = J3dMessage.VIEWSPECIFICGROUP_INIT;
+ createMessage.universe = universe;
+ createMessage.args[0] = s.changedViewGroup;
+ createMessage.args[1] = s.changedViewList;
+ createMessage.args[2] = s.keyList;
+ }
+
+ createMessage = messages[messageIndex++];
+ createMessage.threads = s.notifyThreads;
+ createMessage.type = J3dMessage.INSERT_NODES;
+ createMessage.universe = universe;
+ createMessage.args[0] = s.nodeList.toArray();
+ if (newCtArr != null) {
+ createMessage.args[1] = transformInterface;
+ createMessage.args[2] = newCtArr;
+ } else {
+ createMessage.args[1] = null;
+ createMessage.args[2] = null;
+ }
+
+ if (s.viewScopedNodeList != null) {
+ createMessage.args[3] = s.viewScopedNodeList;
+ createMessage.args[4] = s.scopedNodesViewList;
+ }
+
+ // execute user behavior's initialize methods
+ int sz = s.behaviorNodes.size();
+
+ for (int i=0; i < sz; i++) {
+ BehaviorRetained b;
+ b = (BehaviorRetained)s.behaviorNodes.get(i);
+ b.executeInitialize();
+ }
+
+ s.behaviorNodes.clear();
+
+ createMessage = messages[messageIndex++];
+
+ createMessage.threads = J3dThread.UPDATE_BEHAVIOR;
+ createMessage.type = J3dMessage.BEHAVIOR_ACTIVATE;
+ createMessage.universe = universe;
+
+ if (sendMessages == true) {
+ VirtualUniverse.mc.processMessage(messages);
+ }
+
+ if (nodeType == NodeRetained.SWITCH) {
+ // force reEvaluation of switch children
+ SwitchRetained sw = (SwitchRetained)this;
+ sw.setWhichChild(sw.whichChild, true);
+ }
+
+ //Reset SetLiveState to free up memory.
+ s.reset(null);
+ }
+ }
+
+
+
+ void checkClearLive(NodeRetained child,
+ J3dMessage messages[], int messageIndex,
+ int childIndex, NodeRetained linkNode) {
+ checkClearLive(child, localToVworldKeys, inSharedGroup,
+ messages, messageIndex, childIndex, linkNode);
+ }
+
+
+
+ /**
+ * This checks if clearLive needs to be called. If it does, it gets the
+ * needed info and calls it.
+ */
+ void checkClearLive(NodeRetained child, HashKey keys[],
+ boolean isShared,
+ J3dMessage messages[], int messageIndex,
+ int childIndex, NodeRetained linkNode) {
+
+ SceneGraphObject me = this.source;
+ J3dMessage destroyMessage;
+ boolean sendMessages = false;
+ boolean sendOGMessage = true;
+ boolean sendVSGMessage = true;
+
+ int i, j;
+ TransformGroupRetained tg;
+
+ if (me.isLive()) {
+ SetLiveState s = universe.setLiveState;
+
+ s.reset(locale);
+ s.refCount = refCount;
+ s.inSharedGroup = isShared;
+ s.inBackgroundGroup = inBackgroundGroup;
+ s.inViewSpecificGroup = inViewSpecificGroup;
+ s.keys = keys;
+ s.fogs = fogs;
+ s.lights = lights;
+ s.altAppearances = altAppearances;
+ s.modelClips = modelClips;
+
+
+ if (this instanceof OrderedGroupRetained && linkNode == null) {
+ // set this regardless of refCount
+ s.ogList.add(this);
+ s.ogChildIdList.add(new Integer(childIndex));
+ s.ogCIOList.add(this);
+ int[] newArr = null;
+ OrderedGroupRetained og = (OrderedGroupRetained)this;
+ if(og.userChildIndexOrder != null) {
+ newArr = new int[og.userChildIndexOrder.length];
+ System.arraycopy(og.userChildIndexOrder, 0, newArr,
+ 0, og.userChildIndexOrder.length);
+ }
+ s.ogCIOTableList.add(newArr);
+
+ }
+
+ if (!(this instanceof ViewSpecificGroupRetained)) {
+ s.viewLists = viewLists;
+ }
+
+ TargetsInterface transformInterface, switchInterface;
+ transformInterface = initTransformStates(s, false);
+ switchInterface = initSwitchStates(s, this, child, linkNode, false);
+
+ child.clearLive(s);
+
+ CachedTargets[] newCtArr = null;
+ newCtArr = updateTransformStates(s, transformInterface, false);
+ updateSwitchStates(s, switchInterface, false);
+
+ // We're sending multiple messages in the call, inorder to
+ // have all these messages to be process as an atomic operation.
+ // We need to create an array of messages to MasterControl, this
+ // will ensure that all these messages will get the same time stamp.
+
+ // If it is called from "moveTo", messages is not null.
+ if (messages == null) {
+ int numMessages = 1;
+ if(s.ogList.size() > 0) {
+ numMessages++;
+ }
+ else {
+ sendOGMessage = false;
+ }
+
+ if(s.changedViewGroup != null) {
+ numMessages++;
+ }
+ else {
+ sendVSGMessage = false;
+ }
+
+ messages = new J3dMessage[numMessages];
+ messageIndex = 0;
+ for(int mIndex=0; mIndex < numMessages; mIndex++) {
+ messages[mIndex] = VirtualUniverse.mc.getMessage();
+ }
+ sendMessages = true;
+ }
+
+ if(sendOGMessage) {
+ destroyMessage = messages[messageIndex++];
+ destroyMessage.threads = J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ destroyMessage.type = J3dMessage.ORDERED_GROUP_REMOVED;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = s.ogList.toArray();
+ destroyMessage.args[1] = s.ogChildIdList.toArray();
+ destroyMessage.args[3] = s.ogCIOList.toArray();
+ destroyMessage.args[4] = s.ogCIOTableList.toArray();
+ }
+
+ if(sendVSGMessage) {
+ destroyMessage = messages[messageIndex++];
+ destroyMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ destroyMessage.type = J3dMessage.VIEWSPECIFICGROUP_CLEAR;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = s.changedViewGroup;
+ destroyMessage.args[1] = s.keyList;
+ }
+
+ destroyMessage = messages[messageIndex++];
+ destroyMessage.threads = s.notifyThreads;
+ destroyMessage.type = J3dMessage.REMOVE_NODES;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = s.nodeList.toArray();
+
+ if (newCtArr != null) {
+ destroyMessage.args[1] = transformInterface;
+ destroyMessage.args[2] = newCtArr;
+ } else {
+ destroyMessage.args[1] = null;
+ destroyMessage.args[2] = null;
+ }
+ if (s.viewScopedNodeList != null) {
+ destroyMessage.args[3] = s.viewScopedNodeList;
+ destroyMessage.args[4] = s.scopedNodesViewList;
+ }
+ if (sendMessages == true) {
+ VirtualUniverse.mc.processMessage(messages);
+ }
+
+ s.reset(null); // for GC
+ }
+ }
+
+ TargetsInterface initTransformStates(SetLiveState s, boolean isSetLive) {
+
+ int numPaths = (inSharedGroup)? s.keys.length : 1;
+ TargetsInterface ti = getClosestTargetsInterface(
+ TargetsInterface.TRANSFORM_TARGETS);
+
+
+ if (isSetLive) {
+ s.currentTransforms = localToVworld;
+ s.currentTransformsIndex = localToVworldIndex;
+ s.localToVworldKeys = localToVworldKeys;
+ s.localToVworld = s.currentTransforms;
+ s.localToVworldIndex = s.currentTransformsIndex;
+
+ s.parentTransformLink = parentTransformLink;
+ if (parentTransformLink != null) {
+ if (parentTransformLink instanceof TransformGroupRetained) {
+ TransformGroupRetained tg;
+ tg = (TransformGroupRetained) parentTransformLink;
+ s.childTransformLinks = tg.childTransformLinks;
+ } else {
+ SharedGroupRetained sg;
+ sg = (SharedGroupRetained) parentTransformLink;
+ s.childTransformLinks = sg.childTransformLinks;
+ }
+ }
+ }
+
+ int transformLevels[] = new int[numPaths];
+ findTransformLevels(transformLevels);
+ s.transformLevels = transformLevels;
+
+ if (ti != null) {
+ Targets[] newTargets = new Targets[numPaths];
+ for(int i=0; i<numPaths; i++) {
+ if (s.transformLevels[i] >= 0) {
+ newTargets[i] = new Targets();
+ } else {
+ newTargets[i] = null;
+ }
+ }
+ s.transformTargets = newTargets;
+
+ // TODO - optimization for targetThreads computation, require
+ // cleanup in GroupRetained.doSetLive()
+ //s.transformTargetThreads = 0;
+ }
+
+ return ti;
+ }
+
+ CachedTargets[] updateTransformStates(SetLiveState s,
+ TargetsInterface ti, boolean isSetLive) {
+ CachedTargets[] newCtArr = null;
+
+ if (ti != null) {
+ if (isSetLive) {
+ CachedTargets ct;
+ int newTargetThreads = 0;
+ int hkIndex;
+
+ newCtArr = new CachedTargets[localToVworld.length];
+
+ // update targets
+ if (! inSharedGroup) {
+ if (s.transformTargets[0] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, 0, -1);
+ if (ct != null) {
+ newCtArr[0] = s.transformTargets[0].snapShotAdd(ct);
+ }
+ } else {
+ newCtArr[0] = null;
+ }
+ } else {
+ for (int i=0; i<s.keys.length; i++) {
+
+ if (s.transformTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ newCtArr[i] =
+ s.transformTargets[i].snapShotAdd(ct);
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ } else {
+
+ CachedTargets ct;
+ int hkIndex;
+
+ newCtArr = new CachedTargets[localToVworld.length];
+
+ if (! inSharedGroup) {
+ if (s.transformTargets[0] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, 0, -1);
+ if (ct != null) {
+ newCtArr[0] =
+ s.transformTargets[0].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[0] = null;
+ }
+ } else {
+ for (int i=0; i<s.keys.length; i++) {
+ if (s.transformTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ newCtArr[i] =
+ s.transformTargets[i].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ }
+ // update target threads and propagate change to above
+ // nodes in scene graph
+
+ ti.updateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr);
+ ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr, -1);
+
+ }
+ return newCtArr;
+ }
+
+ TargetsInterface initSwitchStates(SetLiveState s,
+ NodeRetained parentNode, NodeRetained childNode,
+ NodeRetained linkNode, boolean isSetLive) {
+ NodeRetained child;
+ NodeRetained parent;
+ int i,j;
+
+ findSwitchInfo(s, parentNode, childNode, linkNode);
+ TargetsInterface ti = getClosestTargetsInterface(
+ TargetsInterface.SWITCH_TARGETS);
+ if (ti != null) {
+ Targets[] newTargets = null;
+ int numPaths = (inSharedGroup)? s.keys.length : 1;
+ newTargets = new Targets[numPaths];
+ for(i=0; i<numPaths; i++) {
+ if (s.switchLevels[i] >= 0) {
+ newTargets[i] = new Targets();
+ } else {
+ newTargets[i] = null;
+ }
+ }
+ s.switchTargets = newTargets;
+ }
+
+ if (isSetLive) {
+ // set switch states
+ if (nodeType == NodeRetained.SWITCH) {
+ i = parentSwitchLinkChildIndex;
+ s.childSwitchLinks = (ArrayList)childrenSwitchLinks.get(i);
+ s.parentSwitchLink = this;
+
+ } else {
+ if (nodeType == NodeRetained.SHAREDGROUP) {
+ i = parentSwitchLinkChildIndex;
+ s.childSwitchLinks = (ArrayList)childrenSwitchLinks.get(i);
+ s.parentSwitchLink = this;
+
+ } else {
+ s.parentSwitchLink = parentSwitchLink;
+ if (parentSwitchLink != null) {
+ i = parentSwitchLinkChildIndex;
+ s.childSwitchLinks = (ArrayList)
+ parentSwitchLink.childrenSwitchLinks.get(i);
+ }
+ }
+ }
+ if (ti != null) {
+ s.switchStates = ti.getTargetsData(
+ TargetsInterface.SWITCH_TARGETS,
+ parentSwitchLinkChildIndex);
+ } else {
+ s.switchStates = new ArrayList(1);
+ s.switchStates.add(new SwitchState(false));
+ }
+ }
+ return ti;
+ }
+
+ void updateSwitchStates(SetLiveState s, TargetsInterface ti,
+ boolean isSetLive) {
+
+ // update switch leaves's compositeSwitchMask for ancestors
+ // and update switch leaves' switchOn flag if at top level switch
+
+ if (ti != null) {
+ if (isSetLive) {
+ CachedTargets[] newCtArr = null;
+ CachedTargets ct;
+
+ newCtArr = new CachedTargets[localToVworld.length];
+
+ // update targets
+ if (! inSharedGroup) {
+
+ if (s.switchTargets[0] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.SWITCH_TARGETS, 0,
+ parentSwitchLinkChildIndex);
+ if (ct != null) {
+ newCtArr[0] = s.switchTargets[0].snapShotAdd(ct);
+ } else {
+ newCtArr[0] = s.switchTargets[0].snapShotInit();
+ }
+ } else {
+ newCtArr[0] = null;
+ }
+ } else {
+ for (int i=0; i<s.keys.length; i++) {
+ if (s.switchTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.SWITCH_TARGETS, i,
+ parentSwitchLinkChildIndex);
+ if (ct != null) {
+ newCtArr[i] =
+ s.switchTargets[i].snapShotAdd(ct);
+ } else {
+ newCtArr[i] =
+ s.switchTargets[i].snapShotInit();
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ ti.resetCachedTargets(TargetsInterface.SWITCH_TARGETS,
+ newCtArr, parentSwitchLinkChildIndex);
+ if (ti instanceof SwitchRetained) {
+ ((SwitchRetained)ti).traverseSwitchParent();
+ } else if (ti instanceof SharedGroupRetained) {
+ ((SharedGroupRetained)ti).traverseSwitchParent();
+ }
+ } else {
+ CachedTargets ct;
+
+ CachedTargets[] newCtArr =
+ new CachedTargets[localToVworld.length];
+
+ if (! inSharedGroup) {
+ if (s.switchTargets[0] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.SWITCH_TARGETS, 0,
+ parentSwitchLinkChildIndex);
+ if (ct != null) {
+ newCtArr[0] =
+ s.switchTargets[0].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[0] = null;
+ }
+ } else {
+ for (int i=0; i<s.keys.length; i++) {
+ if (s.switchTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.SWITCH_TARGETS, i,
+ parentSwitchLinkChildIndex);
+
+ if (ct != null) {
+ newCtArr[i] =
+ s.switchTargets[i].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ ti.resetCachedTargets(TargetsInterface.SWITCH_TARGETS,
+ newCtArr, parentSwitchLinkChildIndex);
+ }
+ }
+ }
+
+ void appendChildrenData() {
+ }
+ void insertChildrenData(int index) {
+ }
+ void removeChildrenData(int index) {
+ }
+
+ TargetsInterface getClosestTargetsInterface(int type) {
+ return (type == TargetsInterface.TRANSFORM_TARGETS)?
+ (TargetsInterface)parentTransformLink:
+ (TargetsInterface)parentSwitchLink;
+ }
+
+
+ synchronized void updateLocalToVworld() {
+ NodeRetained child;
+
+ // For each children call .....
+ for (int i=children.size()-1; i>=0; i--) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.updateLocalToVworld();
+ }
+ }
+
+ void setNodeData(SetLiveState s) {
+ super.setNodeData(s);
+ orderedPaths = s.orderedPaths;
+ }
+
+ void removeNodeData(SetLiveState s) {
+
+ if((!inSharedGroup) || (s.keys.length == localToVworld.length)) {
+ orderedPaths = null;
+ }
+ else {
+ // Set it back to its parent localToVworld data. This is b/c the
+ // parent has changed it localToVworld data arrays.
+ orderedPaths = s.orderedPaths;
+ }
+ super.removeNodeData(s);
+ }
+
+
+
+ void setLive(SetLiveState s) {
+ doSetLive(s);
+ super.markAsLive();
+ }
+
+ // Note that SwitchRetained, OrderedGroupRetained and SharedGroupRetained
+ // override this method
+ void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) {
+ if(child!=null)
+ child.setLive(s);
+ }
+
+ // Note that BranchRetained, OrderedGroupRetained and SharedGroupRetained
+ // TransformGroupRetained override this method
+ void childCheckSetLive(NodeRetained child, int childIndex,
+ SetLiveState s, NodeRetained linkNode) {
+ child.setLive(s);
+ }
+
+ /**
+ * This version of setLive calls setLive on all of its chidren.
+ */
+ void doSetLive(SetLiveState s) {
+ int i, nchildren;
+
+ BoundingSphere boundingSphere = new BoundingSphere();
+ NodeRetained child;
+ super.doSetLive(s);
+ locale = s.locale;
+
+ inViewSpecificGroup = s.inViewSpecificGroup;
+ nchildren = children.size();
+ ArrayList savedScopedLights = s.lights;
+ ArrayList savedScopedFogs = s.fogs;
+ ArrayList savedScopedAltApps = s.altAppearances;
+ ArrayList savedScopedMclips = s.modelClips;
+
+ boolean oldpickableArray[] = (boolean []) s.pickable.clone();
+ boolean oldcollidableArray[] = (boolean []) s.collidable.clone();
+ boolean workingpickableArray[] = new boolean[oldpickableArray.length];
+ boolean workingcollidableArray[] = new boolean[oldcollidableArray.length];
+ ArrayList oldBranchGroupPaths = s.branchGroupPaths;
+ setScopingInfo(s);
+
+
+ if (!(this instanceof ViewSpecificGroupRetained)) {
+ viewLists = s.viewLists;
+ }
+
+ for (i=0; i<nchildren; i++) {
+ child = (NodeRetained)children.get(i);
+
+ // Restore old values before child.setLive(s)
+ System.arraycopy(oldpickableArray, 0, workingpickableArray, 0,
+ oldpickableArray.length);
+ System.arraycopy(oldcollidableArray, 0, workingcollidableArray, 0,
+ oldcollidableArray.length);
+ s.pickable = workingpickableArray;
+ s.collidable = workingcollidableArray;
+ // s.branchGroupPaths will be modified by child setLive()
+ // so we have to restore it every time.
+ s.parentBranchGroupPaths = branchGroupPaths;
+ s.branchGroupPaths = (ArrayList) oldBranchGroupPaths.clone();
+ s.inViewSpecificGroup = inViewSpecificGroup;
+ childDoSetLive(child, i, s);
+ }
+
+
+
+ if (collisionTarget) {
+ processCollisionTarget(s);
+ }
+
+ s.lights = savedScopedLights;
+ s.fogs = savedScopedFogs;
+ s.altAppearances = savedScopedAltApps;
+ s.modelClips = savedScopedMclips;
+
+ }
+
+ void setScopingInfo(SetLiveState s) {
+
+ int i, k, hkIndex;
+ // If this is a scoped group , then copy the parent's
+ // scoping info
+ if (allocatedLights) {
+ if (s.lights != null) {
+ // Add the parent's scoping info to this group
+ if (inSharedGroup) {
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)lights.get(hkIndex);
+ ArrayList src = (ArrayList)s.lights.get(i);
+ if (src != null) {
+ int size = src.size();
+ for (k = 0; k < size; k++) {
+ l.add(src.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)lights.get(0);
+ ArrayList src = (ArrayList)s.lights.get(0);
+ int size = src.size();
+ for (i = 0; i < size; i++) {
+ l.add(src.get(i));
+ }
+ }
+ }
+ s.lights = lights;
+ }
+ else {
+ lights = s.lights;
+ }
+
+ if (allocatedFogs) {
+ if (s.fogs != null) {
+ // Add the parent's scoping info to this group
+ if (inSharedGroup) {
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)fogs.get(hkIndex);
+ ArrayList src = (ArrayList)s.fogs.get(i);
+ if (src != null) {
+ int size = src.size();
+ for (k = 0; k < size; k++) {
+ l.add(src.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)fogs.get(0);
+ ArrayList src = (ArrayList)s.fogs.get(0);
+ int size = src.size();
+ for (i = 0; i < size; i++) {
+ l.add(src.get(i));
+ }
+ }
+ }
+ s.fogs = fogs;
+ }
+ else {
+ fogs = s.fogs;
+ }
+
+ if (allocatedMclips) {
+ if (s.modelClips != null) {
+ // Add the parent's scoping info to this group
+ if (inSharedGroup) {
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)modelClips.get(hkIndex);
+ ArrayList src = (ArrayList)s.modelClips.get(i);
+ if (src != null) {
+ int size = src.size();
+ for (k = 0; k < size; k++) {
+ l.add(src.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)modelClips.get(0);
+ ArrayList src = (ArrayList)s.modelClips.get(0);
+ int size = src.size();
+ for (i = 0; i < size; i++) {
+ l.add(src.get(i));
+ }
+ }
+ }
+ s.modelClips = modelClips;
+ }
+ else {
+ modelClips = s.modelClips;
+ }
+
+ if (allocatedAltApps) {
+ if (s.altAppearances != null) {
+ // Add the parent's scoping info to this group
+ if (inSharedGroup) {
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)altAppearances.get(hkIndex);
+ ArrayList src = (ArrayList)s.altAppearances.get(i);
+ if (src != null) {
+ int size = src.size();
+ for (k = 0; k < size; k++) {
+ l.add(src.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)altAppearances.get(0);
+ ArrayList src = (ArrayList)s.altAppearances.get(0);
+ int size = src.size();
+ for (i = 0; i < size; i++) {
+ l.add(src.get(i));
+ }
+ }
+ }
+ s.altAppearances = altAppearances;
+ }
+ else {
+ altAppearances = s.altAppearances;
+ }
+ }
+
+ void processCollisionTarget(SetLiveState s) {
+
+ GroupRetained g;
+ if (mirrorGroup == null) {
+ mirrorGroup = new ArrayList();
+ }
+ Bounds bound = (collisionBound != null ?
+ collisionBound : getEffectiveBounds());
+ if (inSharedGroup) {
+ for (int i=0; i < s.keys.length; i++) {
+ int j;
+ g = new GroupRetained();
+ g.key = s.keys[i];
+ g.localToVworld = new Transform3D[1][];
+ g.localToVworldIndex = new int[1][];
+
+ j = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(j < 0) {
+ System.out.println("GroupRetained : Can't find hashKey");
+ }
+
+ g.localToVworld[0] = localToVworld[j];
+ g.localToVworldIndex[0] = localToVworldIndex[j];
+ g.collisionVwcBounds = new BoundingBox();
+ g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld(0));
+ g.sourceNode = this;
+ g.locale = locale; // need by getVisibleGeometryAtom()
+ mirrorGroup.add(g);
+ /*
+ System.out.println("processCollisionTarget mirrorGroup.add() : " +
+ g.getId() + " mirrorGroup.size() "
+ + mirrorGroup.size());
+ */
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(g, Targets.GRP_TARGETS);
+ }
+ s.nodeList.add(g);
+ }
+ } else {
+ g = new GroupRetained();
+ g.localToVworld = new Transform3D[1][];
+ g.localToVworldIndex = new int[1][];
+ g.localToVworld[0] = localToVworld[0];
+ g.localToVworldIndex[0] = localToVworldIndex[0];
+ g.collisionVwcBounds = new BoundingBox();
+ g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld(0));
+ g.sourceNode = this;
+ g.locale = locale; // need by getVisibleGeometryAtom()
+ mirrorGroup.add(g);
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(g, Targets.GRP_TARGETS);
+ }
+ s.nodeList.add(g);
+ }
+ }
+
+ void computeCombineBounds(Bounds bounds) {
+
+ if (boundsAutoCompute) {
+ for (int i=children.size()-1; i>=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.computeCombineBounds(bounds);
+ }
+ } else {
+ // Should this be lock too ? ( MT safe ? )
+ synchronized(localBounds) {
+ bounds.combine(localBounds);
+ }
+ }
+ }
+
+
+ /**
+ * Gets the bounding object of a node.
+ * @return the node's bounding object
+ */
+ Bounds getBounds() {
+
+ if ( boundsAutoCompute) {
+ BoundingSphere boundingSphere = new BoundingSphere();
+ boundingSphere.setRadius(-1.0);
+
+ for (int i=children.size()-1; i>=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.computeCombineBounds((Bounds) boundingSphere);
+ }
+
+ return (Bounds) boundingSphere;
+ }
+ return super.getBounds();
+ }
+
+ /**
+ * Gets the bounding object of a node.
+ * @return the node's bounding object
+ */
+ Bounds getEffectiveBounds() {
+ if ( boundsAutoCompute) {
+ return getBounds();
+ }
+ return super.getEffectiveBounds();
+ }
+
+ // returns true if children cannot be read/written
+ boolean isStaticChildren() {
+ if (source.getCapability(Group.ALLOW_CHILDREN_READ) ||
+ source.getCapability(Group.ALLOW_CHILDREN_WRITE)) {
+ return false;
+ }
+ return true;
+
+ }
+
+
+ boolean isStatic() {
+ return (super.isStatic() && isStaticChildren());
+ }
+
+ /**
+ * This compiles() a group
+ */
+ void setCompiled() {
+ super.setCompiled();
+ for (int i=children.size()-1; i>=0; i--) {
+ SceneGraphObjectRetained node =
+ (SceneGraphObjectRetained) children.get(i);
+ if (node != null)
+ node.setCompiled();
+ }
+ }
+
+ void traverse(boolean sameLevel, int level) {
+ SceneGraphObjectRetained node;
+
+ if (!sameLevel) {
+ super.traverse(true, level);
+
+ if (source.getCapability(Group.ALLOW_CHILDREN_READ)) {
+ System.out.print(" (r)");
+ } else if (isStatic()) {
+ System.out.print(" (s)");
+ } else if (source.getCapability(Group.ALLOW_CHILDREN_WRITE)) {
+ System.out.print(" (w)");
+ }
+ }
+
+ level++;
+ for (int i = 0; i < children.size(); i++) {
+ node = (SceneGraphObjectRetained) children.get(i);
+ if (node != null) {
+ node.traverse(false, level);
+ }
+ }
+ }
+
+ void compile(CompileState compState) {
+
+ SceneGraphObjectRetained node;
+
+ super.compile(compState);
+
+ mergeFlag = SceneGraphObjectRetained.MERGE;
+
+ if (!isStatic()) {
+ compState.keepTG = true;
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+ }
+
+ if (isRoot || this.usedInScoping() ||
+ (parent instanceof SwitchRetained)) {
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+ }
+
+ compiledChildrenList = new ArrayList(5);
+
+ for (int i = 0; i < children.size(); i++) {
+ node = (SceneGraphObjectRetained) children.get(i);
+ if (node != null) {
+ node.compile(compState);
+ }
+ }
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numGroups++;
+ }
+ }
+
+ void merge(CompileState compState) {
+
+ GroupRetained saveParentGroup = null;
+ SceneGraphObjectRetained node;
+
+ if (mergeFlag != SceneGraphObjectRetained.MERGE_DONE) {
+ if (mergeFlag == SceneGraphObjectRetained.DONT_MERGE) {
+
+ // don't merge/eliminate this node
+ super.merge(compState);
+
+ saveParentGroup = compState.parentGroup;
+ compState.parentGroup = this;
+ }
+
+ for (int i = 0; i < children.size(); i++) {
+ node = (SceneGraphObjectRetained) children.get(i);
+ if (node != null) {
+ node.merge(compState);
+ }
+ }
+
+ if (compState.parentGroup == this) {
+ this.children = compiledChildrenList;
+ compState.doShapeMerge();
+ compiledChildrenList = null;
+ compState.parentGroup = saveParentGroup;
+ } else {
+ // this group node can be eliminated
+ this.children.clear();
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numMergedGroups++;
+ }
+ }
+
+ mergeFlag = SceneGraphObjectRetained.MERGE_DONE;
+
+ } else {
+ if (compState.parentGroup != null) {
+ compState.parentGroup.compiledChildrenList.add(this);
+ parent = compState.parentGroup;
+ }
+ }
+ }
+
+ /**
+ * This version of clearLive calls clearLive on all of its chidren.
+ */
+ void clearLive(SetLiveState s) {
+ int i, k, hkIndex, nchildren;
+ NodeRetained child;
+ int parentScopedLtSize = 0;
+ int parentScopedFogSize = 0;
+ int parentScopedMcSize = 0;
+ int parentScopedAltAppSize = 0;
+ int groupScopedLtSize = 0;
+ int groupScopedFogSize = 0;
+ int groupScopedMcSize = 0;
+ int groupScopedAltAppSize = 0;
+ int size;
+
+ isInClearLive = true;
+
+ // Save this for later use in this method. Temporary. to be removed when OG cleanup.
+ HashKey[] savedLocalToVworldKeys = localToVworldKeys;
+
+ super.clearLive(s);
+
+
+ nchildren = this.children.size();
+
+ if (!(this instanceof ViewSpecificGroupRetained)) {
+ viewLists = s.viewLists;
+ }
+
+ ArrayList savedParentLights = s.lights;
+ if (allocatedLights) {
+ s.lights = lights;
+ }
+
+ ArrayList savedParentFogs = s.fogs;
+ if (allocatedFogs) {
+ s.fogs = fogs;
+ }
+
+ ArrayList savedParentMclips = s.modelClips;
+ if (allocatedMclips) {
+ s.modelClips = modelClips;
+ }
+
+
+ ArrayList savedParentAltApps = s.altAppearances;
+ if (allocatedAltApps) {
+ s.altAppearances = altAppearances;
+ }
+
+
+ for (i=nchildren-1; i >=0 ; i--) {
+ child = (NodeRetained)children.get(i);
+ if (this instanceof OrderedGroupRetained) {
+ OrderedGroupRetained og = (OrderedGroupRetained)this;
+
+ // adjust refCount, which has been decremented
+ //in super.clearLive
+ if ((refCount+1) == s.refCount) {
+ //only need to do it once if in shared group. Add
+ //all the children to the list of OG_REMOVED message
+ s.ogList.add(this);
+ s.ogChildIdList.add(new Integer(i));
+ }
+ s.orderedPaths = (ArrayList)og.childrenOrderedPaths.get(i);
+ }
+
+ if (child != null) {
+ child.clearLive(s);
+ }
+ }
+ // Has its own copy
+ // TODO: Handle the case of
+ // was non-zero, gone to zero?
+ if (savedParentLights != null) {
+ if (allocatedLights) {
+ if (inSharedGroup) {
+
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)savedParentLights.get(hkIndex);
+ ArrayList gl = (ArrayList)lights.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)savedParentLights.get(0);
+ ArrayList gl = (ArrayList)lights.get(0);
+ size = l.size();
+ for (int m = 0; m < size; m++) {
+ gl.remove(l.get(m));
+ }
+ }
+ }
+ }
+
+ if (savedParentFogs != null) {
+ if (allocatedFogs) {
+ if (inSharedGroup) {
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)savedParentFogs.get(hkIndex);
+ ArrayList gl = (ArrayList)fogs.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)savedParentFogs.get(0);
+ size = l.size();
+ for (int m = 0; m < size; m++) {
+ fogs.remove(l.get(m));
+ }
+ }
+ }
+ }
+
+ if (savedParentMclips != null) {
+ if (allocatedMclips) {
+ if (inSharedGroup) {
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)savedParentMclips.get(hkIndex);
+ ArrayList gl = (ArrayList)modelClips.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)savedParentMclips.get(0);
+ size = l.size();
+ for (int m = 0; m < size; m++) {
+ modelClips.remove(l.get(m));
+ }
+ }
+ }
+ }
+
+ if (savedParentAltApps != null) {
+ if (allocatedAltApps) {
+ if (inSharedGroup) {
+ for (i=0; i < s.keys.length; i++) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ ArrayList l = (ArrayList)savedParentAltApps.get(hkIndex);
+ ArrayList gl = (ArrayList)altAppearances.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList l = (ArrayList)savedParentAltApps.get(0);
+ size = l.size();
+ for (int m = 0; m < size; m++) {
+ altAppearances.remove(l.get(m));
+ }
+ }
+ }
+ }
+
+ if (collisionTarget) {
+ GroupRetained g;
+ if (inSharedGroup) {
+ for (i=s.keys.length-1; i >=0; i--) {
+ HashKey hkey = s.keys[i];
+ for (int j = mirrorGroup.size()-1; j >=0 ; j--) {
+ g = (GroupRetained) mirrorGroup.get(j);
+ if (g.key.equals(hkey)) {
+ s.nodeList.add(mirrorGroup.remove(j));
+ if (s.transformTargets != null &&
+ s.transformTargets[j] != null) {
+ s.transformTargets[j].addNode(g, Targets.GRP_TARGETS);
+ }
+ break;
+ }
+
+ }
+ }
+ } else {
+ g = (GroupRetained)mirrorGroup.get(0);
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(g, Targets.GRP_TARGETS);
+ }
+ s.nodeList.add(mirrorGroup.remove(0));
+ }
+ }
+ s.lights = savedParentLights;
+ s.modelClips = savedParentMclips;
+ s.fogs = savedParentFogs;
+ s.altAppearances = savedParentAltApps;
+ isInClearLive = false;
+ }
+
+ // This is only used by alternateCollisionTarget
+ public BoundingBox computeBoundingHull() {
+ return collisionVwcBounds;
+ }
+
+ // If isSwitchOn cached here, we don't need to traverse up the tree
+ public boolean isEnable() {
+ return isNodeSwitchOn(this.sourceNode, key);
+ }
+
+ // If isSwitchOn cached here, we don't need to traverse up the tree
+ // This method does nothing with vis.
+ public boolean isEnable(int vis) {
+ return isNodeSwitchOn(this.sourceNode, key);
+ }
+
+ // Can't use getLocale, it is used by BranchGroupRetained
+ public Locale getLocale2() {
+ return locale;
+ }
+
+ /**
+ * Return true of nodeR is not under a switch group or
+ * nodeR is enable under a switch group.
+ */
+ static boolean isNodeSwitchOn(NodeRetained node, HashKey key) {
+ NodeRetained prevNode = null;
+ if (key != null) {
+ key = new HashKey(key);
+ }
+
+ synchronized (node.universe.sceneGraphLock) {
+ do {
+ if ((node instanceof SwitchRetained) &&
+ (prevNode != null) &&
+ !validSwitchChild((SwitchRetained) node, prevNode)) {
+ return false;
+ }
+ prevNode = node;
+ if (node instanceof SharedGroupRetained) {
+ // retrieve the last node ID
+ String nodeId = key.getLastNodeId();
+ Vector parents = ((SharedGroupRetained) node).parents;
+ // find the matching link
+ for(int i=parents.size()-1; i >=0; i--) {
+ NodeRetained link = (NodeRetained) parents.get(i);
+ if (link.nodeId.equals(nodeId)) {
+ node = link;
+ break;
+ }
+ }
+ if (node == prevNode) {
+ // Fail to found a matching link, this is
+ // probably cause by BHTree not yet updated
+ // because message not yet arrive
+ // when collision so it return current node as target.
+ return false;
+ }
+ } else {
+ node = node.parent;
+ }
+ } while (node != null);
+ // reach locale
+ }
+ return true;
+ }
+
+
+
+ /**
+ * Determinte if nodeR is a valid child to render for
+ * Switch Node swR.
+ */
+ static boolean validSwitchChild(SwitchRetained sw,
+ NodeRetained node) {
+
+ int whichChild = sw.whichChild;
+
+ if (whichChild == Switch.CHILD_NONE) {
+ return false;
+ }
+
+ if (whichChild == Switch.CHILD_ALL) {
+ return true;
+ }
+
+ ArrayList children = sw.children;
+
+ if (whichChild >= 0) { // most common case
+ return (children.get(whichChild) == node);
+ }
+
+ // Switch.CHILD_MASK
+ for (int i=children.size()-1; i >=0; i--) {
+ if (sw.childMask.get(i) &&
+ (children.get(i) == node)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Create mirror group when this Group AlternateCollisionTarget
+ * is set to true while live.
+ */
+ void createMirrorGroup() {
+ GroupRetained g;
+
+ mirrorGroup = new ArrayList();
+
+ Bounds bound = (collisionBound != null ?
+ collisionBound : getEffectiveBounds());
+
+ if (inSharedGroup) {
+ for (int i=0; i < localToVworldKeys.length; i++) {
+ g = new GroupRetained();
+ g.key = localToVworldKeys[i];
+ g.localToVworld = new Transform3D[1][];
+ g.localToVworldIndex = new int[1][];
+ g.localToVworld[0] = localToVworld[i];
+ g.localToVworldIndex[0] = localToVworldIndex[i];
+ g.collisionVwcBounds = new BoundingBox();
+ g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld());
+ g.sourceNode = this;
+ g.locale = locale; // need by getVisibleGeometryAtom()
+ mirrorGroup.add(g);
+ }
+ } else {
+ g = new GroupRetained();
+ g.localToVworld = new Transform3D[1][];
+ g.localToVworldIndex = new int[1][];
+ g.localToVworld[0] = localToVworld[0];
+ g.localToVworldIndex[0] = localToVworldIndex[0];
+ g.collisionVwcBounds = new BoundingBox();
+ g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld());
+ g.sourceNode = this;
+ g.locale = locale; // need by getVisibleGeometryAtom()
+ mirrorGroup.add(g);
+ }
+ }
+
+ void setBoundsAutoCompute(boolean autoCompute) {
+ if (autoCompute != boundsAutoCompute) {
+ super.setBoundsAutoCompute(autoCompute);
+ if (!autoCompute) {
+ localBounds = getEffectiveBounds();
+ }
+ if (source.isLive() && collisionBound == null && autoCompute
+ && mirrorGroup != null) {
+
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.COLLISION_BOUND_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY;
+ message.universe = universe;
+ message.args[0] = this;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+ }
+
+ void setBounds(Bounds bounds) {
+ super.setBounds(bounds);
+ if (source.isLive() && !boundsAutoCompute &&
+ collisionBound == null && mirrorGroup != null) {
+
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.COLLISION_BOUND_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY;
+ message.universe = universe;
+ message.args[0] = this;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+
+ int[] processViewSpecificInfo(int mode, HashKey k, View v, ArrayList vsgList, int[] keyList,
+ ArrayList leafList) {
+ int nchildren = children.size();
+ if (source.isLive()) {
+ for (int i = 0; i < nchildren; i++) {
+ NodeRetained child = (NodeRetained) children.get(i);
+ if (child instanceof LeafRetained) {
+ if (child instanceof LinkRetained) {
+ int lastCount = k.count;
+ LinkRetained ln = (LinkRetained) child;
+ if (k.count == 0) {
+ k.append(locale.nodeId);
+ }
+ keyList = ((GroupRetained)(ln.sharedGroup)).
+ processViewSpecificInfo(mode, k.append("+").append(ln.nodeId), v, vsgList,
+ keyList, leafList);
+ k.count = lastCount;
+ }
+ else {
+ ((LeafRetained)child).getMirrorObjects(leafList, k);
+ }
+ } else {
+ keyList = child.processViewSpecificInfo(mode, k, v, vsgList, keyList, leafList);
+ }
+ }
+ }
+ return keyList;
+ }
+
+ void findSwitchInfo(SetLiveState s, NodeRetained parentNode,
+ NodeRetained childNode, NodeRetained linkNode) {
+
+ NodeRetained child;
+ NodeRetained parent;
+
+ parentSwitchLinkChildIndex = -1;
+
+ // traverse up scene graph to find switch parent information
+ if (!inSharedGroup) {
+ child = (linkNode == null)? childNode: linkNode;
+ parent = parentNode;
+ while (parent != null) {
+ if (parent instanceof SwitchRetained) {
+ s.switchLevels[0]++;
+ if (s.closestSwitchParents[0] == null) {
+ s.closestSwitchParents[0] = (SwitchRetained)parent;
+ s.closestSwitchIndices[0] =
+ ((SwitchRetained)parent).switchIndexCount++;
+ }
+ if (parentSwitchLinkChildIndex == -1) {
+ parentSwitchLinkChildIndex =
+ ((GroupRetained)parent).children.indexOf(child);
+ }
+ } else if (parent instanceof SharedGroupRetained) {
+ if (parentSwitchLinkChildIndex == -1) {
+ parentSwitchLinkChildIndex =
+ ((GroupRetained)parent).children.indexOf(child);
+ }
+ }
+ child = parent;
+ parent = child.parent;
+ }
+ } else {
+ HashKey key;
+ int i,j;
+
+ s.switchLevels = new int[localToVworldKeys.length];
+ s.closestSwitchParents =
+ new SwitchRetained[localToVworldKeys.length];
+ s.closestSwitchIndices = new int[localToVworldKeys.length];
+ for (i=0; i<localToVworldKeys.length; i++) {
+ s.switchLevels[i] = -1;
+ s.closestSwitchParents[i] = null;
+ s.closestSwitchIndices[i] = -1;
+ }
+
+ for (i=0; i < localToVworldKeys.length; i++) {
+ child = (linkNode == null)? childNode: linkNode;
+ parent = parentNode;
+ key = new HashKey(localToVworldKeys[i]);
+
+ while (parent != null) {
+
+ if (parent instanceof SwitchRetained) {
+ s.switchLevels[i]++;
+ if (s.closestSwitchParents[i] == null) {
+ s.closestSwitchParents[i] = (SwitchRetained)parent;
+ s.closestSwitchIndices[i] =
+ ((SwitchRetained)parent).switchIndexCount++;
+
+ }
+ if (parentSwitchLinkChildIndex == -1) {
+ parentSwitchLinkChildIndex =
+ ((GroupRetained)parent).children.indexOf(child);
+ }
+ } else if (parent instanceof SharedGroupRetained) {
+ String nodeId = key.getLastNodeId();
+ Vector parents = ((SharedGroupRetained) parent).parents;
+ NodeRetained ln;
+
+ if (parentSwitchLinkChildIndex == -1) {
+ parentSwitchLinkChildIndex =
+ ((GroupRetained)parent).children.indexOf(child);
+ }
+
+ for(j=0; j< parents.size(); j++) {
+ ln = (NodeRetained)parents.get(j);
+ if (ln.nodeId.equals(nodeId)) {
+ parent = ln;
+ break;
+ }
+ }
+ }
+ child = parent;
+ parent = child.parent;
+ }
+ }
+ }
+ }
+
+ static void gatherBlUsers(ArrayList blUsers, Object[] blArr) {
+ ArrayList users;
+
+ for (int i=0; i<blArr.length; i++) {
+ users = ((BoundingLeafRetained)blArr[i]).users;
+ synchronized(users) {
+ blUsers.addAll(users);
+ }
+ }
+ }
+
+ // recursively found all geometryAtoms under this Group
+ void searchGeometryAtoms(UnorderList list) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = (NodeRetained)children.get(i);
+ child.searchGeometryAtoms(list);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/HashKey.java b/src/classes/share/javax/media/j3d/HashKey.java
new file mode 100644
index 0000000..d942ad6
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/HashKey.java
@@ -0,0 +1,240 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class HashKey extends Object {
+
+ /**
+ * The value is used for character storage.
+ */
+ char value[];
+
+ /**
+ * The count is the number of characters in the buffer.
+ */
+ int count = 0;
+
+ HashKey() {
+ this(16);
+ }
+
+ HashKey(int length) {
+ value = new char[length];
+ }
+
+ HashKey(HashKey hashkey) {
+ this.set(hashkey);
+ }
+
+ HashKey(String str) {
+ this(str.length() + 16);
+ append(str);
+ }
+
+ void set(HashKey hashkey) {
+ int i;
+
+ if (this.count < hashkey.count) {
+ this.value = new char[hashkey.count];
+ }
+
+ for (i=0; i<hashkey.count; i++) {
+ this.value[i] = hashkey.value[i];
+ }
+ this.count = hashkey.count;
+ }
+
+ void reset() {
+ count = 0;
+ }
+
+ void ensureCapacity(int minimumCapacity) {
+ int maxCapacity = value.length;
+
+ if (minimumCapacity > maxCapacity) {
+ int newCapacity = (maxCapacity + 1) * 2;
+ if (minimumCapacity > newCapacity) {
+ newCapacity = minimumCapacity;
+ }
+
+ char newValue[] = new char[newCapacity];
+ System.arraycopy(value, 0, newValue, 0, count);
+ value = newValue;
+ }
+ }
+
+ HashKey append(String str) {
+ int len = 0;
+
+ if (str == null)
+ return this;
+
+ len = str.length();
+ ensureCapacity(count + len);
+ str.getChars(0, len, value, count);
+ count += len;
+ return this;
+ }
+
+ public int hashCode() {
+ int h = 0;
+ int off = 0;
+ char val[] = value;
+ int len = count;
+
+ if (len < 16) {
+ for (int i = len ; i > 0; i--) {
+ h = (h * 37) + val[off++];
+ }
+ } else {
+ // only sample some characters
+ int skip = len / 8;
+ for (int i = len ; i > 0; i -= skip, off += skip) {
+ h = (h * 39) + val[off];
+ }
+ }
+ return h;
+ }
+
+ public boolean equals(Object anObject) {
+ if ((anObject != null) && (anObject instanceof HashKey)) {
+ HashKey anotherHashKey = (HashKey)anObject;
+ int n = count;
+ if (n == anotherHashKey.count) {
+ char v1[] = value;
+ char v2[] = anotherHashKey.value;;
+ int i = 0;
+ int j = 0;
+ while (n-- != 0) {
+ if (v1[i++] != v2[j++]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /* For internal use only. */
+ private int equals(HashKey hk) {
+ int index = 0;
+
+ while((index < count) && (index < hk.count)) {
+ if(value[index] < hk.value[index])
+ return -1;
+ else if(value[index] > hk.value[index])
+ return 1;
+ index++;
+ }
+
+ if(count == hk.count)
+ // Found it!
+ return 0;
+ else if(count < hk.count)
+ return -1;
+ else
+ return 1;
+
+ }
+
+
+ /* For package use only. */
+ int equals(HashKey localToVworldKeys[], int start, int end) {
+ int mid;
+
+ mid = start +((end - start)/ 2);
+ if(localToVworldKeys[mid] != null) {
+ int test = equals(localToVworldKeys[mid]);
+
+ if((test < 0) && (start != mid))
+ return equals(localToVworldKeys, start, mid);
+ else if((test > 0) && (start != mid))
+ return equals(localToVworldKeys, mid, end);
+ else if(test == 0)
+ return mid;
+ else
+ return -1;
+ }
+ // A null haskey encountered.
+ return -2;
+ }
+
+ /* For package use only. */
+ boolean equals(HashKey localToVworldKeys[], int[] index,
+ int start, int end) {
+
+ int mid;
+
+ mid = start +((end - start)/ 2);
+ if(localToVworldKeys[mid] != null) {
+ int test = equals(localToVworldKeys[mid]);
+
+ if(start != mid) {
+ if(test < 0) {
+ return equals(localToVworldKeys, index, start, mid);
+ }
+ else if(test > 0) {
+ return equals(localToVworldKeys, index, mid, end);
+ }
+ }
+ else { // (start == mid)
+ if(test < 0) {
+ index[0] = mid;
+ return false;
+ }
+ else if(test > 0) {
+ index[0] = mid+1;
+ return false;
+ }
+ }
+
+ // (test == 0)
+ index[0] = mid;
+ return true;
+
+ }
+ // A null haskey encountered.
+ // But we still want to return the index where we encounter it.
+ index[0] = mid;
+ return false;
+ }
+
+ public String toString() {
+ return new String(value, 0, count);
+ }
+
+ String getLastNodeId() {
+ int i, j, temp;
+
+ for(i=(count-1); i>0; i--)
+ if(value[i] == '+')
+ break;
+
+ if(i>0) {
+ value[i++] = '\0';
+ temp = count-i;
+ char v1[] = new char[temp];
+ for(j=0; j<temp; j++, i++) {
+ v1[j] = value[i];
+ value[i] = '\0';
+ }
+ count = count - (temp+1);
+ return new String(v1);
+ }
+
+ return new String(value, 0, count);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/HiResCoord.java b/src/classes/share/javax/media/j3d/HiResCoord.java
new file mode 100644
index 0000000..31fb48e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/HiResCoord.java
@@ -0,0 +1,724 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.lang.Integer;
+import javax.vecmath.*;
+
+/**
+ * High resolution coordinate object.
+ *
+ */
+
+/**
+ * The HiResCoord object specifies the location of scene
+ * components within the Virtual Universe.
+ * The coordinates of all scene graph objects are relative to
+ * the HiResCoord of the Locale in which they are contained.
+ * <P>
+ * The HiResCoord defines a point using a set of three
+ * high-resolution coordinates, each of which consists of three
+ * two's-complement fixed-point numbers.
+ * Each high-resolution number consists of 256 total bits with a
+ * binary point at bit 128, or between the integers at index
+* 3 and 4. A high-resolution coordinate of 1.0 is defined to be exactly
+ * 1 meter. This coordinate system is sufficient to describe a
+ * universe in excess of several billion light years across, yet
+ * still define objects smaller than a proton.
+ * <P>
+ * Java 3D uses integer arrays of length
+ * eight to define or extract a single 256-bit coordinate value.
+ * Java 3D interprets the integer at index 0 as the 32
+ * most-significant bits and the integer at index 7 as the 32
+ * least-significant bits.
+ */
+
+public class HiResCoord {
+ /**
+ * The eight-element array containing the high resolution coordinate's
+ * x value.
+ */
+ int x[];
+
+ /**
+ * The eight-element array containing the high resolution coordinate's
+ * y value.
+ */
+ int y[];
+
+ /**
+ * The eight-element array containing the high resolution coordinate's
+ * z value.
+ */
+ int z[];
+
+private double scales[] = {
+ 79228162514264337593543950336.0, // 2^96
+ 18446744073709551616.0, // 2^64
+ 4294967296.0, // 2^32
+ 1.0, // 2^0
+ 2.3283064365386962890625e-10, // 2^-32
+ 5.421010862427522170037264004349708557128906250000000000000000e-20, // 2^-64
+ 1.26217744835361888865876570445245796747713029617443680763244628906250e-29, // 2^-96
+ 2.938735877055718769921841343055614194546663891930218803771879265696043148636817932128906250e-39 };
+
+
+
+ /**
+ * Constructs and initializes a new HiResCoord using the values
+ * provided in the argument.
+ * The HiResCoord represents 768 bits of floating point 3-Space.
+ * @param X an eight element array specifying the x position
+ * @param Y an eight element array specifying the y position
+ * @param Z an eight element array specifying the z position
+ */
+ public HiResCoord(int[] X, int[] Y, int[] Z) {
+ int i;
+
+ this.x = new int[8];
+ this.y = new int[8];
+ this.z = new int[8];
+
+ for(i=0;i<8;i++) {
+ this.x[i] = X[i];
+ this.y[i] = Y[i];
+ this.z[i] = Z[i];
+ }
+
+ }
+
+ /**
+ * Constructs and initializes a new HiResCoord using the values
+ * provided in the argument.
+ * The HiResCoord represents 768 bits of floating point 3-Space.
+ * @param hc the HiResCoord to copy
+ */
+ public HiResCoord(HiResCoord hc) {
+ this.x = new int[8];
+ this.y = new int[8];
+ this.z = new int[8];
+
+ this.x[0] = hc.x[0];
+ this.y[0] = hc.y[0];
+ this.z[0] = hc.z[0];
+
+ this.x[1] = hc.x[1];
+ this.y[1] = hc.y[1];
+ this.z[1] = hc.z[1];
+
+ this.x[2] = hc.x[2];
+ this.y[2] = hc.y[2];
+ this.z[2] = hc.z[2];
+
+ this.x[3] = hc.x[3];
+ this.y[3] = hc.y[3];
+ this.z[3] = hc.z[3];
+
+ this.x[4] = hc.x[4];
+ this.y[4] = hc.y[4];
+ this.z[4] = hc.z[4];
+
+ this.x[5] = hc.x[5];
+ this.y[5] = hc.y[5];
+ this.z[5] = hc.z[5];
+
+ this.x[6] = hc.x[6];
+ this.y[6] = hc.y[6];
+ this.z[6] = hc.z[6];
+
+ this.x[7] = hc.x[7];
+ this.y[7] = hc.y[7];
+ this.z[7] = hc.z[7];
+ }
+
+ /**
+ * Constructs and initializes a new HiResCoord located at (0, 0, 0).
+ * The HiResCoord represents 768 bits of floating point 3-Space.
+ */
+ public HiResCoord() {
+ this.x = new int[8];
+ this.y = new int[8];
+ this.z = new int[8];
+ }
+
+ /**
+ * Sets this HiResCoord to the location specified by the
+ * parameters provided.
+ * @param X an eight-element array specifying the x position
+ * @param Y an eight-element array specifying the y position
+ * @param Z an eight-element array specifying the z position
+ */
+ public void setHiResCoord(int[] X, int[] Y, int[] Z) {
+ int i;
+
+ for(i=0;i<8;i++) {
+ this.x[i] = X[i];
+ this.y[i] = Y[i];
+ this.z[i] = Z[i];
+ }
+
+ }
+
+ /**
+ * Sets this HiResCoord to the location specified by the
+ * hires provided.
+ * @param hires the hires coordinate to copy
+ */
+ public void setHiResCoord(HiResCoord hires) {
+ this.x[0] = hires.x[0];
+ this.y[0] = hires.y[0];
+ this.z[0] = hires.z[0];
+
+ this.x[1] = hires.x[1];
+ this.y[1] = hires.y[1];
+ this.z[1] = hires.z[1];
+
+ this.x[2] = hires.x[2];
+ this.y[2] = hires.y[2];
+ this.z[2] = hires.z[2];
+
+ this.x[3] = hires.x[3];
+ this.y[3] = hires.y[3];
+ this.z[3] = hires.z[3];
+
+ this.x[4] = hires.x[4];
+ this.y[4] = hires.y[4];
+ this.z[4] = hires.z[4];
+
+ this.x[5] = hires.x[5];
+ this.y[5] = hires.y[5];
+ this.z[5] = hires.z[5];
+
+ this.x[6] = hires.x[6];
+ this.y[6] = hires.y[6];
+ this.z[6] = hires.z[6];
+
+ this.x[7] = hires.x[7];
+ this.y[7] = hires.y[7];
+ this.z[7] = hires.z[7];
+ }
+
+
+ /**
+ * Sets this HiResCoord's X value to that specified by the argument.
+ * @param X an eight-element array specifying the x position
+ */
+ public void setHiResCoordX(int[] X) {
+ this.x[0] = X[0];
+ this.x[1] = X[1];
+ this.x[2] = X[2];
+ this.x[3] = X[3];
+ this.x[4] = X[4];
+ this.x[5] = X[5];
+ this.x[6] = X[6];
+ this.x[7] = X[7];
+ }
+
+ /**
+ * Sets this HiResCoord's Y value to that specified by the argument.
+ * @param Y an eight-element array specifying the y position
+ */
+ public void setHiResCoordY(int[] Y) {
+ this.y[0] = Y[0];
+ this.y[1] = Y[1];
+ this.y[2] = Y[2];
+ this.y[3] = Y[3];
+ this.y[4] = Y[4];
+ this.y[5] = Y[5];
+ this.y[6] = Y[6];
+ this.y[7] = Y[7];
+ }
+
+ /**
+ * Sets this HiResCoord's Z value to that specified by the argument.
+ * @param Z an eight-element array specifying the z position
+ */
+ public void setHiResCoordZ(int[] Z) {
+ this.z[0] = Z[0];
+ this.z[1] = Z[1];
+ this.z[2] = Z[2];
+ this.z[3] = Z[3];
+ this.z[4] = Z[4];
+ this.z[5] = Z[5];
+ this.z[6] = Z[6];
+ this.z[7] = Z[7];
+ }
+
+ /**
+ * Retrieves this HiResCoord's location and saves the coordinates
+ * in the specified arrays. The arrays must be large enough
+ * to hold all of the ints.
+ * @param X an eight element array that will receive the x position
+ * @param Y an eight element array that will receive the y position
+ * @param Z an eight element array that will receive the z position
+ */
+ public void getHiResCoord(int[] X, int[] Y, int[] Z) {
+ X[0] = this.x[0];
+ X[1] = this.x[1];
+ X[2] = this.x[2];
+ X[3] = this.x[3];
+ X[4] = this.x[4];
+ X[5] = this.x[5];
+ X[6] = this.x[6];
+ X[7] = this.x[7];
+
+ Y[0] = this.y[0];
+ Y[1] = this.y[1];
+ Y[2] = this.y[2];
+ Y[3] = this.y[3];
+ Y[4] = this.y[4];
+ Y[5] = this.y[5];
+ Y[6] = this.y[6];
+ Y[7] = this.y[7];
+
+ Z[0] = this.z[0];
+ Z[1] = this.z[1];
+ Z[2] = this.z[2];
+ Z[3] = this.z[3];
+ Z[4] = this.z[4];
+ Z[5] = this.z[5];
+ Z[6] = this.z[6];
+ Z[7] = this.z[7];
+ }
+
+ /**
+ * Retrieves this HiResCoord's location and places it into the hires
+ * argument.
+ * @param hc the hires coordinate that will receive this node's location
+ */
+ public void getHiResCoord(HiResCoord hc) {
+ hc.x[0] = this.x[0];
+ hc.x[1] = this.x[1];
+ hc.x[2] = this.x[2];
+ hc.x[3] = this.x[3];
+ hc.x[4] = this.x[4];
+ hc.x[5] = this.x[5];
+ hc.x[6] = this.x[6];
+ hc.x[7] = this.x[7];
+
+ hc.y[0] = this.y[0];
+ hc.y[1] = this.y[1];
+ hc.y[2] = this.y[2];
+ hc.y[3] = this.y[3];
+ hc.y[4] = this.y[4];
+ hc.y[5] = this.y[5];
+ hc.y[6] = this.y[6];
+ hc.y[7] = this.y[7];
+
+ hc.z[0] = this.z[0];
+ hc.z[1] = this.z[1];
+ hc.z[2] = this.z[2];
+ hc.z[3] = this.z[3];
+ hc.z[4] = this.z[4];
+ hc.z[5] = this.z[5];
+ hc.z[6] = this.z[6];
+ hc.z[7] = this.z[7];
+ }
+
+ /**
+ * Retrieves this HiResCoord's X value and stores it in the specified
+ * array. The array must be large enough to hold all of the ints.
+ * @param X an eight-element array that will receive the x position
+ */
+ public void getHiResCoordX(int[] X) {
+ X[0] = this.x[0];
+ X[1] = this.x[1];
+ X[2] = this.x[2];
+ X[3] = this.x[3];
+ X[4] = this.x[4];
+ X[5] = this.x[5];
+ X[6] = this.x[6];
+ X[7] = this.x[7];
+ }
+
+ /**
+ * Retrieves this HiResCoord's Y value and stores it in the specified
+ * array. The array must be large enough to hold all of the ints.
+ * @param Y an eight-element array that will receive the y position
+ */
+ public void getHiResCoordY(int[] Y) {
+ Y[0] = this.y[0];
+ Y[1] = this.y[1];
+ Y[2] = this.y[2];
+ Y[3] = this.y[3];
+ Y[4] = this.y[4];
+ Y[5] = this.y[5];
+ Y[6] = this.y[6];
+ Y[7] = this.y[7];
+ }
+
+ /**
+ * Retrieves this HiResCoord's Z value and stores it in the specified
+ * array. The array must be large enough to hold all of the ints.
+ * @param Z an eight-element array that will receive the z position
+ */
+ public void getHiResCoordZ(int[] Z) {
+ Z[0] = this.z[0];
+ Z[1] = this.z[1];
+ Z[2] = this.z[2];
+ Z[3] = this.z[3];
+ Z[4] = this.z[4];
+ Z[5] = this.z[5];
+ Z[6] = this.z[6];
+ Z[7] = this.z[7];
+ }
+
+ /**
+ * Compares the specified HiResCoord to this HiResCoord.
+ * @param h1 the second HiResCoord
+ * @return true if equal, false if not equal
+ */
+ public boolean equals(HiResCoord h1) {
+ try {
+ return ((this.x[0] == h1.x[0])
+ && (this.x[1] == h1.x[1])
+ && (this.x[2] == h1.x[2])
+ && (this.x[3] == h1.x[3])
+ && (this.x[4] == h1.x[4])
+ && (this.x[5] == h1.x[5])
+ && (this.x[6] == h1.x[6])
+ && (this.x[7] == h1.x[7])
+ && (this.y[0] == h1.y[0])
+ && (this.y[1] == h1.y[1])
+ && (this.y[2] == h1.y[2])
+ && (this.y[3] == h1.y[3])
+ && (this.y[4] == h1.y[4])
+ && (this.y[5] == h1.y[5])
+ && (this.y[6] == h1.y[6])
+ && (this.y[7] == h1.y[7])
+ && (this.z[0] == h1.z[0])
+ && (this.z[1] == h1.z[1])
+ && (this.z[2] == h1.z[2])
+ && (this.z[3] == h1.z[3])
+ && (this.z[4] == h1.z[4])
+ && (this.z[5] == h1.z[5])
+ && (this.z[6] == h1.z[6])
+ && (this.z[7] == h1.z[7]));
+ }
+ catch (NullPointerException e2) {return false;}
+
+ }
+
+ /**
+ * Returns true if the Object o1 is of type HiResCoord and all of the
+ * data members of o1 are equal to the corresponding data members in
+ * this HiResCoord.
+ * @param o1 the second HiResCoord
+ * @return true if equal, false if not equal
+ */
+ public boolean equals(Object o1) {
+ try {
+ HiResCoord h1 = (HiResCoord)o1;
+ return ((this.x[0] == h1.x[0])
+ && (this.x[1] == h1.x[1])
+ && (this.x[2] == h1.x[2])
+ && (this.x[3] == h1.x[3])
+ && (this.x[4] == h1.x[4])
+ && (this.x[5] == h1.x[5])
+ && (this.x[6] == h1.x[6])
+ && (this.x[7] == h1.x[7])
+ && (this.y[0] == h1.y[0])
+ && (this.y[1] == h1.y[1])
+ && (this.y[2] == h1.y[2])
+ && (this.y[3] == h1.y[3])
+ && (this.y[4] == h1.y[4])
+ && (this.y[5] == h1.y[5])
+ && (this.y[6] == h1.y[6])
+ && (this.y[7] == h1.y[7])
+ && (this.z[0] == h1.z[0])
+ && (this.z[1] == h1.z[1])
+ && (this.z[2] == h1.z[2])
+ && (this.z[3] == h1.z[3])
+ && (this.z[4] == h1.z[4])
+ && (this.z[5] == h1.z[5])
+ && (this.z[6] == h1.z[6])
+ && (this.z[7] == h1.z[7]));
+ }
+ catch (NullPointerException e2) {return false;}
+ catch (ClassCastException e1) {return false;}
+
+
+ }
+ /**
+ * Adds two HiResCoords placing the results into this HiResCoord.
+ * @param h1 the first HiResCoord
+ * @param h2 the second HiResCoord
+ */
+ public void add(HiResCoord h1, HiResCoord h2) {
+ // needs to handle carry bits
+ // move to long, add, add in carry bit
+
+ hiResAdd( this, h1, h2 );
+
+ }
+
+ /**
+ * Subtracts two HiResCoords placing the results into this HiResCoord.
+ * @param h1 the first HiResCoord
+ * @param h2 the second HiResCoord
+ */
+ public void sub(HiResCoord h1, HiResCoord h2) {
+ HiResCoord tmpHc = new HiResCoord();
+
+ // negate via two's complement then add
+ //
+ hiResNegate( tmpHc, h2);
+ hiResAdd( this, h1, tmpHc);
+
+ }
+
+ /**
+ * Negates the specified HiResCoords and places the
+ * results into this HiResCoord.
+ * @param h1 the source HiResCoord
+ */
+ public void negate(HiResCoord h1) {
+
+ hiResNegate( this, h1);
+
+ }
+
+ /**
+ * Negates this HiResCoord
+ */
+ public void negate() {
+
+ hiResNegate( this, this );
+
+ }
+
+ /**
+ * Scales the specified HiResCoords by the specified value and
+ * places the results into this HiResCoord.
+ * @param scale the amount to scale the specified HiResCoord
+ * @param h1 the source HiResCoord
+ */
+ public void scale(int scale, HiResCoord h1) {
+ hiResScale( h1.x, this.x, scale);
+ hiResScale( h1.y, this.y, scale);
+ hiResScale( h1.z, this.z, scale);
+ }
+
+ /**
+ * Scales this HiResCoord by the specified value.
+ * @param scale the amount to scale the specified HiResCoord
+ */
+ public void scale(int scale) {
+ hiResScale( this.x, this.x, scale);
+ hiResScale( this.y, this.y, scale);
+ hiResScale( this.z, this.z, scale);
+ return;
+ }
+
+ /**
+ * Subtracts the specified HiResCoord from this HiResCoord
+ * placing the difference vector into the specified
+ * double-precision vector.
+ * @param h1 the HiResCoord to be subtracted from this
+ * @param v the vector that will receive the result
+ */
+ public void difference(HiResCoord h1, Vector3d v) {
+ // negate coord via two compliment, add, convert result to double
+ // by scaling each bit set appropriately
+
+ hiResDiff( this, h1, v);
+ return;
+ }
+
+ /**
+ * The floating point distance between the specified
+ * HiResCoord and this HiResCoord.
+ * @param h1 the second HiResCoord
+ */
+ public double distance(HiResCoord h1) {
+ Vector3d diff = new Vector3d();
+
+ hiResDiff( this, h1, diff);
+
+ return( Math.sqrt( diff.x*diff.x + diff.y*diff.y + diff.z*diff.z));
+ }
+
+ private void hiResNegate( HiResCoord ho, HiResCoord hi) {
+
+ negateCoord( ho.x, hi.x);
+ negateCoord( ho.y, hi.y);
+ negateCoord( ho.z, hi.z);
+
+ return;
+ }
+
+ private void negateCoord( int cout[], int cin[] ) {
+ int i;
+
+ for(i=0;i<8;i++) {
+ cout[i] = ~cin[i]; // take compliment of each
+ }
+
+ for(i=7;i>=0;i--) { // add one
+ if( cout[i] == 0xffffffff) {
+ cout[i] = 0;
+ } else {
+ cout[i] += 1;
+ break;
+ }
+ }
+ return;
+ }
+
+ private void hiResAdd(HiResCoord ho, HiResCoord h1, HiResCoord h2 ){
+ int i;
+ long tmp1, tmp2,carry;
+ long signMask = Integer.MAX_VALUE;
+ long signBit = 1;
+ signBit = signBit << 31;
+ long carryMask = 0x7fffffff;
+ carryMask = carryMask <<1;
+ carryMask += 1;
+
+
+ carry = 0;
+ for(i=7;i>0;i--) {
+ tmp1 = 0;
+ tmp1 = signMask & h1.x[i]; // mask off sign bit so will not get put in msb
+ if( h1.x[i] < 0 ) tmp1 |= signBit; // add sign bit back
+
+ tmp2 = 0;
+ tmp2 = signMask & h2.x[i]; // mask off sign bit so will not get put in msb
+ if( h2.x[i] < 0 ) tmp2 |= signBit; // add sign bit back
+
+ tmp2 = tmp2+tmp1 + carry;
+ carry = tmp2 >> 32; // get carry bits for next operation
+ ho.x[i] = (int)(tmp2 & carryMask); // mask off high bits
+ }
+ ho.x[0] = h1.x[0] + h2.x[0] + (int)carry;
+
+
+ carry = 0;
+ for(i=7;i>0;i--) {
+ tmp1 = 0;
+ tmp1 = signMask & h1.y[i]; // mask off sign bit so will not get put in msb
+ if( h1.y[i] < 0 ) tmp1 |= signBit; // add sign bit back
+
+ tmp2 = 0;
+ tmp2 = signMask & h2.y[i]; // mask off sign bit so will not get put in msb
+ if( h2.y[i] < 0 ) tmp2 |= signBit; // add sign bit back
+
+ tmp2 = tmp2+tmp1 + carry;
+ carry = tmp2 >> 32; // get carry bits for next operation
+ ho.y[i] = (int)(tmp2 & carryMask); // mask off high bits
+ }
+ ho.y[0] = h1.y[0] + h2.y[0] + (int)carry;
+
+ carry = 0;
+ for(i=7;i>0;i--) {
+ tmp1 = 0;
+ tmp1 = signMask & h1.z[i]; // mask off sign bit so will not get put in msb
+ if( h1.z[i] < 0 ) tmp1 |= signBit; // add sign bit back
+
+ tmp2 = 0;
+ tmp2 = signMask & h2.z[i]; // mask off sign bit so will not get put in msb
+ if( h2.z[i] < 0 ) tmp2 |= signBit; // add sign bit back
+
+ tmp2 = tmp2+tmp1 + carry;
+ carry = tmp2 >> 32; // get carry bits for next operation
+ ho.z[i] = (int)(tmp2 & carryMask); // mask off high bits
+ }
+ ho.z[0] = h1.z[0] + h2.z[0] + (int)carry;
+ return;
+ }
+
+ private void hiResScale( int tin[], int tout[], double scale) {
+ int i;
+ long tmp,carry;
+ int signMask = Integer.MAX_VALUE;
+ long carryMask = 0x7fffffff;
+ carryMask = carryMask <<1;
+ carryMask += 1;
+ long signBit = 1;
+ signBit = signBit << 31;
+
+ carry = 0;
+ for(i=7;i>0;i--) {
+ tmp = 0;
+ tmp = (long)(signMask & tin[i]); // mask off sign bit
+ if( tin[i] < 0 ) tmp |= signBit; // add sign bit back
+ tmp = (long)(tmp*scale + carry);
+ carry = tmp >> 32; // get carry bits for next operation
+ tout[i] = (int)(tmp & carryMask); // mask off high bits
+ }
+ tout[0] = (int)(tin[0]*scale + carry);
+ return;
+ }
+ private void hiResDiff( HiResCoord h1, HiResCoord h2, Vector3d diff) {
+ int i;
+ HiResCoord diffHi = new HiResCoord();
+ long value;
+ int coordSpace[] = new int[8];
+ int[] tempCoord;
+ int signMask = Integer.MAX_VALUE;
+ long signBit = 1;
+ signBit = signBit << 31;
+
+ // negate via two's complement then add
+ //
+ hiResNegate( diffHi, h2);
+ hiResAdd( diffHi, h1, diffHi);
+
+
+ if( diffHi.x[0] < 0 ) {
+ tempCoord = coordSpace;
+ negateCoord( tempCoord, diffHi.x );
+ } else {
+ tempCoord = diffHi.x;
+ }
+ diff.x = 0;
+ for(i=7;i>0;i--) {
+ value = (long)(tempCoord[i] & signMask);
+ if( tempCoord[i] < 0) value |= signBit;
+ diff.x += (double)(scales[i]*value);
+ }
+ diff.x += scales[0]*tempCoord[0];
+ if( diffHi.x[0] < 0 )diff.x = -diff.x;
+
+ if( diffHi.y[0] < 0 ) {
+ tempCoord = coordSpace;
+ negateCoord( tempCoord, diffHi.y );
+ } else {
+ tempCoord = diffHi.y;
+ }
+ diff.y = 0;
+ for(i=7;i>0;i--) {
+ value = (long)(tempCoord[i] & signMask);
+ if( tempCoord[i] < 0) value |= signBit;
+ diff.y += scales[i]*value;
+ }
+ diff.y += scales[0]*tempCoord[0];
+ if( diffHi.y[0] < 0 )diff.y = -diff.y;
+
+ if( diffHi.z[0] < 0 ) {
+ tempCoord = coordSpace;
+ negateCoord( tempCoord, diffHi.z );
+ } else {
+ tempCoord = diffHi.z;
+ }
+ diff.z = 0;
+ for(i=7;i>0;i--) {
+ value = (long)(tempCoord[i] & signMask);
+ if( tempCoord[i] < 0) value |= signBit;
+ diff.z += scales[i]*value;
+ }
+ diff.z += scales[0]*tempCoord[0];
+ if( diffHi.z[0] < 0 )diff.z = -diff.z;
+ return;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IllegalRenderingStateException.java b/src/classes/share/javax/media/j3d/IllegalRenderingStateException.java
new file mode 100644
index 0000000..1096573
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IllegalRenderingStateException.java
@@ -0,0 +1,35 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates an illegal state for rendering. This exception is currently
+ * unused.
+ */
+public class IllegalRenderingStateException extends IllegalStateException {
+
+ /**
+ * Create the exception object with default values.
+ */
+ public IllegalRenderingStateException(){
+ }
+
+ /**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public IllegalRenderingStateException(String str){
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/IllegalSceneGraphException.java b/src/classes/share/javax/media/j3d/IllegalSceneGraphException.java
new file mode 100644
index 0000000..046eec1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IllegalSceneGraphException.java
@@ -0,0 +1,40 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates an illegal Java 3D scene graph.
+ * For example, the following is illegal:
+ * <ul>
+ * <li>A ViewPlatform node under a ViewSpecificGroup</li>
+ * </ul>
+ *
+ * @since Java 3D 1.3
+ */
+
+public class IllegalSceneGraphException extends RuntimeException {
+
+ /**
+ * Create the exception object with default values.
+ */
+ public IllegalSceneGraphException() {
+ }
+
+ /**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public IllegalSceneGraphException(String str) {
+ super(str);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IllegalSharingException.java b/src/classes/share/javax/media/j3d/IllegalSharingException.java
new file mode 100644
index 0000000..7938541
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IllegalSharingException.java
@@ -0,0 +1,60 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates an illegal attempt to share a scene graph object. For example,
+ * the following are illegal:
+ * <UL>
+ * <LI>referencing a shared subgraph in more than one virtual universe</LI>
+ * <LI>using the same node both in the scene graph and in an
+ * immediate mode graphics context</LI>
+ * <LI>including any of the following unsupported types of leaf node within a shared subgraph:</LI>
+ * <UL>
+ * <LI>AlternateAppearance</LI>
+ * <LI>Background</LI>
+ * <LI>Behavior</LI>
+ * <LI>BoundingLeaf</LI>
+ * <LI>Clip</LI>
+ * <LI>Fog</LI>
+ * <LI>ModelClip</LI>
+ * <LI>Soundscape</LI>
+ * <LI>ViewPlatform</LI>
+ * </UL>
+ * <LI>referencing a BranchGroup node in more than one of the following
+ * ways:</LI>
+ * <UL>
+ * <LI>attaching it to a (single) Locale</LI>
+ * <LI>adding it as a child of a Group Node within the scene graph</LI>
+ * <LI>referencing it from a (single) Background Leaf Node as
+ * background geometry</LI>
+ * </UL>
+ * </UL>
+ */
+public class IllegalSharingException extends IllegalSceneGraphException {
+
+ /**
+ * Create the exception object with default values.
+ */
+ public IllegalSharingException() {
+ }
+
+ /**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public IllegalSharingException(String str) {
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ImageComponent.java b/src/classes/share/javax/media/j3d/ImageComponent.java
new file mode 100644
index 0000000..ab63f56
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ImageComponent.java
@@ -0,0 +1,346 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Abstract class that is used to define 2D or 3D ImageComponent
+ * classes used in a Java 3D scene graph. This is used for texture
+ * images, background images and raster components of Shape3D nodes.
+ *
+ * <p>
+ * Image data may be passed to this ImageComponent object in
+ * one of two ways: by copying the image data into this object or by
+ * accessing the image data by reference.
+ *
+ * <p>
+ * <ul>
+ * <li>
+ * <b>By Copying:</b>
+ * By default, the set and get image methods copy the image
+ * data into or out of this ImageComponent object. This is
+ * appropriate for many applications, since the application may reuse
+ * the RenderedImage object after copying it to the ImageComponent.
+ * </li>
+ * <li><b>By Reference:</b>
+ * A new feature in Java 3D version 1.2 allows image data to
+ * be accessed by reference, directly from the RenderedImage object.
+ * To use this feature, you need to construct an ImageComponent object
+ * with the <code>byReference</code> flag set to <code>true</code>.
+ * In this mode, a reference to the input data is saved, but the data
+ * itself is not necessarily copied (although it may be, depending on
+ * the value of the <code>yUp</code> flag, the format of the
+ * ImageComponent, and the format of the RenderedImage). Image data
+ * referenced by an ImageComponent object can only be modified via
+ * the updateData method.
+ * Applications must exercise care not to violate this rule. If any
+ * referenced RenderedImage is modified outside the updateData method
+ * after it has been passed
+ * to an ImageComponent object, the results are undefined.
+ * Another restriction in by-reference mode is that if the specified
+ * RenderedImage is not an instance of BufferedImage, then
+ * this ImageComponent cannot be used for readRaster or
+ * off-screen rendering operations, since these operations modify
+ * the ImageComponent data.
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * An image component object also specifies whether the orientation of
+ * its image data is "y-up" or "y-down" (the default). Y-up mode
+ * causes images to be interpreted as having their origin at the lower
+ * left (rather than the default upper left) of a texture or raster
+ * image with successive scan lines moving up. This is more
+ * consistent with texture mapping data onto a surface, and maps
+ * directly into the the way textures are used in OpenGL and other 3D
+ * APIs. Setting the <code>yUp</code> flag to true in conjunction
+ * with setting the <code>byReference</code> flag to true makes it
+ * possible for Java 3D to avoid copying the texture map in some
+ * cases.
+ *
+ * <p>
+ * Note that all color fields are treated as unsigned values, even though
+ * Java does not directly support unsigned variables. This means, for
+ * example, that an ImageComponent using a format of FORMAT_RGB5 can
+ * represent red, green, and blue values between 0 and 31, while an
+ * ImageComponent using a format of FORMAT_RGB8 can represent color
+ * values between 0 and 255. Even when byte values are used to create a
+ * RenderedImage with 8-bit color components, the resulting colors
+ * (bytes) are interpreted as if they were unsigned.
+ * Values greater than 127 can be assigned to a byte variable using a
+ * type cast. For example:
+ * <ul>byteVariable = (byte) intValue; // intValue can be > 127</ul>
+ * If intValue is greater than 127, then byteVariable will be negative. The
+ * correct value will be extracted when it is used (by masking off the upper
+ * bits).
+ */
+
+public abstract class ImageComponent extends NodeComponent {
+ //
+ // Pixel format values
+ //
+
+ /**
+ * Specifies that each pixel contains 3 8-bit channels: one each
+ * for red, green, blue. Same as FORMAT_RGB8.
+ */
+ public static final int FORMAT_RGB = 1;
+
+ /**
+ * Specifies that each pixel contains 4 8-bit channels: one each
+ * for red, green, blue, alpha. Same as FORMAT_RGBA8.
+ */
+ public static final int FORMAT_RGBA = 2;
+
+ /**
+ * Specifies that each pixel contains 3 8-bit channels: one each
+ * for red, green, blue. Same as FORMAT_RGB.
+ */
+ public static final int FORMAT_RGB8 = FORMAT_RGB;
+
+ /**
+ * Specifies that each pixel contains 4 8-bit channels: one each
+ * for red, green, blue, alpha. Same as FORMAT_RGBA.
+ */
+ public static final int FORMAT_RGBA8 = FORMAT_RGBA;
+
+ /**
+ * Specifies that each pixel contains 3 5-bit channels: one each
+ * for red, green, blue.
+ */
+ public static final int FORMAT_RGB5 = 3;
+
+ /**
+ * Specifies that each pixel contains 3 5-bit channels: one each
+ * for red, green, blue and 1 1-bit channel for alpha.
+ */
+ public static final int FORMAT_RGB5_A1 = 4;
+
+ /**
+ * Specifies that each pixel contains 3 4-bit channels: one each
+ * for red, green, blue.
+ */
+ public static final int FORMAT_RGB4 = 5;
+
+ /**
+ * Specifies that each pixel contains 4 4-bit channels: one each
+ * for red, green, blue, alpha.
+ */
+ public static final int FORMAT_RGBA4 = 6;
+
+ /**
+ * Specifies that each pixel contains 2 4-bit channels: one each
+ * for luminance and alpha.
+ */
+ public static final int FORMAT_LUM4_ALPHA4 = 7;
+
+ /**
+ * Specifies that each pixel contains 2 8-bit channels: one each
+ * for luminance and alpha.
+ */
+ public static final int FORMAT_LUM8_ALPHA8 = 8;
+
+ /**
+ * Specifies that each pixel contains 2 3-bit channels: one each
+ * for red, green, and 1 2-bit channel for blue.
+ */
+ public static final int FORMAT_R3_G3_B2 = 9;
+
+ /**
+ * Specifies that each pixel contains 1 8-bit channel: it can be
+ * used for only luminance or only alpha or only intensity.
+ */
+ public static final int FORMAT_CHANNEL8 = 10;
+
+ // Internal variable for checking validity of formats
+ // Change this if any more formats are added or removed
+ static final int FORMAT_TOTAL = 10;
+
+ /**
+ * Specifies that this ImageComponent object allows reading its
+ * size component information (width, height, and depth).
+ */
+ public static final int
+ ALLOW_SIZE_READ = CapabilityBits.IMAGE_COMPONENT_ALLOW_SIZE_READ;
+
+ /**
+ * Specifies that this ImageComponent object allows reading its
+ * format component information.
+ */
+ public static final int
+ ALLOW_FORMAT_READ = CapabilityBits.IMAGE_COMPONENT_ALLOW_FORMAT_READ;
+
+ /**
+ * Specifies that this ImageComponent object allows reading its
+ * image component information.
+ */
+ public static final int
+ ALLOW_IMAGE_READ = CapabilityBits.IMAGE_COMPONENT_ALLOW_IMAGE_READ;
+
+ /**
+ * Specifies that this ImageComponent object allows writing its
+ * image component information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_IMAGE_WRITE = CapabilityBits.IMAGE_COMPONENT_ALLOW_IMAGE_WRITE;
+
+ /**
+ * Not a public constructor, for internal use
+ */
+ ImageComponent() {
+ }
+
+ /**
+ * Constructs an image component object using the specified format, width,
+ * and height. Default values are used for all other parameters. The
+ * default values are as follows:
+ * <ul>
+ * byReference : false<br>
+ * yUp : false<br>
+ * </ul>
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA etc.
+ * @param width the number of columns of pixels in this image component
+ * object
+ * @param height the number of rows of pixels in this image component
+ * object
+ * @exception IllegalArgumentException if format is invalid, or if
+ * width or height are not positive.
+ */
+ public ImageComponent(int format, int width, int height) {
+ ((ImageComponentRetained)this.retained).processParams(format, width, height, 1);
+ }
+
+ /**
+ * Constructs an image component object using the specified format, width,
+ * height, byReference flag, and yUp flag.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA etc.
+ * @param width the number of columns of pixels in this image component
+ * object
+ * @param height the number of rows of pixels in this image component
+ * object
+ * @param byReference a flag that indicates whether the data is copied
+ * into this image component object or is accessed by reference.
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * width or height are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent(int format,
+ int width,
+ int height,
+ boolean byReference,
+ boolean yUp) {
+
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ ((ImageComponentRetained)this.retained).setByReference(byReference);
+ ((ImageComponentRetained)this.retained).processParams(format, width, height, 1);
+ }
+
+ /**
+ * Retrieves the width of this image component object.
+ * @return the width of this image component object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getWidth() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent0"));
+ return ((ImageComponentRetained)this.retained).getWidth();
+ }
+
+ /**
+ * Retrieves the height of this image component object.
+ * @return the height of this image component object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getHeight() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent1"));
+ return ((ImageComponentRetained)this.retained).getHeight();
+ }
+
+ /**
+ * Retrieves the format of this image component object.
+ * @return the format of this image component object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getFormat() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_FORMAT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent2"));
+ return ((ImageComponentRetained)this.retained).getFormat();
+ }
+
+
+ /**
+ * Retrieves the data access mode for this ImageComponent object.
+ *
+ * @return <code>true</code> if the data access mode for this
+ * ImageComponent object is by-reference;
+ * <code>false</code> if the data access mode is by-copying.
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean isByReference() {
+ return ((ImageComponentRetained)this.retained).isByReference();
+ }
+
+
+ /**
+ * Sets the y-orientation of this ImageComponent object to
+ * y-up or y-down.
+ *
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ *
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setYUp(boolean yUp) {
+ checkForLiveOrCompiled();
+
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ }
+
+
+ /**
+ * Retrieves the y-orientation for this ImageComponent object.
+ *
+ * @return <code>true</code> if the y-orientation of this
+ * ImageComponent object is y-up; <code>false</code> if the
+ * y-orientation of this ImageComponent object is y-down.
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean isYUp() {
+ return ((ImageComponentRetained)this.retained).isYUp();
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ImageComponent2D.java b/src/classes/share/javax/media/j3d/ImageComponent2D.java
new file mode 100644
index 0000000..5fdfa18
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ImageComponent2D.java
@@ -0,0 +1,534 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+
+/**
+ * This class defines a 2D image component. This is used for texture
+ * images, background images and raster components of Shape3D nodes.
+ * Prior to Java 3D 1.2, only BufferedImage objects could be used as the
+ * input to an ImageComponent2D object. As of Java 3D 1.2, an
+ * ImageComponent2D accepts any RenderedImage object (BufferedImage is
+ * an implementation of the RenderedImage interface). The methods
+ * that set/get a BufferedImage object are left in for compatibility.
+ * The new methods that set/get a RenderedImage are a superset of the
+ * old methods. In particular, the two set methods in the following
+ * example are equivalent:
+ *
+ * <p>
+ * <ul>
+ * <code>
+ * BufferedImage bi;<br>
+ * RenderedImage ri = bi;<br>
+ * ImageComponent2D ic;<br>
+ * <p>
+ * // Set the image to the specified BufferedImage<br>
+ * ic.set(bi);<br>
+ * <p>
+ * // Set the image to the specified RenderedImage<br>
+ * ic.set(ri);<br>
+ * </code>
+ * </ul>
+ *
+ */
+
+public class ImageComponent2D extends ImageComponent {
+
+ // non-public, no parameter constructor
+ ImageComponent2D() {}
+
+ /**
+ * Constructs a 2D image component object using the specified
+ * format, width, and height, and a null image.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param width the number of columns of pixels in this image component
+ * object
+ * @param height the number of rows of pixels in this image component
+ * object
+ * @exception IllegalArgumentException if format is invalid, or if
+ * width or height are not positive.
+ */
+ public ImageComponent2D(int format,
+ int width,
+ int height) {
+
+ ((ImageComponent2DRetained)this.retained).processParams(format, width, height, 1);
+ }
+
+ /**
+ * Constructs a 2D image component object using the specified format
+ * and BufferedImage. A copy of the BufferedImage is made.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param image the BufferedImage used to create this 2D image component.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the image are not positive.
+ */
+ public ImageComponent2D(int format, BufferedImage image) {
+
+ ((ImageComponent2DRetained)this.retained).processParams(format, image.getWidth(null), image.getHeight(null), 1);
+ ((ImageComponent2DRetained)this.retained).set(image);
+ }
+
+ /**
+ * Constructs a 2D image component object using the specified format
+ * and RenderedImage. A copy of the RenderedImage is made.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param image the RenderedImage used to create this 2D image component
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the image are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent2D(int format, RenderedImage image) {
+
+
+ ((ImageComponent2DRetained)this.retained).processParams(format, image.getWidth(), image.getHeight(), 1);
+ ((ImageComponent2DRetained)this.retained).set(image);
+ }
+
+ /**
+ * Constructs a 2D image component object using the specified
+ * format, width, height, byReference flag, and yUp flag, and
+ * a null image.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param width the number of columns of pixels in this image component
+ * object
+ * @param height the number of rows of pixels in this image component
+ * object
+ * @param byReference a flag that indicates whether the data is copied
+ * into this image component object or is accessed by reference.
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * width or height are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent2D(int format,
+ int width,
+ int height,
+ boolean byReference,
+ boolean yUp) {
+
+
+ ((ImageComponentRetained)this.retained).setByReference(byReference);
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ ((ImageComponent2DRetained)this.retained).processParams(format, width, height, 1);
+ }
+
+ /**
+ * Constructs a 2D image component object using the specified format,
+ * BufferedImage, byReference flag, and yUp flag.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param image the BufferedImage used to create this 2D image component
+ * @param byReference a flag that indicates whether the data is copied
+ * into this image component object or is accessed by reference
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the image are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent2D(int format,
+ BufferedImage image,
+ boolean byReference,
+ boolean yUp) {
+
+
+ ((ImageComponentRetained)this.retained).setByReference(byReference);
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ ((ImageComponent2DRetained)this.retained).processParams(format, image.getWidth(null), image.getHeight(null), 1);
+ ((ImageComponent2DRetained)this.retained).set(image);
+ }
+
+ /**
+ * Constructs a 2D image component object using the specified format,
+ * RenderedImage, byReference flag, and yUp flag.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param image the RenderedImage used to create this 2D image component
+ * @param byReference a flag that indicates whether the data is copied
+ * into this image component object or is accessed by reference.
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the image are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent2D(int format,
+ RenderedImage image,
+ boolean byReference,
+ boolean yUp) {
+
+
+ ((ImageComponentRetained)this.retained).setByReference(byReference);
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ ((ImageComponent2DRetained)this.retained).processParams(format, image.getWidth(), image.getHeight(), 1);
+ ((ImageComponent2DRetained)this.retained).set(image);
+ }
+
+ /**
+ * Sets this image component to the specified BufferedImage
+ * object. If the data access mode is not by-reference, then the
+ * BufferedImage data is copied into this object. If
+ * the data access mode is by-reference, then a reference to the
+ * BufferedImage is saved, but the data is not necessarily
+ * copied.
+ *
+ * @param image BufferedImage object containing the image.
+ * The format and size must be the same as the current format in this
+ * ImageComponent2D object.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ */
+ public void set(BufferedImage image) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("ImageComponent2D1"));
+ }
+
+ ((ImageComponent2DRetained)this.retained).set(image);
+ }
+
+ /**
+ * Sets this image component to the specified RenderedImage
+ * object. If the data access mode is not by-reference, the
+ * RenderedImage data is copied into this object. If
+ * the data access mode is by-reference, a reference to the
+ * RenderedImage is saved, but the data is not necessarily
+ * copied.
+ *
+ * @param image RenderedImage object containing the image.
+ * The format and size must be the same as the current format in this
+ * ImageComponent2D object.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void set(RenderedImage image) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("ImageComponent2D1"));
+ }
+
+ ((ImageComponent2DRetained)this.retained).set(image);
+ }
+
+ /**
+ * Retrieves the image from this ImageComponent2D object. If the
+ * data access mode is not by-reference, a copy of the image
+ * is made. If the data access mode is by-reference, the
+ * reference is returned.
+ *
+ * @return either a new BufferedImage object created from the data
+ * in this image component, or the BufferedImage object referenced
+ * by this image component.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data access mode is
+ * by-reference and the image referenced by this ImageComponent2D
+ * object is not an instance of BufferedImage.
+ */
+ public BufferedImage getImage() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ImageComponent.ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent2D0"));
+ }
+
+ RenderedImage img = ((ImageComponent2DRetained)this.retained).getImage();
+
+ if ((img != null) && !(img instanceof BufferedImage)) {
+ throw new IllegalStateException(J3dI18N.getString("ImageComponent2D5"));
+ }
+ return (BufferedImage) img;
+
+ }
+
+ /**
+ * Retrieves the image from this ImageComponent2D object. If the
+ * data access mode is not by-reference, a copy of the image
+ * is made. If the data access mode is by-reference, the
+ * reference is returned.
+ *
+ * @return either a new RenderedImage object created from the data
+ * in this image component, or the RenderedImage object referenced
+ * by this image component.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public RenderedImage getRenderedImage() {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ImageComponent.ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent2D0"));
+ return ((ImageComponent2DRetained)this.retained).getImage();
+ }
+
+
+ /**
+ * Modifies a contiguous subregion of the image component.
+ * Block of data of dimension (width * height)
+ * starting at the offset (srcX, srcY) of the specified
+ * RenderedImage object will be copied into the image component
+ * starting at the offset (dstX, dstY) of the ImageComponent2D object.
+ * The RenderedImage object must be of the same format as the current
+ * format of this object.
+ * This method can only be used if the data access mode is
+ * by-copy. If it is by-reference, see updateData().
+ *
+ * @param image RenderedImage object containing the subimage.
+ * @param width width of the subregion.
+ * @param height height of the subregion.
+ * @param srcX starting X offset of the subregion in the
+ * specified image.
+ * @param srcY starting Y offset of the subregion in the
+ * specified image.
+ * @param dstX starting X offset of the subregion in the image
+ * component of this object.
+ * @param dstY starting Y offset of the subregion in the image
+ * component of this object.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalStateException if the data access mode is
+ * <code>BY_REFERENCE</code>.
+ * @exception IllegalArgumentException if <code>width</code> or
+ * <code>height</code> of
+ * the subregion exceeds the dimension of the image of this object.
+ * @exception IllegalArgumentException if <code>dstX</code> < 0, or
+ * (<code>dstX</code> + <code>width</code>) > width of this object, or
+ * <code>dstY</code> < 0, or
+ * (<code>dstY</code> + <code>height</code>) > height of this object.
+ * @exception IllegalArgumentException if <code>srcX</code> < 0, or
+ * (<code>srcX</code> + <code>width</code>) > width of the RenderedImage
+ * object containing the subimage, or
+ * <code>srcY</code> < 0, or
+ * (<code>srcY</code> + <code>height</code>) > height of the
+ * RenderedImage object containing the subimage.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setSubImage(RenderedImage image, int width, int height,
+ int srcX, int srcY, int dstX, int dstY) {
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_IMAGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("ImageComponent2D1"));
+ }
+
+ if (((ImageComponent2DRetained)this.retained).isByReference()) {
+ throw new IllegalStateException(
+ J3dI18N.getString("ImageComponent2D4"));
+ }
+
+ int w = ((ImageComponent2DRetained)this.retained).getWidth();
+ int h = ((ImageComponent2DRetained)this.retained).getHeight();
+
+ if ((srcX < 0) || (srcY < 0) ||
+ ((srcX + width) > w) || ((srcY + height) > h) ||
+ (dstX < 0) || (dstY < 0) ||
+ ((dstX + width) > w) || ((dstY + height) > h)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("ImageComponent2D3"));
+ }
+
+ ((ImageComponent2DRetained)this.retained).setSubImage(
+ image, width, height, srcX, srcY, dstX, dstY);
+ }
+
+ /**
+ * Updates image data that is accessed by reference.
+ * This method calls the updateData method of the specified
+ * ImageComponent2D.Updater object to synchronize updates to the
+ * image data that is referenced by this ImageComponent2D object.
+ * Applications that wish to modify such data must perform all
+ * updates via this method.
+ * <p>
+ * The data to be modified has to be within the boundary of the
+ * subregion
+ * specified by the offset (x, y) and the dimension (width*height).
+ * It is illegal to modify data outside this boundary.
+ * If any referenced data is modified outisde the updateData
+ * method, or any data outside the specified boundary is modified,
+ * the results are undefined.
+ * <p>
+ * @param updater object whose updateData callback method will be
+ * called to update the data referenced by this ImageComponent2D object.
+ * @param x starting X offset of the subregion.
+ * @param y starting Y offset of the subregion.
+ * @param width width of the subregion.
+ * @param height height of the subregion.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability
+ * is not set, and this object is part of a live or compiled scene graph
+ * @exception IllegalStateException if the data access mode is
+ * <code>BY_COPY</code>.
+ * @exception IllegalArgumentException if <code>width</code> or
+ * <code>height</code> of
+ * the subregion exceeds the dimension of the image of this object.
+ * @exception IllegalArgumentException if <code>x</code> < 0, or
+ * (<code>x</code> + <code>width</code>) > width of this object, or
+ * <code>y</code> < 0, or
+ * (<code>y</code> + <code>height</code>) > height of this object.
+ *
+ * @since Java 3D 1.3
+ */
+ public void updateData(Updater updater,
+ int x, int y,
+ int width, int height) {
+
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_IMAGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("ImageComponent2D1"));
+ }
+
+ if (!((ImageComponent2DRetained)this.retained).isByReference()) {
+ throw new IllegalStateException(
+ J3dI18N.getString("ImageComponent2D2"));
+ }
+
+ int w = ((ImageComponent2DRetained)this.retained).getWidth();
+ int h = ((ImageComponent2DRetained)this.retained).getHeight();
+
+ if ((x < 0) || (y < 0) || ((x + width) > w) || ((y + height) > h)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("ImageComponent2D3"));
+ }
+
+ ((ImageComponent2DRetained)this.retained).updateData(
+ updater, x, y, width, height);
+ }
+
+ /**
+ * Creates a retained mode ImageComponent2DRetained object that this
+ * ImageComponent2D component object will point to.
+ */
+ void createRetained() {
+ this.retained = new ImageComponent2DRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ ImageComponent2DRetained rt = (ImageComponent2DRetained) retained;
+
+ ImageComponent2D img = new ImageComponent2D(rt.format,
+ rt.width,
+ rt.height,
+ rt.byReference,
+ rt.yUp);
+ img.duplicateNodeComponent(this);
+ return img;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code>
+ * into the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ RenderedImage img = ((ImageComponent2DRetained)
+ originalNodeComponent.retained).getImage();
+
+ if (img != null) {
+ ((ImageComponent2DRetained) retained).set(img);
+ }
+ }
+
+ /**
+ * The ImageComponent2D.Updater interface is used in updating image data
+ * that is accessed by reference from a live or compiled ImageComponent
+ * object. Applications that wish to modify such data must define a
+ * class that implements this interface. An instance of that class is
+ * then passed to the <code>updateData</code> method of the
+ * ImageComponent object to be modified.
+ *
+ * @since Java 3D 1.3
+ */
+ public static interface Updater {
+ /**
+ * Updates image data that is accessed by reference.
+ * This method is called by the updateData method of an
+ * ImageComponent object to effect
+ * safe updates to image data that
+ * is referenced by that object. Applications that wish to modify
+ * such data must implement this method and perform all updates
+ * within it.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ *
+ * @param imageComponent the ImageComponent object being updated.
+ * @param x starting X offset of the subregion.
+ * @param y starting Y offset of the subregion.
+ * @param width width of the subregion.
+ * @param height height of the subregion.
+ *
+ * @see ImageComponent2D#updateData
+ */
+ public void updateData(ImageComponent2D imageComponent,
+ int x, int y,
+ int width, int height);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ImageComponent2DRetained.java b/src/classes/share/javax/media/j3d/ImageComponent2DRetained.java
new file mode 100644
index 0000000..b390e67
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ImageComponent2DRetained.java
@@ -0,0 +1,1384 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.image.*;
+import java.awt.color.ColorSpace;
+
+/**
+ * This class defines a 2D image component.
+ * This is used for texture images, background images and raster components
+ * of Shape3D nodes.
+ */
+
+class ImageComponent2DRetained extends ImageComponentRetained {
+ private int rasterRefCnt = 0; // number of raster using this object
+ private int textureRefCnt = 0; // number of texture using this object
+
+ private DetailTextureImage detailTexture = null; // will reference a
+ // DetailTexture object if
+ // this image is being
+ // referenced as a detail image
+
+ // use in D3D to map object to surface pointer
+ int hashId;
+ native void freeD3DSurface(int hashId);
+
+ float[] lastAlpha = new float[1];
+
+ static final double EPSILON = 1.0e-6;
+
+ // dirty mask to track if the image has been changed since the last
+ // alpha update. The nth bit in the mask represents the dirty bit
+ // of the image for screen nth. If nth bit is set, then the image
+ // needs to be updated with the current alpha values.
+ int imageChanged = 0;
+
+ ImageComponent2DRetained() {
+ hashId = hashCode();
+ }
+
+ /**
+ * Copies the specified BufferedImage to this 2D image component object.
+ * @param image BufferedImage object containing the image.
+ * The format and size must be the same as the current format in this
+ * ImageComponent2D object.
+ */
+ final void set(BufferedImage image) {
+ int width = image.getWidth(null);
+ int height = image.getHeight(null);
+
+ if (width != this.width)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2DRetained0"));
+
+ if (height != this.height)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2DRetained1"));
+
+ int imageBytes;
+ // Note, currently only EXT_ABGR and EXT_BGR are not copied, if
+ // its going to be copied, do it at set time (at this time, the
+ // renderer is running in parallel), if we delay it until
+ // renderBin:updateObject time (when it calls evaluateExtension),
+ // we are stalling the renderer
+ geomLock.getLock();
+ if (!byReference || (byReference && willBeCopied(image))) {
+ imageBytes = height * width * bytesPerPixelIfStored;
+ if (usedByTexture || ! usedByRaster) {
+ // ==> (usedByTexture) || (! usedByTexture && ! usedByRaster)
+
+ if (imageYup == null || imageYup.length < imageBytes) {
+ imageYup = new byte[imageBytes];
+ imageYupAllocated = true;
+ }
+
+ // buffered image -> imageYup
+ storedYupFormat = internalFormat;
+ bytesPerYupPixelStored = getBytesStored(storedYupFormat);
+ copyImage(image, imageYup, true, 0, storedYupFormat,
+ bytesPerYupPixelStored);
+ imageYupClass = BUFFERED_IMAGE;
+ }
+
+ if (usedByRaster) {
+ imageYdownClass = BUFFERED_IMAGE;
+ storedYdownFormat = internalFormat;
+ bytesPerYdownPixelStored = getBytesStored(storedYdownFormat);
+
+ if (imageYdown[0] == null || imageYdown[0].length < imageBytes) {
+ imageYdown[0] = new byte[imageBytes];
+ imageYdownAllocated = true;
+ }
+
+ if (imageYup != null){
+ //imageYup -> imageYdown
+ setImageYdown(imageYup, imageYdown[0]);
+ } else {
+ // buffered image -> imageYdown
+ copyImage(image, imageYdown[0], false, 0,
+ storedYdownFormat, bytesPerYdownPixelStored);
+ }
+ }
+ // If its byRef case, but we copied because we know that
+ // the underlying native API cannot support this case!
+ if (byReference) {
+ bImage[0] = image;
+ if (usedByTexture || !usedByRaster)
+ imageYupCacheDirty = false;
+ else
+ imageYupCacheDirty = true;
+
+ if (usedByRaster)
+ imageYdownCacheDirty = false;
+ else
+ imageYdownCacheDirty = true;
+
+ }
+ else {
+ imageDirty[0] = true;
+ }
+
+ }
+ // If its by reference, then make a copy only if necessary
+ else {
+ imageYupCacheDirty = true;
+ imageYdownCacheDirty = true;
+ bImage[0] = image;
+ }
+ imageChanged = 0xffff;
+ lastAlpha[0] = 1.0f;
+ geomLock.unLock();
+
+ if (source.isLive()) {
+ freeSurface();
+
+ // send a IMAGE_CHANGED message in order to
+ // notify all the users of the change
+ sendMessage(IMAGE_CHANGED, null);
+ }
+ }
+
+
+ boolean willBeCopied(RenderedImage image) {
+ return shouldImageBeCopied(getImageType(image),
+ (Canvas3D.EXT_ABGR|Canvas3D.EXT_BGR), image);
+ }
+
+
+
+ // NOTE, IMPORTANT: any additions to the biType tested , should be added to
+ // the willBeCopied() function
+ final boolean shouldImageBeCopied(int biType, int ext, RenderedImage ri) {
+
+ if (!byReference)
+ return true;
+
+ if ((((ext & Canvas3D.EXT_ABGR) != 0) &&
+ ((biType == BufferedImage.TYPE_4BYTE_ABGR) &&
+ (format == ImageComponent.FORMAT_RGBA8))) ||
+ (((ext & Canvas3D.EXT_BGR) != 0) &&
+ ((biType == BufferedImage.TYPE_3BYTE_BGR) &&
+ (format == ImageComponent.FORMAT_RGB))) ||
+ ((biType == BufferedImage.TYPE_BYTE_GRAY) &&
+ (format == ImageComponent.FORMAT_CHANNEL8)) ||
+ (is4ByteRGBAOr3ByteRGB(ri))) {
+ /* ||TODO: Don't do short for now!
+ ((biType == BufferedImage.TYPE_USHORT_GRAY) &&
+ (format == ImageComponent.FORMAT_CHANNEL8)
+ */
+
+ return false;
+ }
+ return true;
+ }
+
+ final int getStoredFormat(int biType, RenderedImage ri) {
+ int f = 0;
+ switch(biType) {
+ case BufferedImage.TYPE_4BYTE_ABGR:
+ f= BYTE_ABGR;
+ break;
+ case BufferedImage.TYPE_BYTE_GRAY:
+ f= BYTE_GRAY;
+ break;
+ case BufferedImage.TYPE_USHORT_GRAY:
+ f = USHORT_GRAY;
+ break;
+ case BufferedImage.TYPE_3BYTE_BGR:
+ f = BYTE_BGR;
+ break;
+ case BufferedImage.TYPE_CUSTOM:
+ if (is4ByteRGBAOr3ByteRGB(ri)) {
+ SampleModel sm = ri.getSampleModel();
+ if (sm.getNumBands() == 3) {
+ f = BYTE_RGB;
+ }
+ else {
+ f = BYTE_RGBA;
+ }
+ }
+ break;
+ default:
+ // Should never come here
+ }
+ return f;
+ }
+
+ final void set(RenderedImage image) {
+
+ if (image instanceof BufferedImage) {
+ set(((BufferedImage)image));
+ }
+ else {
+ /*
+ // Create a buffered image from renderImage
+ ColorModel cm = image.getColorModel();
+ WritableRaster wRaster = image.copyData(null);
+ BufferedImage bi = new BufferedImage(cm,
+ wRaster,
+ cm.isAlphaPremultiplied()
+ ,null);
+ set(bi);
+ }
+ */
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ if (width != this.width)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2DRetained0"));
+
+ if (height != this.height)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2DRetained1"));
+
+ int imageBytes;
+ // Note, currently only EXT_ABGR and EXT_BGR are not copied, if
+ // its going to be copied, do it at set time (at this time, the
+ // renderer is running in parallel), if we delay it until
+ // renderBin:updateObject time (when it calls evaluateExtension),
+ // we are stalling the renderer
+ geomLock.getLock();
+ if (!byReference ||(byReference && willBeCopied(image))) {
+ imageBytes = height * width * bytesPerPixelIfStored;
+ if (usedByTexture || ! usedByRaster) {
+ if (imageYup == null || imageYup.length < imageBytes) {
+ imageYup = new byte[imageBytes];
+ imageYupAllocated = true;
+ }
+
+ // buffered image -> imageYup
+ storedYupFormat = internalFormat;
+ bytesPerYupPixelStored = getBytesStored(storedYupFormat);
+ copyImage(image, imageYup, true, 0, storedYupFormat,
+ bytesPerYupPixelStored);
+ imageYupClass = BUFFERED_IMAGE;
+ }
+
+ if (usedByRaster) {
+
+ imageYdownClass = BUFFERED_IMAGE;
+ storedYdownFormat = internalFormat;
+ bytesPerYdownPixelStored = getBytesStored(storedYdownFormat);
+
+ if (imageYdown[0] == null || imageYdown[0].length < imageBytes) {
+ imageYdown[0] = new byte[imageBytes];
+ imageYdownAllocated = true;
+ }
+
+ if (imageYup != null)
+ //imageYup -> imageYdown
+ setImageYdown(imageYup, imageYdown[0]);
+ else
+ // buffered image -> imageYdown
+ copyImage(image, imageYdown[0], false, 0,
+ storedYdownFormat, bytesPerYdownPixelStored);
+ }
+ if (byReference) {
+ bImage[0] = image;
+ if (usedByTexture || !usedByRaster)
+ imageYupCacheDirty = false;
+ else
+ imageYupCacheDirty = true;
+
+ if (usedByRaster)
+ imageYdownCacheDirty = false;
+ else
+ imageYdownCacheDirty = true;
+ }
+ else {
+ imageDirty[0] = true;
+ }
+ }
+ // If its by reference, then make a copy only if necessary
+ else {
+ imageYupCacheDirty = true;
+ imageYdownCacheDirty = true;
+ bImage[0] = image;
+ }
+
+ }
+ imageChanged = 0xffff;
+ lastAlpha[0] = 1.0f;
+ geomLock.unLock();
+ if (source.isLive()) {
+ freeSurface();
+ sendMessage(IMAGE_CHANGED, null);
+ }
+ }
+
+ /**
+ * Retrieves a copy of the image in this ImageComponent2D object.
+ * @return a new BufferedImage object created from the image in this
+ * ImageComponent2D object
+ */
+ final RenderedImage getImage() {
+ if (!byReference && imageDirty[0]) {
+ imageDirty[0] = false;
+ retrieveBufferedImage(0);
+ }
+ return bImage[0];
+ }
+
+ // allocate storage for imageYdown
+ // set imageYdown and free imageYup if necessary
+ final void setRasterRef() {
+ // Ref case will be handled by evaluateExtension();
+ if (usedByRaster)
+ return;
+
+ usedByRaster = true;
+
+ if (format == ImageComponent.FORMAT_CHANNEL8)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2DRetained2"));
+
+ if (!byReference) {
+ if (imageYdown[0] == null && imageYup != null) {
+ imageYdown[0] = new byte[height * width * bytesPerYupPixelStored];
+ imageYdownAllocated = true;
+
+ // imageYup -> imageYdown
+ imageYdownClass = BUFFERED_IMAGE;
+ storedYdownFormat = storedYupFormat;
+ bytesPerYdownPixelStored = bytesPerYupPixelStored;
+ setImageYdown(imageYup, imageYdown[0]);
+ }
+ if (usedByTexture == false) {
+ imageYup = null;
+ imageYupAllocated = false;
+ }
+
+ }
+ else {
+ if (willBeCopied(bImage[0])) {
+ geomLock.getLock();
+ if (imageYdownCacheDirty) {
+ if (imageYdown[0] == null) {
+
+ if (imageYup != null) {
+ storedYdownFormat = storedYupFormat;
+ bytesPerYdownPixelStored = bytesPerYupPixelStored;
+ imageYdown[0] =new byte[height*width *bytesPerYdownPixelStored];
+ setImageYdown(imageYup, imageYdown[0]);
+ }
+ else {
+ imageYdown[0] = new byte[height * width * bytesPerPixelIfStored];
+ bytesPerYdownPixelStored = bytesPerPixelIfStored;
+ storedYdownFormat = internalFormat;
+
+ if (bImage[0] instanceof BufferedImage) {
+ copyImage(((BufferedImage)bImage[0]),
+ imageYdown[0], false, 0,
+ storedYdownFormat,
+ bytesPerYdownPixelStored);
+ }
+ else {
+ copyImage(bImage[0], imageYdown[0], false, 0,
+ storedYdownFormat,
+ bytesPerYdownPixelStored);
+ }
+ }
+ imageYdownClass = BUFFERED_IMAGE;
+ imageYdownAllocated = true;
+ }
+ imageYdownCacheDirty = false;
+ }
+ geomLock.unLock();
+ }
+ else {
+ geomLock.getLock();
+ imageYdownCacheDirty = true;
+ geomLock.unLock();
+ }
+ /*
+ // Can't do this - since I don't know which extension
+ // will be supported, if Ydown is going away then
+ // this code will be useful
+ else if (yUp) {
+ geomLock.getLock();
+ if (imageYdownCacheDirty) {
+ storeRasterImageWithFlip(bImage[0]);
+ imageYdownCacheDirty = false;
+ }
+ geomLock.unLock();
+ }
+ */
+ }
+ }
+
+ // allocate storage for imageYup
+ // set imageYup and free imageYdown if necessary
+ final void setTextureRef() {
+ // Ref case will be handled by evaluateExtension();
+ if (usedByTexture)
+ return;
+
+ usedByTexture = true;
+
+ if (!byReference) {
+
+ if (imageYup == null && imageYdown[0] != null) {
+ storedYupFormat = storedYdownFormat;
+ bytesPerYupPixelStored = bytesPerYdownPixelStored;
+ imageYup = new byte[height * width * bytesPerYupPixelStored];
+ // imageYdown -> imageYup
+ setImageYup(imageYdown[0], imageYup);
+ imageYupClass = BUFFERED_IMAGE;
+ imageYupAllocated = true;
+ }
+ if (usedByRaster == false) {
+ imageYdown[0] = null;
+ imageYdownAllocated = false;
+ }
+
+ }
+ // If the image will not be stored by reference, because
+ // the format is not supported by the underlying API
+ else {
+ if (willBeCopied(bImage[0])) {
+ geomLock.getLock();
+ if (imageYupCacheDirty) {
+ if (imageYup == null) {
+ if (imageYdown[0] != null) {
+ // imageYdown -> imageYup
+ storedYupFormat = storedYdownFormat;
+ bytesPerYupPixelStored = bytesPerYdownPixelStored;
+ imageYup = new byte[height * width * bytesPerYupPixelStored];
+ setImageYup(imageYdown[0], imageYup);
+ }
+ else {
+ imageYup = new byte[height * width * bytesPerPixelIfStored];
+ bytesPerYupPixelStored = bytesPerPixelIfStored;
+ storedYupFormat = internalFormat;
+
+ if (bImage[0] instanceof BufferedImage) {
+ copyImage(((BufferedImage)bImage[0]), imageYup,
+ true, 0, storedYupFormat,
+ bytesPerYupPixelStored);
+ }
+ else {
+ copyImage(bImage[0], imageYup, true, 0,
+ storedYupFormat,
+ bytesPerYupPixelStored);
+ }
+ }
+ imageYupClass = BUFFERED_IMAGE;
+ imageYupAllocated = true;
+ }
+ imageYupCacheDirty = false;
+ }
+ geomLock.unLock();
+ }
+ else {
+ geomLock.getLock();
+ imageYupCacheDirty = true;
+ geomLock.unLock();
+ }
+ /*
+ // Can't do this - since I don't know which extension
+ // will be supported, if Ydown is going away then
+ // this code will be useful
+
+ // If Image will not be stored by reference because
+ // of wrong orienetation
+ else if (!yUp) {
+ geomLock.getLock();
+ if (imageYupCacheDirty) {
+ storeTextureImageWithFlip(bImage[0]);
+ imageYupCacheDirty = false;
+ }
+ geomLock.unLock();
+ }
+ */
+ }
+
+ }
+
+
+ // copy imageYup to imageYdown in reverse scanline order
+ final void setImageYdown(byte[] src, byte[] dst) {
+ int scanLineSize = width * bytesPerYdownPixelStored;
+ int srcOffset, dstOffset, i;
+
+ for (srcOffset = (height - 1) * scanLineSize, dstOffset = 0,
+ i = 0; i < height; i++,
+ srcOffset -= scanLineSize, dstOffset += scanLineSize) {
+
+ System.arraycopy(src, srcOffset, dst, dstOffset,
+ scanLineSize);
+ }
+
+ }
+
+ // Preserve the incoming format of thre renderImage, but maybe
+ // flip the image or make a plain copy
+ // Used only for raster
+ final void copyRImage(RenderedImage image, byte[] dst, boolean flip,
+ int bPerPixel) {
+
+ int numX = image.getNumXTiles();
+ int numY = image.getNumYTiles();
+ int tilew = image.getTileWidth();
+ int tileh = image.getTileHeight();
+ int i, j, h;
+ java.awt.image.Raster ras;
+ int tileLineBytes = tilew * bPerPixel;
+ int x = image.getMinTileX();
+ int y = image.getMinTileY();
+ int srcOffset, dstOffset;
+ int dstLineBytes = 0;
+ int tileStart;
+ int tileBytes = tileh * tilew * numX * bPerPixel;
+ int dstStart;
+ int tmph, tmpw, curw, curh;
+ int rowOffset, colOffset;
+ int sign;
+
+ if (flip) {
+ dstLineBytes =width * bPerPixel;
+ tileStart = (height - 1) * dstLineBytes;
+ dstLineBytes = -(dstLineBytes);
+ }
+ else {
+ tileStart = 0;
+ dstLineBytes = width * bPerPixel;
+ }
+ // convert from Ydown to Yup for texture
+ int minX = image.getMinX();
+ int minY = image.getMinY();
+ int xoff = image.getTileGridXOffset();
+ int yoff = image.getTileGridYOffset();
+ int endXTile = x * tilew + xoff+tilew;
+ int endYTile = y * tileh + yoff+tileh;
+ tmpw = width;
+ tmph = height;
+ // Check if the width is less than the tile itself ..
+ curw = (endXTile - minX);
+ curh = (endYTile - minY);
+
+ if (tmpw < curw) {
+ curw = tmpw;
+ }
+
+ if (tmph < curh) {
+ curh = tmph;
+ }
+ int startw = curw ;
+
+ rowOffset = (tilew - curw) * bPerPixel;
+ colOffset = tilew * (tileh - curh) * bPerPixel;
+ int bytesCopied = 0;
+ srcOffset = rowOffset + colOffset;
+
+
+ for (i = y; i < y+numY; i++) {
+ dstStart = tileStart;
+ curw = startw;
+ tmpw = width;
+ for (j = x; j < x+numX; j++) {
+ ras = image.getTile(j,i);
+ byte[] src = ((DataBufferByte)ras.getDataBuffer()).getData();
+ dstOffset = dstStart;
+ bytesCopied = curw * bPerPixel;
+ for (h = 0;h < curh; h++) {
+ System.arraycopy(src, srcOffset, dst, dstOffset,
+ bytesCopied);
+ srcOffset += tileLineBytes;
+ dstOffset += dstLineBytes;
+ }
+ srcOffset = colOffset;
+ dstStart += curw * bPerPixel;
+ tmpw -= curw;
+ if (tmpw < tilew)
+ curw = tmpw;
+ else
+ curw = tilew;
+ }
+ srcOffset = rowOffset;
+ colOffset = 0;
+ tileStart += curh * dstLineBytes;
+ tmph -= curh;
+ if (tmph < tileh)
+ curh = tmph;
+ else
+ curh = tileh;
+ }
+ }
+
+ // copy imageYdown to imageYup in reverse scanline order
+ final void setImageYup(byte[] src, byte[] dst) {
+ int scanLineSize = width * bytesPerYupPixelStored;
+ int srcOffset, dstOffset, i;
+
+ for (srcOffset = 0, dstOffset = (height - 1) * scanLineSize,
+ i = 0; i < height; i++,
+ srcOffset += scanLineSize, dstOffset -= scanLineSize) {
+
+ System.arraycopy(src, srcOffset, dst, dstOffset,
+ scanLineSize);
+ }
+ }
+
+ // Lock out user thread from modifying usedByRaster and
+ // usedByTexture variables by using synchronized routines
+ final void evaluateExtensions(int ext) {
+ int i;
+ int imageBytes;
+ RenderedImage image = bImage[0];
+
+ // System.out.println("!!!!!!!!!!!!!imageYupCacheDirty = "+imageYupCacheDirty);
+ // System.out.println("!!!!!!!!!!!!!imageYdownCacheDirty = "+imageYdownCacheDirty);
+ // System.out.println("!!!!!!!!!!!!!usedByTexture = "+usedByTexture);
+ // System.out.println("!!!!!!!!!!!!!usedByRaster = "+usedByRaster);
+
+
+ if (!imageYupCacheDirty && !imageYdownCacheDirty) {
+ return;
+ }
+
+ int riType = getImageType(image);
+
+ // Thread.dumpStack();
+ if (usedByTexture == true || ! usedByRaster) {
+ // If the image is already allocated, then return
+ // nothing to do!
+ // Since this is a new image, the app may have changed the image
+ // when it was not live, so re-compute, until the image is allocated
+ // for this pass!
+ // System.out.println("!!!!!!!!!!!!!imageYupCacheDirty = "+imageYupCacheDirty);
+ if (!imageYupCacheDirty) {
+ evaluateRaster(riType, ext);
+ return;
+ }
+ if (shouldImageBeCopied(riType, ext, image)) {
+
+ imageBytes = height * width * bytesPerPixelIfStored;
+ if (imageYup == null || !imageYupAllocated) {
+ imageYup = new byte[imageBytes];
+ imageYupAllocated = true;
+ }
+ // buffered image -> imageYup
+ bytesPerYupPixelStored = bytesPerPixelIfStored;
+ storedYupFormat = internalFormat;
+ copyImage(image, imageYup, true, 0,
+ storedYupFormat, bytesPerYupPixelStored);
+ imageYupClass = BUFFERED_IMAGE;
+ imageYupCacheDirty = false;
+ }
+ else {
+ // This image is already valid ..
+ if (!imageYupCacheDirty) {
+ evaluateRaster(riType, ext);
+ return;
+ }
+ storedYupFormat = getStoredFormat(riType, image);
+ bytesPerYupPixelStored = getBytesStored(storedYupFormat);
+
+ // It does not have to be copied, but we
+ // have to copy because the incoming image is
+ // ydown
+ if (!yUp) {
+ storeTextureImageWithFlip(image);
+ }
+ else {
+ if (image instanceof BufferedImage) {
+ byte[] tmpImage = ((DataBufferByte)((BufferedImage)image).getRaster().getDataBuffer()).getData();
+ imageYup = tmpImage;
+ imageYupAllocated = false;
+ imageYupClass = BUFFERED_IMAGE;
+ }
+ else {
+ numXTiles = image.getNumXTiles();
+ numYTiles = image.getNumYTiles();
+ tilew = image.getTileWidth();
+ tileh = image.getTileHeight();
+ minTileX = image.getMinTileX();
+ minTileY = image.getMinTileY();
+ minX = image.getMinX();
+ minY = image.getMinY();
+ tileGridXOffset =image.getTileGridXOffset();
+ tileGridYOffset = image.getTileGridYOffset();
+ imageYupAllocated = false;
+ imageYupClass = RENDERED_IMAGE;
+ imageYup = null;
+ }
+ }
+
+ }
+ if (usedByRaster == false) {
+ imageYdown[0] = null;
+ imageYdownAllocated = false;
+ }
+ }
+ evaluateRaster(riType, ext);
+ }
+
+ void evaluateRaster(int riType, int ext) {
+ int i;
+ int imageBytes;
+ RenderedImage image = bImage[0];
+
+ if (usedByRaster) {
+ // If the image is already allocated, then return
+ // nothing to do!
+ if (!imageYdownCacheDirty) {
+ return;
+ }
+ if (shouldImageBeCopied(riType, ext, image)) {
+ // System.out.println("Raster Image is copied");
+ imageBytes = height * width * bytesPerPixelIfStored;
+ if (imageYdown[0] == null || !imageYdownAllocated || imageYdown[0].length < imageBytes){
+ imageYdown[0] = new byte[imageBytes];
+ imageYdownAllocated = true;
+ }
+ if (imageYup != null) {
+ storedYdownFormat = storedYupFormat;
+ bytesPerYdownPixelStored = bytesPerYupPixelStored;
+ setImageYdown(imageYup, imageYdown[0]);
+ }
+ else {
+ // buffered image -> imageYup
+ storedYdownFormat = internalFormat;
+ bytesPerYdownPixelStored = bytesPerPixelIfStored;
+ copyImage(image, imageYdown[0], false, 0,
+ storedYdownFormat, bytesPerYdownPixelStored);
+ }
+ imageYdownCacheDirty = false;
+ imageYdownClass = BUFFERED_IMAGE;
+ }
+ else {
+ // This image is already valid ..
+ if (!imageYdownCacheDirty) {
+ return;
+ }
+ storedYdownFormat = getStoredFormat(riType, image);
+ bytesPerYdownPixelStored = getBytesStored(storedYdownFormat);
+ if (yUp) {
+ storeRasterImageWithFlip(image);
+ }
+ else {
+ if (image instanceof BufferedImage) {
+ byte[] tmpImage = ((DataBufferByte)((BufferedImage)image).getRaster().getDataBuffer()).getData();
+ imageYdown[0] = tmpImage;
+ imageYdownAllocated = false;
+ imageYdownClass = BUFFERED_IMAGE;
+ // System.out.println("Raster Image is stored by ref");
+ }
+ else {
+ // Right now, always copy since opengl rasterpos is
+ // too restrictive
+ imageBytes = width*height*bytesPerYdownPixelStored;
+ if (imageYdown[0] == null || !imageYdownAllocated ||imageYdown[0].length < imageBytes){
+ imageYdown[0] = new byte[imageBytes];
+ imageYdownAllocated = true;
+ }
+ imageYdownClass = BUFFERED_IMAGE;
+ copyRImage(image,imageYdown[0], false, bytesPerYdownPixelStored);
+ // System.out.println("Copying by ref RImage");
+
+ /*
+ numXTiles = image.getNumXTiles();
+ numYTiles = image.getNumYTiles();
+ tilew = image.getTileWidth();
+ tileh = image.getTileHeight();
+ minTileX = image.getMinTileX();
+ minTileY = image.getMinTileY();
+ imageYdownAllocated = false;
+ imageYdownClass = RENDERED_IMAGE;
+ imageYdown = null;
+ */
+ }
+ imageYdownCacheDirty = false;
+ }
+
+
+ }
+ if (usedByTexture == false) {
+ imageYup = null;
+ imageYupAllocated = false;
+ }
+ }
+ }
+
+ void storeRasterImageWithFlip(RenderedImage image) {
+ int imageBytes;
+
+ if (image instanceof BufferedImage) {
+ imageBytes = width*height*bytesPerYdownPixelStored;
+ if (imageYdown[0] == null || !imageYdownAllocated ||imageYdown[0].length < imageBytes){
+ imageYdown[0] = new byte[imageBytes];
+ imageYdownAllocated = true;
+ }
+ imageYdownClass = BUFFERED_IMAGE;
+ imageYdownCacheDirty = false;
+ byte[] tmpImage = ((DataBufferByte)((BufferedImage)image).getRaster().getDataBuffer()).getData();
+ setImageYdown(tmpImage, imageYdown[0]);
+ }
+ else {
+ // Right now, always copy since opengl rasterpos is
+ // too restrictive
+
+ imageBytes = width*height*bytesPerYdownPixelStored;
+ if (imageYdown[0] == null || !imageYdownAllocated ||imageYdown[0].length < imageBytes){
+ imageYdown[0] = new byte[imageBytes];
+ imageYdownAllocated = true;
+ }
+ imageYdownClass = BUFFERED_IMAGE;
+ imageYdownCacheDirty = false;
+ copyRImage(image, imageYdown[0], true, bytesPerYdownPixelStored);
+ }
+ }
+
+ void storeTextureImageWithFlip(RenderedImage image) {
+ int imageBytes;
+
+ if (image instanceof BufferedImage) {
+ byte[] tmpImage = ((DataBufferByte)((BufferedImage)image).getRaster().getDataBuffer()).getData();
+
+ if (imageYup == null || !imageYupAllocated) {
+ imageBytes = width*height*bytesPerYupPixelStored;
+ imageYup = new byte[imageBytes];
+ imageYupAllocated = true;
+ }
+ imageYupClass = BUFFERED_IMAGE;
+ setImageYup(tmpImage, imageYup);
+ imageYupCacheDirty = false;
+
+ }
+ else {
+ if (imageYup == null || !imageYupAllocated) {
+ imageBytes = width*height*bytesPerYupPixelStored;
+ imageYup = new byte[imageBytes];
+ imageYupAllocated = true;
+ }
+ imageYupClass = BUFFERED_IMAGE;
+ copyRImage(image, imageYup, true, bytesPerYupPixelStored);
+ imageYupCacheDirty = false;
+ }
+ }
+
+ void setLive(boolean inBackgroundGroup, int refCount) {
+ super.setLive(inBackgroundGroup, refCount);
+ }
+
+ void clearLive(int refCount) {
+ super.clearLive(refCount);
+ if (this.refCount <= 0) {
+ freeSurface();
+ }
+ }
+
+ void freeSurface() {
+ if (VirtualUniverse.mc.isD3D()) {
+ freeD3DSurface(hashId);
+ }
+ }
+
+ protected void finalize() {
+ // For Pure immediate mode, there is no clearLive so
+ // surface will free when JVM do GC
+ freeSurface();
+ }
+ void updateAlpha(Canvas3D cv, int screen, float alpha) {
+ // if alpha is smaller than EPSILON, set it to EPSILON, so that
+ // even if alpha is equal to 0, we will not completely lose
+ int i, j;
+ byte byteAlpha;
+ float rndoff = 0.0f;
+
+ // the original alpha value
+ if (alpha <= EPSILON) {
+ alpha = (float)EPSILON;
+ }
+ // System.out.println("========> updateAlpha, this = "+this);
+ // Lock out the other renderers ..
+ synchronized (this) {
+ // If by reference, the image has been copied, but aset has occured
+ // or if the format is not RGBA, then copy
+ // Thread.dumpStack();
+ if (isByReference() && ((storedYdownFormat != internalFormat) || ((imageChanged & 1) != 0))) {
+ int imageBytes = height * width * bytesPerPixelIfStored;
+ if (imageYdown[0] == null || !imageYdownAllocated|| imageYdown[0].length < imageBytes)
+ imageYdown[0] = new byte[imageBytes];
+ bytesPerYdownPixelStored = bytesPerPixelIfStored;
+ storedYdownFormat = internalFormat;
+ copyImage(bImage[0],imageYdown[0], false, 0,
+ storedYdownFormat, bytesPerYdownPixelStored);
+ imageYdownCacheDirty = false;
+ imageYdownClass = BUFFERED_IMAGE;
+ imageYdownAllocated = true;
+ imageChanged &= ~1;
+ freeSurface();
+ }
+
+ // allocate an entry for the last alpha of the screen if needed
+ if (lastAlpha == null) {
+ lastAlpha = new float[screen+1];
+ lastAlpha[screen] = 1.0f;
+ }
+ else if (lastAlpha.length <= screen) {
+ float[] la = new float[screen+1];
+ for (i = 0; i < lastAlpha.length; i++) {
+ la[i] = lastAlpha[i];
+ }
+ lastAlpha = la;
+ lastAlpha[screen] = 1.0f;
+ }
+ // allocate a copy of the color data for the screen if needed.
+ // this piece of code is mainly for multi-screens case
+ if (imageYdown.length <= screen) {
+ byte[][] bdata = new byte[screen+1][];
+ byte[] idata;
+ int refScreen = -1;
+
+ int imageBytes = height * width * bytesPerYdownPixelStored;
+ idata = bdata[screen] = new byte[imageBytes];
+ for (i = 0; i < imageYdown.length; i++) {
+ bdata[i] = imageYdown[i];
+ if (Math.abs(lastAlpha[i] - alpha) < EPSILON) {
+ refScreen = i;
+ }
+ }
+
+ if (noAlpha) {
+ byteAlpha = (byte) (alpha * 255.0f + 0.5);
+ for (j=3,i=0; i< width * height; i++,j+=4) {
+ idata[j] = byteAlpha;
+ }
+ }
+ else {
+
+ // copy the data from a reference screen which has the closest
+ // alpha values
+ if (refScreen >= 0) {
+ System.arraycopy(imageYdown[refScreen], 0, idata, 0, imageBytes);
+ lastAlpha[screen] = lastAlpha[refScreen];
+ }
+ else {
+ float m = alpha/lastAlpha[0];
+ if (m < 1.0f)
+ rndoff = 0.5f;
+ else
+ rndoff = -0.5f;
+
+ byte[] srcData = imageYdown[0];
+ for (i = 0, j = 0; i < width * height; i++, j+= 4) {
+ System.arraycopy(srcData, j, idata, j, 3);
+ idata[j+3] =(byte)( ((int)srcData[j+3] & 0xff) * m + rndoff);
+ }
+ lastAlpha[screen] = alpha;
+ }
+ }
+ imageYdown = bdata;
+
+ imageChanged &= ~(1 << screen);
+ freeSurface();
+ return;
+ }
+
+ if ((imageChanged & (1<< screen)) == 0) {
+ // color data is not modified
+ // if alpha is different, update the alpha values
+ int val = -1;
+ if (Math.abs(lastAlpha[screen] - alpha) > EPSILON) {
+ byte[] idata = imageYdown[screen];
+ if (noAlpha) {
+ byteAlpha = (byte) (alpha * 255.0f + 0.5);
+ for (j=3,i=0; i< width * height; i++,j+=4) {
+ idata[j] = byteAlpha;
+ }
+ }
+ else {
+ float m = alpha/lastAlpha[screen];
+ if (m < 1.0f)
+ rndoff = 0.5f;
+ else
+ rndoff = -0.5f;
+
+ for (i = 0, j = 3; i < width * height; i++, j+= 4) {
+ idata[j] =(byte)( ((int)idata[j] & 0xff) * m + rndoff);
+ }
+ }
+ freeSurface();
+ }
+ }
+ else {
+ // color data is modified
+ if (screen == 0) {
+ // just update alpha values since screen 0 data is
+ // already updated in copyImage()
+ byte[] idata = imageYdown[0];
+ if (noAlpha) {
+ byteAlpha = (byte) (alpha * 255.0f + 0.5);
+ for (j=3,i=0; i< width * height; i++,j+=4) {
+ idata[j] = byteAlpha;
+ }
+ }
+ else {
+ for (i = 0, j = 3; i < width * height; i++, j+= 4) {
+ idata[j] =(byte)( ((int)idata[j] & 0xff) * alpha + 0.5);
+ }
+ }
+
+ }
+ else {
+ // update color values from screen 0 data
+ float m;
+ byte[] ddata = imageYdown[screen];
+ if (noAlpha) {
+ byteAlpha = (byte) (alpha * 255.0f + 0.5);
+ for (j=3,i=0; i< width * height; i++,j+=4) {
+ ddata[j] = byteAlpha;
+ }
+ }
+ else {
+ if ((imageChanged & 1) == 0) {
+ // alpha is up to date in screen 0
+ m = alpha / lastAlpha[0];
+ }
+ else {
+ m = alpha;
+ }
+
+ if (m < 1.0f)
+ rndoff = 0.5f;
+ else
+ rndoff = -0.5f;
+
+ byte[] sdata = imageYdown[0];
+
+ for (i = 0, j = 0; i < width * height; i++, j+= 4) {
+ System.arraycopy(sdata, j, ddata, j, 3);
+ ddata[j+3] =(byte)( ((int)sdata[j+3] & 0xff) * m + rndoff);
+ }
+ }
+ }
+ freeSurface();
+ }
+ lastAlpha[screen] = alpha;
+ imageChanged &= ~(1 << screen);
+ }
+ }
+
+
+ int getEffectiveBytesPerPixel() {
+ if (byReference) {
+ if (usedByTexture || !usedByRaster)
+ return bytesPerYupPixelStored;
+ else
+ return bytesPerYdownPixelStored;
+ }
+ else {
+ return bytesPerPixelIfStored;
+ }
+ }
+
+
+ int getEffectiveFormat() {
+ if (byReference) {
+ if (usedByTexture || !usedByRaster)
+ return storedYupFormat;
+ else
+ return storedYdownFormat;
+ }
+ else {
+ return internalFormat;
+ }
+ }
+
+ /**
+ * retrieve image data from read buffer to ImageComponent's
+ * internal representation
+ */
+ final void retrieveImage(byte[] buf, int wRead, int hRead) {
+ retrieveImage(buf, 0, 0, wRead, hRead);
+ }
+
+
+ /**
+ * retrieve a subimage data from read buffer to ImageComponent's
+ * internal representation
+ */
+ final void retrieveImage(byte[] buf, int xRead, int yRead, int
+ wRead, int hRead) {
+
+ int srcOffset, dstOffset, h,w,i,j;
+ int dstWidth;
+
+ byte[] bdata;
+
+
+ // If byReference, then copy to the reference image immediately after
+ // readRaster or offscreenbuffer
+
+ // In the by reference case, they should have set and image, before
+ // calling readRaster, so there should be a valid imageYup or imageYdown
+ // as used by texture or raster
+
+ // Note the format of the glReadPixels is based on storedFormat
+ // so, we can do a direct copy
+
+ int bpp = getEffectiveBytesPerPixel();
+ int format = getEffectiveFormat();
+
+ if (!byReference) {
+
+ dstWidth = width * bytesPerPixelIfStored;
+ if ((usedByTexture || !usedByRaster)&& (imageYup == null)) {
+ imageYup = new byte[height * dstWidth];
+ bytesPerYupPixelStored = bytesPerPixelIfStored;
+ storedYupFormat = internalFormat;
+ imageYupAllocated = true;
+ }
+ if (usedByRaster && imageYdown[0] == null) {
+ imageYdown[0] = new byte[height * dstWidth];
+ bytesPerYdownPixelStored = bytesPerPixelIfStored;
+ storedYdownFormat = internalFormat;
+ imageYdownAllocated = true;
+ }
+ }
+
+
+ int srcWidth = wRead * bpp;
+ int srcLineBytes = width * bpp;
+
+ /*
+ System.out.println("bytesPerYdownPixelStored = "+bytesPerYdownPixelStored+" bpp = "+bpp);
+ System.out.println("storedYdownFormat = "+storedYdownFormat+" format = "+format);
+ System.out.println("bytesPerPixelIfStored = "+bytesPerPixelIfStored+" internalformat = "+internalFormat);
+ System.out.println("imageYup = "+imageYup+" imageYdown = "+imageYdown[0]);
+ System.out.println("===> usedByRaster = "+usedByRaster);
+ */
+
+ // array copy by scanline
+
+ //position of a raster specifies the upper-left corner
+ // copy yUp -> yDown
+ imageDirty [0] = true;
+
+ if (usedByRaster) {
+ dstWidth = width * bytesPerYdownPixelStored;
+ srcOffset = (yRead * srcLineBytes) + (xRead * bpp);
+
+ dstOffset = ((height - yRead - 1)) * dstWidth +
+ (xRead * bytesPerYdownPixelStored);
+ // If by Reference and a copy has not been made ...
+ if (byReference && storedYdownFormat != internalFormat) {
+ bdata =((DataBufferByte) ((BufferedImage)bImage[0]).getRaster().getDataBuffer()).getData();
+ imageDirty [0] = false;
+ }
+ else {
+ bdata = imageYdown[0];
+ }
+ if (storedYdownFormat == format) {
+ for (h = 0; h < hRead; h++,
+ srcOffset += srcLineBytes, dstOffset -= dstWidth) {
+ System.arraycopy(buf, srcOffset, bdata, dstOffset, srcWidth);
+ }
+ }
+ else { // Would be one of the stored formats to RGBA
+ // Convert from one of the byRef formats to RGBA
+ switch(format) {
+ case BYTE_ABGR:
+ for (h = 0; h < hRead; h++,
+ srcOffset += srcLineBytes, dstOffset -= dstWidth) {
+ int offset = dstOffset;
+ for (w = 0; w < srcWidth; w +=bpp) {
+ bdata[offset++] = buf[srcOffset+w+3];
+ bdata[offset++] = buf[srcOffset+w+2];
+ bdata[offset++] = buf[srcOffset+w+1];
+ bdata[offset++] = buf[srcOffset+w];
+
+ }
+ }
+ break;
+ case BYTE_BGR:
+
+ for (h = 0; h < hRead; h++,
+ srcOffset += srcLineBytes, dstOffset -= dstWidth) {
+ int offset = dstOffset;
+ for (w = 0; w < srcWidth; w +=bpp) {
+ bdata[offset++] = buf[srcOffset+w+2];
+ bdata[offset++] = buf[srcOffset+w+1];
+ bdata[offset++] = buf[srcOffset+w];
+ bdata[offset++] = (byte)0xff;
+
+ }
+ }
+ break;
+
+ }
+ }
+ }
+
+ if (usedByTexture || !usedByRaster) {
+ imageYupCacheDirty = true;
+ dstWidth = width * bytesPerYupPixelStored;
+ srcOffset = (yRead * srcLineBytes) + (xRead * bpp);
+
+ // If by Reference and a copy has not been made ...
+ if (byReference && storedYupFormat != internalFormat) {
+ bdata =((DataBufferByte) ((BufferedImage)bImage[0]).getRaster().getDataBuffer()).getData();
+ imageDirty [0] = false;
+ }
+ else {
+ bdata = imageYup;
+ }
+ // If used by texture, then storedYupFormat is always equal to format
+ if (storedYupFormat == format) {
+ for (dstOffset = srcOffset,
+ h = 0; h < hRead; h++,
+ srcOffset += srcLineBytes, dstOffset += dstWidth) {
+ System.arraycopy(buf, srcOffset, bdata, dstOffset, srcWidth);
+ }
+ }
+ }
+ // If its by reference and a copy has been made, make the user's copy
+ // up-to-date
+
+ if (byReference && imageDirty[0]) {
+ imageDirty [0] = false;
+ if (usedByTexture || !usedByRaster) {
+ copyBufferedImageWithFormatConversion(true, 0);
+ }
+ else {
+ copyBufferedImageWithFormatConversion(false, 0);
+ }
+ }
+ imageChanged = 0xffff;
+ lastAlpha[0] = 1.0f;
+ }
+
+ /**
+ * Update data.
+ * x and y specifies the x & y offset of the image data in
+ * ImageComponent. It assumes that the origin is (0, 0).
+ */
+ void updateData(ImageComponent2D.Updater updater,
+ int x, int y, int width, int height) {
+
+ geomLock.getLock();
+
+ // call the user supplied updateData method to update the data
+ updater.updateData((ImageComponent2D)source, x, y, width, height);
+
+ // update the internal copy of the image data if a copy has been
+ // made
+ if (imageYupAllocated) {
+ copyImage(bImage[0], (x + bImage[0].getMinX()),
+ (y + bImage[0].getMinY()), imageYup, x, y,
+ true, 0, width, height, storedYupFormat,
+ bytesPerYupPixelStored);
+ }
+
+
+ if (imageYdownAllocated) {
+ copyImage(bImage[0], (x + bImage[0].getMinX()),
+ (y + bImage[0].getMinY()), imageYdown[0], x, y,
+ false, 0, width, height, storedYdownFormat,
+ bytesPerYdownPixelStored);
+ }
+
+ imageDirty[0] = true;
+
+ geomLock.unLock();
+
+
+ if (source.isLive()) {
+
+ //TODO: check whether this is needed
+ freeSurface();
+
+ // send a SUBIMAGE_CHANGED message in order to
+ // notify all the users of the change
+
+ ImageComponentUpdateInfo info;
+
+ info = VirtualUniverse.mc.getFreeImageUpdateInfo();
+ info.x = x;
+ info.y = y;
+ info.z = 0;
+ info.width = width;
+ info.height = height;
+
+ sendMessage(SUBIMAGE_CHANGED, info);
+ }
+ }
+
+ void setSubImage(RenderedImage image, int width, int height,
+ int srcX, int srcY, int dstX, int dstY) {
+
+ geomLock.getLock();
+
+ if (imageYupAllocated) {
+ copyImage(image, srcX, srcY, imageYup, dstX, dstY,
+ true, 0, width, height, storedYupFormat,
+ bytesPerYupPixelStored);
+ }
+
+ if (imageYdownAllocated) {
+ copyImage(image, srcX, srcY, imageYdown[0],
+ dstX, dstY, false, 0, width, height,
+ storedYdownFormat, bytesPerYdownPixelStored);
+ }
+
+ imageDirty[0] = true;
+
+ geomLock.unLock();
+
+
+ if (source.isLive()) {
+
+ // TODO: check whether this is needed
+ freeSurface();
+
+ // send a SUBIMAGE_CHANGED message in order to
+ // notify all the users of the change
+
+ ImageComponentUpdateInfo info;
+
+ info = VirtualUniverse.mc.getFreeImageUpdateInfo();
+ info.x = dstX;
+ info.y = dstY;
+ info.z = 0;
+ info.width = width;
+ info.height = height;
+
+ sendMessage(SUBIMAGE_CHANGED, info);
+ }
+ }
+
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ super.updateMirrorObject(component, value);
+
+ if (detailTexture != null) {
+ if (((component & IMAGE_CHANGED) != 0) ||
+ ((component & SUBIMAGE_CHANGED) != 0)) {
+
+ // notify detailTexture of image change
+
+ detailTexture.notifyImageComponentImageChanged(this, value);
+ }
+ }
+ }
+
+
+ synchronized void setDetailTexture(DetailTextureImage tex) {
+ detailTexture = tex;
+ }
+
+ synchronized DetailTextureImage getDetailTexture() {
+ if (detailTexture == null) {
+ detailTexture = new DetailTextureImage(this);
+ }
+ return detailTexture;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ImageComponent3D.java b/src/classes/share/javax/media/j3d/ImageComponent3D.java
new file mode 100644
index 0000000..6404784
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ImageComponent3D.java
@@ -0,0 +1,711 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+
+/**
+ * This class defines a 3D image component. This is used for texture
+ * images.
+ * Prior to Java 3D 1.2, only BufferedImage objects could be used as
+ * the input to an ImageComponent3D object. As of Java 3D 1.2, an
+ * ImageComponent3D accepts an array of arbitrary RenderedImage
+ * objects (BufferedImage is an implementation of the RenderedImage
+ * interface). The methods that set/get a BufferedImage object are
+ * left in for compatibility. The new methods that set/get a
+ * RenderedImage are a superset of the old methods. In particular,
+ * the two set methods in the following example are equivalent:
+ *
+ * <p>
+ * <ul>
+ * <code>
+ * BufferedImage bi;<br>
+ * RenderedImage ri = bi;<br>
+ * ImageComponent3D ic;<br>
+ * <p>
+ * // Set image 0 to the specified BufferedImage<br>
+ * ic.set(0, bi);<br>
+ * <p>
+ * // Set image 0 to the specified RenderedImage<br>
+ * ic.set(0, ri);<br>
+ * </code>
+ * </ul>
+ */
+public class ImageComponent3D extends ImageComponent {
+
+ // non-public, no parameter constructor
+ ImageComponent3D() {}
+
+ /**
+ * Constructs a 3D image component object using the specified
+ * format, width, height, and depth. Default values are used for
+ * all other parameters. The default values are as follows:
+ * <ul>
+ * array of images : null<br>
+ * </ul>
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param width the number of columns of pixels in this image component
+ * object
+ * @param height the number of rows of pixels in this image component
+ * object
+ * @param depth the number of 2D slices in this image component object
+ * @exception IllegalArgumentException if format is invalid, or if
+ * any of width, height, or depth are not positive.
+ */
+ public ImageComponent3D(int format,
+ int width,
+ int height,
+ int depth) {
+
+ ((ImageComponent3DRetained)this.retained).processParams(format, width, height, depth);
+ ((ImageComponent3DRetained)this.retained).setDepth(depth);
+ }
+
+ /**
+ * Constructs a 3D image component object using the specified format,
+ * and the BufferedImage array.
+ * Default values are used for all other parameters.
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA etc.
+ * @param images an array of BufferedImage objects. The
+ * first image in the array determines the width and height of this
+ * ImageComponent3D.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the first image are not positive.
+ */
+ public ImageComponent3D(int format, BufferedImage[] images) {
+ ((ImageComponent3DRetained)this.retained).processParams(format,
+ images[0].getWidth(null), images[0].getHeight(null), images.length);
+ ((ImageComponent3DRetained)this.retained).setDepth(images.length);
+ for (int i=0; i<images.length; i++) {
+ ((ImageComponent3DRetained)this.retained).set(i, images[i]);
+ }
+ }
+
+ /**
+ * Constructs a 3D image component object using the specified format,
+ * and the RenderedImage array.
+ * Default values are used for all other parameters.
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA etc.
+ * @param images an array of RenderedImage objects. The
+ * first image in the array determines the width and height of this
+ * ImageComponent3D.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the first image are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent3D(int format, RenderedImage[] images) {
+
+ ((ImageComponent3DRetained)this.retained).processParams(format,
+ images[0].getWidth(), images[0].getHeight(), images.length);
+ ((ImageComponent3DRetained)this.retained).setDepth(images.length);
+ for (int i=0; i<images.length; i++) {
+ ((ImageComponent3DRetained)this.retained).set(i, images[i]);
+ }
+ }
+
+ /**
+ * Constructs a 3D image component object using the specified
+ * format, width, height, depth, byReference flag, and yUp flag.
+ * Default values are used for all other parameters.
+ *
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA, etc.
+ * @param width the number of columns of pixels in this image component
+ * object
+ * @param height the number of rows of pixels in this image component
+ * object
+ * @param depth the number of 2D slices in this image component object
+ * @param byReference a flag that indicates whether the data is copied
+ * into this image component object or is accessed by reference.
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ *
+ * @exception IllegalArgumentException if format is invalid, or if
+ * any of width, height, or depth are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent3D(int format,
+ int width,
+ int height,
+ int depth,
+ boolean byReference,
+ boolean yUp) {
+
+ ((ImageComponentRetained)this.retained).setByReference(byReference);
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ ((ImageComponent3DRetained)this.retained).processParams(format, width, height, depth);
+ ((ImageComponent3DRetained)this.retained).setDepth(depth);
+ }
+
+ /**
+ * Constructs a 3D image component object using the specified format,
+ * BufferedImage array, byReference flag, and yUp flag.
+ * Default values are used for all other parameters.
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA etc.
+ * @param images an array of BufferedImage objects. The
+ * first image in the array determines the width and height of this
+ * ImageComponent3D.
+ * @param byReference a flag that indicates whether the data is copied
+ * into this image component object or is accessed by reference.
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the first image are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent3D(int format,
+ BufferedImage[] images,
+ boolean byReference,
+ boolean yUp) {
+
+
+ ((ImageComponentRetained)this.retained).setByReference(byReference);
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ ((ImageComponent3DRetained)this.retained).processParams(format, images[0].getWidth(null), images[0].getHeight(null), images.length);
+ ((ImageComponent3DRetained)this.retained).setDepth(images.length);
+ for (int i=0; i<images.length; i++) {
+ ((ImageComponent3DRetained)this.retained).set(i, images[i]);
+ }
+ }
+
+ /**
+ * Constructs a 3D image component object using the specified format,
+ * RenderedImage array, byReference flag, and yUp flag.
+ * Default values are used for all other parameters.
+ * @param format the image component format, one of: FORMAT_RGB,
+ * FORMAT_RGBA etc.
+ * @param images an array of RenderedImage objects. The
+ * first image in the array determines the width and height of this
+ * ImageComponent3D.
+ * @param byReference a flag that indicates whether the data is copied
+ * into this image component object or is accessed by reference.
+ * @param yUp a flag that indicates the y-orientation of this image
+ * component. If yUp is set to true, the origin of the image is
+ * the lower left; otherwise, the origin of the image is the upper
+ * left.
+ * @exception IllegalArgumentException if format is invalid, or if
+ * the width or height of the first image are not positive.
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent3D(int format,
+ RenderedImage[] images,
+ boolean byReference,
+ boolean yUp) {
+
+
+ ((ImageComponentRetained)this.retained).setByReference(byReference);
+ ((ImageComponentRetained)this.retained).setYUp(yUp);
+ ((ImageComponent3DRetained)this.retained).processParams(format, images[0].getWidth(), images[0].getHeight(), images.length);
+ ((ImageComponent3DRetained)this.retained).setDepth(images.length);
+ for (int i=0; i<images.length; i++) {
+ ((ImageComponent3DRetained)this.retained).set(i, images[i]);
+ }
+ }
+
+ /**
+ * Retrieves the depth of this 3D image component object.
+ * @return the format of this 3D image component object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getDepth() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ImageComponent.ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent3D0"));
+ return ((ImageComponent3DRetained)this.retained).getDepth();
+ }
+
+ /**
+ * Sets the array of images in this image component to the
+ * specified array of BufferedImage objects. If the data access
+ * mode is not by-reference, then the BufferedImage data is copied
+ * into this object. If the data access mode is by-reference,
+ * then a shallow copy of the array of references to the
+ * BufferedImage objects is made, but the BufferedImage
+ * data is not necessarily copied.
+ *
+ * @param images array of BufferedImage objects containing the image.
+ * The format and size must be the same as the current format in the
+ * image component.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ */
+ public void set(BufferedImage[] images) {
+ checkForLiveOrCompiled();
+ int depth = ((ImageComponent3DRetained)this.retained).getDepth();
+
+ if (depth != images.length)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D1"));
+ for (int i=0; i<depth; i++) {
+ ((ImageComponent3DRetained)this.retained).set(i, images[i]);
+ }
+ }
+
+ /**
+ * Sets the array of images in this image component to the
+ * specified array of RenderedImage objects. If the data access
+ * mode is not by-reference, then the RenderedImage data is copied
+ * into this object. If the data access mode is by-reference,
+ * then a shallow copy of the array of references to the
+ * RenderedImage objects is made, but the RenderedImage
+ * data is not necessarily copied.
+ *
+ * @param images array of RenderedImage objects containing the image.
+ * The format and size must be the same as the current format in the
+ * image component.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void set(RenderedImage[] images) {
+
+ checkForLiveOrCompiled();
+ int depth = ((ImageComponent3DRetained)this.retained).getDepth();
+
+ if (depth != images.length)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D1"));
+ for (int i=0; i<depth; i++) {
+ ((ImageComponent3DRetained)this.retained).set(i, images[i]);
+ }
+ }
+
+ /**
+ * Sets this image component at the specified index to the
+ * specified BufferedImage object. If the data access mode is not
+ * by-reference, then the BufferedImage data is copied into this
+ * object. If the data access mode is by-reference, then a
+ * reference to the BufferedImage is saved, but the data is not
+ * necessarily copied.
+ *
+ * @param index the image index
+ * @param image BufferedImage object containing the image.
+ * The format and size must be the same as the current format in this
+ * ImageComponent3D object. The index must not exceed the depth of this
+ * ImageComponent3D object.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ */
+ public void set(int index, BufferedImage image) {
+ checkForLiveOrCompiled();
+ if (image.getWidth(null) != this.getWidth())
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D2"));
+
+ if (image.getHeight(null) != this.getHeight())
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D4"));
+
+ ((ImageComponent3DRetained)this.retained).set(index, image);
+ }
+
+ /**
+ * Sets this image component at the specified index to the
+ * specified RenderedImage object. If the data access mode is not
+ * by-reference, then the RenderedImage data is copied into this
+ * object. If the data access mode is by-reference, then a
+ * reference to the RenderedImage is saved, but the data is not
+ * necessarily copied.
+ *
+ * @param index the image index
+ * @param image RenderedImage object containing the image.
+ * The format and size must be the same as the current format in this
+ * ImageComponent3D object. The index must not exceed the depth of this
+ * ImageComponent3D object.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void set(int index, RenderedImage image) {
+
+ checkForLiveOrCompiled();
+ if (image.getWidth() != this.getWidth())
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D2"));
+
+ if (image.getHeight() != this.getHeight())
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D4"));
+
+ ((ImageComponent3DRetained)this.retained).set(index, image);
+ }
+
+ /**
+ * Retrieves the images from this ImageComponent3D object. If the
+ * data access mode is not by-reference, then a copy of the images
+ * is made. If the data access mode is by-reference, then the
+ * references are returned.
+ *
+ * @return either a new array of new BufferedImage objects created from
+ * the data
+ * in this image component, or a new array of
+ * references to the BufferedImages that this image component refers to.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data access mode is
+ * by-reference and any image referenced by this ImageComponent3D
+ * object is not an instance of BufferedImage.
+ */
+ public BufferedImage[] getImage() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ImageComponent.ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent3D3"));
+ return ((ImageComponent3DRetained)this.retained).getImage();
+ }
+
+ /**
+ * Retrieves the images from this ImageComponent3D object. If the
+ * data access mode is not by-reference, then a copy of the images
+ * is made. If the data access mode is by-reference, then the
+ * references are returned.
+ *
+ * @return either a new array of new RenderedImage objects created from
+ * the data
+ * in this image component, or a new array of
+ * references to the RenderedImages that this image component refers to.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public RenderedImage[] getRenderedImage() {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ImageComponent.ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent3D3"));
+ return ((ImageComponent3DRetained)this.retained).getRenderedImage();
+ }
+
+ /**
+ * Retrieves one of the images from this ImageComponent3D object. If the
+ * data access mode is not by-reference, then a copy of the image
+ * is made. If the data access mode is by-reference, then the
+ * reference is returned.
+ *
+ * @param index the index of the image to retrieve
+ * @return either a new BufferedImage object created from the data
+ * in this image component, or the BufferedImage object referenced
+ * by this image component.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalStateException if the data access mode is
+ * by-reference and the image referenced by this ImageComponent3D
+ * object at the specified index is not an instance of BufferedImage.
+ */
+ public BufferedImage getImage(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ImageComponent.ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent3D3"));
+
+ RenderedImage img = ((ImageComponent3DRetained)this.retained).getImage(index);
+ if ((img != null) && !(img instanceof BufferedImage)) {
+ throw new IllegalStateException(J3dI18N.getString("ImageComponent3D9"));
+ }
+ return (BufferedImage) img;
+ }
+
+ /**
+ * Retrieves one of the images from this ImageComponent3D object. If the
+ * data access mode is not by-reference, then a copy of the image
+ * is made. If the data access mode is by-reference, then the
+ * reference is returned.
+ *
+ * @param index the index of the image to retrieve
+ * @return either a new RenderedImage object created from the data
+ * in this image component, or the RenderedImage object referenced
+ * by this image component.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public RenderedImage getRenderedImage(int index) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ImageComponent.ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent3D3"));
+ return ((ImageComponent3DRetained)this.retained).getImage(index);
+ }
+
+ /**
+ * Modifies a contiguous subregion of a particular slice of
+ * image of this ImageComponent3D object.
+ * Block of data of dimension (width * height)
+ * starting at the offset (srcX, srcY) of the specified
+ * RenderedImage object will be copied into the particular slice of
+ * image component
+ * starting at the offset (dstX, dstY) of this ImageComponent3D object.
+ * The specified RenderedImage object must be of the same format as
+ * the current format of this object.
+ * This method can only be used if the data access mode is
+ * by-copy. If it is by-reference, see updateData().
+ *
+ * @param index index of the image to be modified. The index must not
+ * exceed the depth of the object.
+ * @param image RenderedImage object containing the subimage.
+ * @param width width of the subregion.
+ * @param height height of the subregion.
+ * @param srcX starting X offset of the subregion in the specified image.
+ * @param srcY starting Y offset of the subregion in the specified image.
+ * @param dstX startin X offset of the subregion in the image
+ * component of this object.
+ * @param dstY starting Y offset of the subregion in the image
+ * component of this object.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalStateException if the data access mode is
+ * <code>BY_REFERENCE</code>.
+ * @exception IllegalArgumentException if <code>width</code> or
+ * <code>height</code> of
+ * the subregion exceeds the dimension of the image in this object.
+ * @exception IllegalArgumentException if <code>dstX</code> < 0, or
+ * (<code>dstX</code> + <code>width</code>) > width of this object, or
+ * <code>dstY</code> < 0, or
+ * (<code>dstY</code> + <code>height</code>) > height of this object.
+ * @exception IllegalArgumentException if <code>srcX</code> < 0, or
+ * (<code>srcX</code> + <code>width</code>) > width of the RenderedImage
+ * object containing the subimage, or
+ * <code>srcY</code> < 0, or
+ * (<code>srcY</code> + <code>height</code>) > height of the
+ * RenderedImage object containing the subimage.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setSubImage(int index, RenderedImage image,
+ int width, int height,
+ int srcX, int srcY, int dstX, int dstY) {
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_IMAGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("ImageComponent3D5"));
+ }
+
+ if (((ImageComponent3DRetained)this.retained).isByReference()) {
+ throw new IllegalStateException(
+ J3dI18N.getString("ImageComponent3D8"));
+ }
+
+ int w = ((ImageComponent3DRetained)this.retained).getWidth();
+ int h = ((ImageComponent3DRetained)this.retained).getHeight();
+
+ if ((srcX < 0) || (srcY < 0) ||
+ ((srcX + width) > w) || ((srcY + height) > h) ||
+ (dstX < 0) || (dstY < 0) ||
+ ((dstX + width) > w) || ((dstY + height) > h)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("ImageComponent3D7"));
+ }
+
+ ((ImageComponent3DRetained)this.retained).setSubImage(
+ index, image, width, height, srcX, srcY, dstX, dstY);
+ }
+
+ /**
+ * Updates a particular slice of image data that is accessed by reference.
+ * This method calls the updateData method of the specified
+ * ImageComponent3D.Updater object to synchronize updates to the
+ * image data that is referenced by this ImageComponent3D object.
+ * Applications that wish to modify such data must perform all
+ * updates via this method.
+ * <p>
+ * The data to be modified has to be within the boundary of the
+ * subregion
+ * specified by the offset (x, y) and the dimension (width*height).
+ * It is illegal to modify data outside this boundary. If any
+ * referenced data is modified outside the updateData method, or
+ * any data outside the specified boundary is modified, the
+ * results are undefined.
+ * <p>
+ * @param updater object whose updateData callback method will be
+ * called to update the data referenced by this ImageComponent3D object.
+ * @param index index of the image to be modified. The index must
+ * not exceed the depth of this object.
+ * @param x starting X offset of the subregion.
+ * @param y starting Y offset of the subregion.
+ * @param width width of the subregion.
+ * @param height height of the subregion.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalStateException if the data access mode is
+ * <code>BY_COPY</code>.
+ * @exception IllegalArgumentException if <code>width</code> or
+ * <code>height</code> of
+ * the subregion exceeds the dimension of the image in this object.
+ * @exception IllegalArgumentException if <code>x</code> < 0, or
+ * (<code>x</code> + <code>width</code>) > width of this object, or
+ * <code>y</code> < 0, or
+ * (<code>y</code> + <code>height</code>) > height of this object.
+ * @exception ArrayIndexOutOfBoundsException if <code>index</code> > the
+ * depth of this object.
+ *
+ * @since Java 3D 1.3
+ */
+ public void updateData(Updater updater, int index,
+ int x, int y,
+ int width, int height) {
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_IMAGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("ImageComponent3D5"));
+ }
+
+ if (!((ImageComponent3DRetained)this.retained).isByReference()) {
+ throw new IllegalStateException(
+ J3dI18N.getString("ImageComponent3D6"));
+ }
+
+ int w = ((ImageComponent3DRetained)this.retained).getWidth();
+ int h = ((ImageComponent3DRetained)this.retained).getHeight();
+
+ if ((x < 0) || (y < 0) || ((x + width) > w) || ((y + height) > h)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("ImageComponent3D7"));
+ }
+
+ ((ImageComponent3DRetained)this.retained).updateData(
+ updater, index, x, y, width, height);
+ }
+
+
+ /**
+ * Creates a retained mode ImageComponent3DRetained object that this
+ * ImageComponent3D component object will point to.
+ */
+ void createRetained() {
+ this.retained = new ImageComponent3DRetained();
+ this.retained.setSource(this);
+ }
+
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ ImageComponent3DRetained rt = (ImageComponent3DRetained) retained;
+
+ ImageComponent3D img = new ImageComponent3D(rt.format,
+ rt.width,
+ rt.height,
+ rt.depth);
+
+ // TODO : replace by this to duplicate other attributes
+ /*
+ ImageComponent3D img = new ImageComponent3D(rt.format,
+ rt.width,
+ rt.height,
+ rt.depth,
+ rt.byReference,
+ rt.yUp);
+ */
+ img.duplicateNodeComponent(this);
+ return img;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ RenderedImage imgs[] = ((ImageComponent3DRetained)
+ originalNodeComponent.retained).getImage();
+
+ if (imgs != null) {
+ ImageComponent3DRetained rt = (ImageComponent3DRetained) retained;
+
+ for (int i=rt.depth-1; i>=0; i--) {
+ if (imgs[i] != null) {
+ rt.set(i, imgs[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * The ImageComponent3D.Updater interface is used in updating image data
+ * that is accessed by reference from a live or compiled ImageComponent
+ * object. Applications that wish to modify such data must define a
+ * class that implements this interface. An instance of that class is
+ * then passed to the <code>updateData</code> method of the
+ * ImageComponent object to be modified.
+ *
+ * @since Java 3D 1.3
+ */
+ public static interface Updater {
+ /**
+ * Updates image data that is accessed by reference.
+ * This method is called by the updateData method of an
+ * ImageComponent object to effect
+ * safe updates to image data that
+ * is referenced by that object. Applications that wish to modify
+ * such data must implement this method and perform all updates
+ * within it.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ *
+ * @param imageComponent the ImageComponent object being updated.
+ * @param index index of the image to be modified.
+ * @param x starting X offset of the subregion.
+ * @param y starting Y offset of the subregion.
+ * @param width width of the subregion.
+ * @param height height of the subregion.
+ *
+ * @see ImageComponent3D#updateData
+ */
+ public void updateData(ImageComponent3D imageComponent,
+ int index,
+ int x, int y,
+ int width, int height);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ImageComponent3DRetained.java b/src/classes/share/javax/media/j3d/ImageComponent3DRetained.java
new file mode 100644
index 0000000..800ff67
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ImageComponent3DRetained.java
@@ -0,0 +1,217 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.image.*;
+
+/**
+ * This class defines a 3D array of pixels.
+ * This is used for texture images.
+ */
+
+class ImageComponent3DRetained extends ImageComponentRetained {
+ int depth; // Depth of 3D image
+
+ void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ /**
+ * Retrieves the depth of this 3D image component object.
+ * @return the format of this 3D image component object
+ */
+ final int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Copies the specified BufferedImage to this 3D image component
+ * object at the specified index.
+ * @param index the image index
+ * @param images BufferedImage object containing the image.
+ * The format and size must be the same as the current format in this
+ * ImageComponent3D object. The index must not exceed the depth of this
+ * ImageComponent3D object.
+ */
+ final void set(int index, BufferedImage image) {
+ if (imageYup == null)
+ imageYup = new byte[height * width * depth * bytesPerPixelIfStored];
+ imageDirty[index] = true;
+ storedYupFormat = internalFormat;
+ bytesPerYupPixelStored = bytesPerPixelIfStored;
+ copyImage(image, imageYup, true, index, storedYupFormat,
+ bytesPerYupPixelStored);
+ if (byReference)
+ bImage[index] = image;
+ }
+
+ final void set(int index, RenderedImage image) {
+ if (image instanceof BufferedImage) {
+ set(index, ((BufferedImage)image));
+ }
+ else {
+ // Create a buffered image from renderImage
+ ColorModel cm = image.getColorModel();
+ WritableRaster wRaster = image.copyData(null);
+ BufferedImage bi = new BufferedImage(cm,
+ wRaster,
+ cm.isAlphaPremultiplied()
+ ,null);
+ set(index, bi);
+ }
+ }
+
+ /**
+ * Retrieves a copy of the images in this ImageComponent3D object.
+ * @return a new array of new BufferedImage objects created from the
+ * images in this ImageComponent3D object
+ */
+ final RenderedImage[] getRenderedImage() {
+ int i;
+ RenderedImage bi[] = new RenderedImage[bImage.length];
+ if (!byReference) {
+ for (i=0; i<depth; i++) {
+ if (imageDirty[i]) {
+ retrieveBufferedImage(i);
+ }
+ }
+ }
+ for (i = 0; i < bImage.length; i++) {
+ bi[i] = bImage[i];
+ }
+ // If by reference, then the image should not be dirty
+ return bi;
+ }
+
+
+ /**
+ * Retrieves a copy of the images in this ImageComponent3D object.
+ * @return a new array of new BufferedImage objects created from the
+ * images in this ImageComponent3D object
+ */
+ final BufferedImage[] getImage() {
+ int i;
+ BufferedImage bi[] = new BufferedImage[bImage.length];
+
+ if (!byReference) {
+ for (i=0; i<depth; i++) {
+ if (imageDirty[i]) {
+ retrieveBufferedImage(i);
+ }
+ }
+ }
+
+ for (i = 0; i < bImage.length; i++) {
+ if (!(bImage[i] instanceof BufferedImage)) {
+ throw new IllegalStateException(J3dI18N.getString("ImageComponent3DRetained0"));
+ }
+ bi[i] = (BufferedImage) bImage[i];
+ }
+ // If by reference, then the image should not be dirty
+ return bi;
+ }
+
+ /**
+ * Retrieves a copy of one of the images in this ImageComponent3D object.
+ * @param index the index of the image to retrieve
+ * @return a new BufferedImage objects created from the
+ * image at the specified index in this ImageComponent3D object
+ */
+ final RenderedImage getImage(int index) {
+ if (!byReference) {
+ if (imageDirty[index]) {
+ retrieveBufferedImage(index);
+ }
+ }
+ return bImage[index];
+ }
+
+ /**
+ * Update data.
+ * x and y specifies the x & y offset of the image data in
+ * ImageComponent. It assumes that the origin is (0, 0).
+ */
+ void updateData(ImageComponent3D.Updater updater,
+ int index, int x, int y, int width, int height) {
+
+ geomLock.getLock();
+
+ // call the user supplied updateData method to update the data
+ updater.updateData((ImageComponent3D)source, index, x, y, width, height);
+
+ // update the internal copy of the image data if a copy has been
+ // made
+ if (imageYupAllocated) {
+ copyImage(bImage[0], (x + bImage[0].getMinX()),
+ (y + bImage[0].getMinY()), imageYup, x, y,
+ true, index, width, height, storedYupFormat,
+ bytesPerYupPixelStored);
+ }
+
+ imageDirty[index] = true;
+
+ geomLock.unLock();
+
+
+ if (source.isLive()) {
+
+ // send a SUBIMAGE_CHANGED message in order to
+ // notify all the users of the change
+
+ ImageComponentUpdateInfo info;
+
+ info = VirtualUniverse.mc.getFreeImageUpdateInfo();
+ info.x = x;
+ info.y = y;
+ info.z = index;
+ info.width = width;
+ info.height = height;
+
+ sendMessage(SUBIMAGE_CHANGED, info);
+ }
+ }
+
+ void setSubImage(int index, RenderedImage image, int width, int height,
+ int srcX, int srcY, int dstX, int dstY) {
+
+ geomLock.getLock();
+
+ if (imageYupAllocated) {
+ copyImage(image, srcX, srcY, imageYup, dstX, dstY,
+ true, index, width, height, storedYupFormat,
+ bytesPerYupPixelStored);
+ }
+
+ imageDirty[index] = true;
+
+ geomLock.unLock();
+
+
+ if (source.isLive()) {
+
+ // send a SUBIMAGE_CHANGED message in order to
+ // notify all the users of the change
+
+ ImageComponentUpdateInfo info;
+
+ info = VirtualUniverse.mc.getFreeImageUpdateInfo();
+ info.x = dstX;
+ info.y = dstY;
+ info.z = index;
+ info.width = width;
+ info.height = height;
+
+ sendMessage(SUBIMAGE_CHANGED, info);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ImageComponentRetained.java b/src/classes/share/javax/media/j3d/ImageComponentRetained.java
new file mode 100644
index 0000000..17f45be
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ImageComponentRetained.java
@@ -0,0 +1,1559 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import java.awt.image.*;
+import java.awt.color.ColorSpace;
+import java.awt.Transparency;
+
+/**
+ * Abstract class that is used to define 2D or 3D ImageComponent classes
+ * used in a Java 3D scene graph.
+ * This is used for texture images, background images and raster components
+ * of Shape3D nodes.
+ */
+
+abstract class ImageComponentRetained extends NodeComponentRetained {
+ int format; // PixelArray format (RGB, RGBA, ALPHA, etc.)
+ int width; // Width of PixelArray
+ int height; // Height of PixelArray
+ byte[][] imageYdown = new byte[1][]; // 2D array of pixel values in
+ // one of various formats - Y downwards
+ byte[] imageYup; // 2D or 3D array of pixel values in
+ // one of various formats - Y upwards
+
+ int bytesPerPixel; // computed from input format
+ boolean usedByRaster = false; // used by a raster object?
+ boolean usedByTexture = false; // used by a texture object?
+
+ boolean byReference = false; // Is the imageComponent by reference
+ boolean yUp = false;
+
+ // array of Buffered image
+ // This will store the refImage array, if the imagecomponent
+ // is by reference
+ RenderedImage[] bImage;
+ boolean[] imageDirty; // array of image dirty flag
+
+ boolean noAlpha = false;
+
+ // Format of the Yup and Ydown image
+ // in the case of "by Copy" it is RGBA
+ // In the case of "by ref" it may be one of the original
+ // formats supported by OpenGL
+ /*
+ int storedFormat;
+ */
+ int bytesPerPixelIfStored; // Number of bytes if a copy is made
+ int storedYupFormat;
+ int storedYdownFormat;
+
+ int bytesPerYupPixelStored;
+ int bytesPerYdownPixelStored;
+
+ int internalFormat; // Used when a copy is made, RGBA, LA, L
+ boolean imageYupAllocated = false;
+ boolean imageYdownAllocated = false;
+
+ // If cache is dirty (clearLive, setLive has occureed), then
+ // extension based cache needs to be re-evaluated
+ boolean imageYupCacheDirty = false;
+ boolean imageYdownCacheDirty = false;
+
+
+ static final int BYTE_RGBA = 0x1;
+ static final int BYTE_ABGR = 0x2;
+ static final int BYTE_GRAY = 0x4;
+ static final int USHORT_GRAY = 0x8;
+ static final int BYTE_LA = 0x10;
+ static final int BYTE_BGR = 0x20;
+ static final int BYTE_RGB = 0x40;
+
+ int imageYupClass = 0;
+ int imageYdownClass = 0;
+ static final int BUFFERED_IMAGE = 0x1;
+ static final int RENDERED_IMAGE = 0x2;
+
+ // change flag
+ static final int IMAGE_CHANGED = 0x01;
+ static final int SUBIMAGE_CHANGED = 0x02;
+
+ // Lock used in the "by ref case"
+ GeometryLock geomLock = new GeometryLock();
+
+ int minTileX = 0;
+ int minTileY = 0;
+ int minTileZ = 0;
+
+ int tilew = 0;
+ int tileh = 0;
+ int tiled = 0;
+
+ int numXTiles = 0;
+ int numYTiles = 0;
+ int numZTiles = 0;
+
+ int tileGridXOffset = 0;
+ int tileGridYOffset = 0;
+
+
+ int minX = 0;
+ int minY = 0;
+
+ // lists of Node Components that are referencing this ImageComponent
+ // object. This list is used to notify the referencing node components
+ // of any changes of this ImageComponent.
+
+ ArrayList userList = new ArrayList();
+
+ /**
+ * Retrieves the width of this image component object.
+ * @return the width of this image component object
+ */
+ final int getWidth() {
+ return width;
+ }
+
+ /**
+ * Retrieves the height of this image component object.
+ * @return the height of this image component object
+ */
+ final int getHeight() {
+ return height;
+ }
+
+ /**
+ * Retrieves the format of this image component object.
+ * @return the format of this image component object
+ */
+ final int getFormat() {
+ return format;
+ }
+
+ /**
+ * Check if ImageComponent parameters have valid values..
+ */
+ void processParams(int format, int width, int height, int depth) {
+ if (width < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained0"));
+
+ if (height < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained1"));
+
+ if (depth < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained2"));
+
+ if (format < 1 || format > ImageComponent.FORMAT_TOTAL)
+ throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained3"));
+ this.format = format;
+ this.width = width;
+ this.height = height;
+ imageDirty = new boolean[depth];
+ for (int i=0; i< depth; i++)
+ imageDirty[i] = false;
+ bImage = new RenderedImage[depth];
+
+ noAlpha = (format == ImageComponent.FORMAT_RGB ||
+ format == ImageComponent.FORMAT_RGB4 ||
+ format == ImageComponent.FORMAT_R3_G3_B2 ||
+ format == ImageComponent.FORMAT_RGB5);
+
+ // If the format is 8bit per component, we may send it down
+ // to OpenGL directly if its by ref case
+ switch (format) {
+ case ImageComponent.FORMAT_RGB:// same as ImageComponent.FORMAT_RGB8
+ bytesPerPixel = 3;
+ bytesPerPixelIfStored = 4;
+ internalFormat = BYTE_RGBA;
+ break;
+ case ImageComponent.FORMAT_RGBA:// same as ImageComponent.FORMAT_RGBA8
+ bytesPerPixel = 4;
+ bytesPerPixelIfStored = 4;
+ internalFormat = BYTE_RGBA;
+ break;
+ case ImageComponent.FORMAT_RGB5:
+ bytesPerPixel = 2;
+ bytesPerPixelIfStored = 4;
+ internalFormat = BYTE_RGBA;
+ break;
+ case ImageComponent.FORMAT_RGB5_A1:
+ bytesPerPixel = 2;
+ bytesPerPixelIfStored = 4;
+ internalFormat = BYTE_RGBA;
+ break;
+ case ImageComponent.FORMAT_RGB4:
+ bytesPerPixel = 2;
+ bytesPerPixelIfStored = 4;
+ internalFormat = BYTE_RGBA;
+ break;
+ case ImageComponent.FORMAT_RGBA4:
+ bytesPerPixel = 2;
+ bytesPerPixelIfStored = 4;
+ internalFormat = BYTE_RGBA;
+ break;
+ case ImageComponent.FORMAT_R3_G3_B2:
+ bytesPerPixel = 1;
+ bytesPerPixelIfStored = 4;
+ internalFormat = BYTE_RGBA;
+ break;
+ case ImageComponent.FORMAT_LUM4_ALPHA4:
+ bytesPerPixel = 1;
+ bytesPerPixelIfStored = 2;
+ internalFormat = BYTE_LA;
+ break;
+ case ImageComponent.FORMAT_LUM8_ALPHA8:
+ bytesPerPixel = 2;
+ bytesPerPixelIfStored = 2;
+ internalFormat = BYTE_LA;
+ break;
+ case ImageComponent.FORMAT_CHANNEL8:
+ bytesPerPixel = 1;
+ bytesPerPixelIfStored = 1;
+ internalFormat = BYTE_GRAY;
+ break;
+ default:
+ // ERROR
+ }
+ }
+
+ void setTextureRef() {
+ usedByTexture = true;
+ }
+
+ void setRasterRef() {
+ usedByRaster = true;
+ }
+
+
+ boolean formatMatches(int format, RenderedImage ri) {
+
+ // there is no RenderedImage format that matches BYTE_LA
+ if (format == BYTE_LA) {
+ return false;
+ }
+
+ int riFormat = getImageType(ri);
+
+ if ((format == BYTE_ABGR && riFormat == BufferedImage.TYPE_4BYTE_ABGR)
+ || (format == BYTE_BGR && riFormat == BufferedImage.TYPE_3BYTE_BGR)
+ || (format == BYTE_GRAY && riFormat == BufferedImage.TYPE_BYTE_GRAY)
+ || (format == USHORT_GRAY && riFormat ==
+ BufferedImage.TYPE_USHORT_GRAY)) {
+ return true;
+ }
+
+ if (riFormat == BufferedImage.TYPE_CUSTOM) {
+ if (is4ByteRGBAOr3ByteRGB(ri)) {
+ int numBands = ri.getSampleModel().getNumBands();
+ if (numBands == 3 && format == BYTE_RGB) {
+ return true;
+ } else if (numBands == 4 && format == BYTE_RGBA) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * copy complete region of a RenderedImage
+ */
+ final void copyImage(RenderedImage ri, byte[] image,
+ boolean usedByTexture, int depth,
+ int imageFormat, int imageBytesPerPixel) {
+
+ if (ri instanceof BufferedImage) {
+ copyImage((BufferedImage)ri, 0, 0, image, 0, 0,
+ usedByTexture, depth, width, height,
+ imageFormat, imageBytesPerPixel);
+ } else {
+ copyImage(ri, ri.getMinX(), ri.getMinY(),
+ image, 0, 0, usedByTexture, depth, width, height,
+ imageFormat, imageBytesPerPixel);
+ }
+ }
+
+
+ /**
+ * copy subregion of a RenderedImage
+ */
+ final void copyImage(RenderedImage ri, int srcX, int srcY,
+ byte[] image, int dstX, int dstY,
+ boolean usedByTexture, int depth,
+ int copywidth, int copyheight,
+ int imageFormat, int imageBytesPerPixel) {
+
+ if (ri instanceof BufferedImage) {
+ copyImage((BufferedImage)ri, srcX, srcY, image, dstX, dstY,
+ usedByTexture, depth, copywidth, copyheight,
+ imageFormat, imageBytesPerPixel);
+ return;
+ }
+
+
+ int w, h, i, j, m, n;
+ int dstBegin;
+ Object pixel = null;
+ java.awt.image.Raster ras;
+ int lineBytes = width * imageBytesPerPixel; // nbytes per line in
+ // dst image buffer
+ int sign; // -1 for going down
+ int dstLineBytes; // sign * lineBytes
+ int tileStart; // destination buffer offset
+ // at the next left most tile
+
+ int offset;
+
+ ColorModel cm = ri.getColorModel();
+
+ int xoff = ri.getTileGridXOffset(); // tile origin x offset
+ int yoff = ri.getTileGridYOffset(); // tile origin y offset
+ int minTileX = ri.getMinTileX(); // min tile x index
+ int minTileY = ri.getMinTileY(); // min tile y index
+ tilew = ri.getTileWidth(); // tile width in pixels
+ tileh = ri.getTileHeight(); // tile height in pixels
+
+
+ // determine the first tile of the image
+
+ float mt;
+
+ mt = (float)(srcX - xoff) / (float)tilew;
+ if (mt < 0) {
+ minTileX = (int)(mt - 1);
+ } else {
+ minTileX = (int)mt;
+ }
+
+ mt = (float)(srcY - yoff) / (float)tileh;
+ if (mt < 0) {
+ minTileY = (int)(mt - 1);
+ } else {
+ minTileY = (int)mt;
+ }
+
+
+ // determine the pixel offset of the upper-left corner of the
+ // first tile
+
+ int startXTile = minTileX * tilew + xoff;
+ int startYTile = minTileY * tileh + yoff;
+
+
+ // image dimension in the first tile
+
+ int curw = (startXTile + tilew - srcX);
+ int curh = (startYTile + tileh - srcY);
+
+
+ // check if the to-be-copied region is less than the tile image
+ // if so, update the to-be-copied dimension of this tile
+
+ if (curw > copywidth) {
+ curw = copywidth;
+ }
+
+ if (curh > copyheight) {
+ curh = copyheight;
+ }
+
+
+ // save the to-be-copied width of the left most tile
+
+ int startw = curw;
+
+
+ // temporary variable for dimension of the to-be-copied region
+
+ int tmpw = copywidth;
+ int tmph = copyheight;
+
+
+ // offset of the first pixel of the tile to be copied; offset is
+ // relative to the upper left corner of the title
+
+ int x = srcX - startXTile;
+ int y = srcY - startYTile;
+
+
+ // determine the number of tiles in each direction that the
+ // image spans
+
+ numXTiles = (copywidth + x) / tilew;
+ numYTiles = (copyheight + y) / tileh;
+
+ if (((float)(copywidth + x ) % (float)tilew) > 0) {
+ numXTiles += 1;
+ }
+
+ if (((float)(copyheight + y ) % (float)tileh) > 0) {
+ numYTiles += 1;
+ }
+
+/*
+ System.out.println("-----------------------------------------------");
+ System.out.println("minTileX= " + minTileX + " minTileY= " + minTileY);
+ System.out.println("numXTiles= " + numXTiles + " numYTiles= " + numYTiles);
+ System.out.println("tilew= " + tilew + " tileh= " + tileh);
+ System.out.println("xoff= " + xoff + " yoff= " + yoff);
+ System.out.println("startXTile= " + startXTile + " startYTile= " + startYTile);
+ System.out.println("srcX= " + srcX + " srcY= " + srcY);
+ System.out.println("copywidth= " + copywidth + " copyheight= " + copyheight);
+
+ System.out.println("rminTileX= " + ri.getMinTileX() + " rminTileY= " + ri.getMinTileY());
+ System.out.println("rnumXTiles= " + ri.getNumXTiles() + " rnumYTiles= " + ri.getNumYTiles());
+*/
+
+ if ((!yUp && usedByTexture) ||
+ (yUp && !usedByTexture)) {
+
+ // destination buffer offset
+
+ tileStart = ((height - dstY - 1) * width + dstX)
+ * imageBytesPerPixel;
+
+ sign = -1;
+ dstLineBytes = -lineBytes;
+ } else {
+
+ // destination buffer offset
+
+ tileStart = (dstY * width + dstX) * imageBytesPerPixel;
+ sign = 1;
+ dstLineBytes = lineBytes;
+ }
+
+/*
+ System.out.println("tileStart= " + tileStart + " dstLineBytes= " + dstLineBytes);
+ System.out.println("startw= " + startw);
+*/
+
+ // allocate memory for a pixel
+
+ ras = ri.getTile(minTileX,minTileY);
+ pixel = getDataElementBuffer(ras);
+
+ if (formatMatches(imageFormat, ri)) {
+ byte[] src;
+ int srcOffset, dstOffset;
+ int tileLineBytes= tilew * imageBytesPerPixel;
+ int copyBytes;
+
+ for (n = minTileY; n < minTileY+numYTiles; n++) {
+
+ dstBegin = tileStart; // destination buffer offset
+ tmpw = copywidth; // reset the width to be copied
+ curw = startw; // reset the width to be copied of
+ // the left most tile
+ x = srcX - startXTile; // reset the starting x offset of
+ // the left most tile
+
+ for (m = minTileX; m < minTileX+numXTiles; m++) {
+
+ // retrieve the raster for the next tile
+ ras = ri.getTile(m,n);
+ src = ((DataBufferByte)ras.getDataBuffer()).getData();
+
+ srcOffset = (y * tilew + x) * imageBytesPerPixel;
+ dstOffset = dstBegin;
+
+ copyBytes = curw * imageBytesPerPixel;
+
+ //System.out.println("curh = "+curh+" curw = "+curw);
+ //System.out.println("x = "+x+" y = "+y);
+
+ for (h = 0; h < curh; h++) {
+ System.arraycopy(src, srcOffset, image, dstOffset,
+ copyBytes);
+ srcOffset += tileLineBytes;
+ dstOffset += dstLineBytes;
+ }
+
+ // advance the destination buffer offset
+ dstBegin += curw * imageBytesPerPixel;
+
+ // move to the next tile in x direction
+ x = 0;
+
+ // determine the width of copy region of the next tile
+
+ tmpw -= curw;
+ if (tmpw < tilew) {
+ curw = tmpw;
+ } else {
+ curw = tilew;
+ }
+ }
+
+
+ // we are done copying an array of tiles in the x direction
+ // advance the tileStart offset
+
+ tileStart += width * imageBytesPerPixel * curh * sign;
+
+
+ // move to the next set of tiles in y direction
+ y = 0;
+
+ // determine the height of copy region for the next set
+ // of tiles
+ tmph -= curh;
+ if (tmph < tileh) {
+ curh = tmph;
+ } else {
+ curh = tileh;
+ }
+ }
+ return;
+ }
+
+ switch(format) {
+ case ImageComponent.FORMAT_RGBA8:
+ case ImageComponent.FORMAT_RGB5_A1:
+ case ImageComponent.FORMAT_RGBA4: {
+ // System.out.println("Case 1: byReference = "+byReference);
+ for (n = minTileY; n < minTileY+numYTiles; n++) {
+
+ dstBegin = tileStart; // destination buffer offset
+ tmpw = copywidth; // reset the width to be copied
+ curw = startw; // reset the width to be copied of
+ // the left most tile
+ x = srcX - startXTile; // reset the starting x offset of
+ // the left most tile
+
+ for (m = minTileX; m < minTileX+numXTiles; m++) {
+
+ // retrieve the raster for the next tile
+ ras = ri.getTile(m,n);
+
+ j = dstBegin;
+ offset = 0;
+
+ //System.out.println("curh = "+curh+" curw = "+curw);
+ //System.out.println("x = "+x+" y = "+y);
+
+ for (h = y; h < (y + curh); h++) {
+ // System.out.println("j = "+j);
+ for (w = x; w < (x + curw); w++) {
+ ras.getDataElements(w, h, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ image[j++] = (byte)cm.getGreen(pixel);
+ image[j++] = (byte)cm.getBlue(pixel);
+ image[j++] = (byte)cm.getAlpha(pixel);
+ }
+ offset += dstLineBytes;
+ j = dstBegin + offset;
+ }
+
+ // advance the destination buffer offset
+ dstBegin += curw * imageBytesPerPixel;
+
+ // move to the next tile in x direction
+ x = 0;
+
+ // determine the width of copy region of the next tile
+
+ tmpw -= curw;
+ if (tmpw < tilew) {
+ curw = tmpw;
+ } else {
+ curw = tilew;
+ }
+ }
+
+
+ // we are done copying an array of tiles in the x direction
+ // advance the tileStart offset
+
+ tileStart += width * imageBytesPerPixel * curh * sign;
+
+
+ // move to the next set of tiles in y direction
+ y = 0;
+
+ // determine the height of copy region for the next set
+ // of tiles
+ tmph -= curh;
+ if (tmph < tileh) {
+ curh = tmph;
+ } else {
+ curh = tileh;
+ }
+ }
+ }
+ break;
+ case ImageComponent.FORMAT_RGB8:
+ case ImageComponent.FORMAT_RGB5:
+ case ImageComponent.FORMAT_RGB4:
+ case ImageComponent.FORMAT_R3_G3_B2: {
+ for (n = minTileY; n < minTileY+numYTiles; n++) {
+
+ dstBegin = tileStart; // destination buffer offset
+ tmpw = copywidth; // reset the width to be copied
+ curw = startw; // reset the width to be copied of
+ // the left most tile
+ x = srcX - startXTile; // reset the starting x offset of
+ // the left most tile
+
+ for (m = minTileX; m < minTileX+numXTiles; m++) {
+
+ // retrieve the raster for the next tile
+ ras = ri.getTile(m,n);
+
+ j = dstBegin;
+ offset = 0;
+
+ //System.out.println("curh = "+curh+" curw = "+curw);
+ //System.out.println("x = "+x+" y = "+y);
+
+ for (h = y; h < (y + curh); h++) {
+ // System.out.println("j = "+j);
+ for (w = x; w < (x + curw); w++) {
+ ras.getDataElements(w, h, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ image[j++] = (byte)cm.getGreen(pixel);
+ image[j++] = (byte)cm.getBlue(pixel);
+ image[j++] = (byte)255;
+ }
+ offset += dstLineBytes;
+ j = dstBegin + offset;
+ }
+
+ // advance the destination buffer offset
+ dstBegin += curw * imageBytesPerPixel;
+
+ // move to the next tile in x direction
+ x = 0;
+
+ // determine the width of copy region of the next tile
+
+ tmpw -= curw;
+ if (tmpw < tilew) {
+ curw = tmpw;
+ } else {
+ curw = tilew;
+ }
+ }
+
+
+ // we are done copying an array of tiles in the x direction
+ // advance the tileStart offset
+
+ tileStart += width * imageBytesPerPixel * curh * sign;
+
+
+ // move to the next set of tiles in y direction
+ y = 0;
+
+ // determine the height of copy region for the next set
+ // of tiles
+ tmph -= curh;
+ if (tmph < tileh) {
+ curh = tmph;
+ } else {
+ curh = tileh;
+ }
+ }
+ }
+ break;
+ case ImageComponent.FORMAT_LUM8_ALPHA8:
+ case ImageComponent.FORMAT_LUM4_ALPHA4: {
+ for (n = minTileY; n < minTileY+numYTiles; n++) {
+
+ dstBegin = tileStart; // destination buffer offset
+ tmpw = copywidth; // reset the width to be copied
+ curw = startw; // reset the width to be copied of
+ // the left most tile
+ x = srcX - startXTile; // reset the starting x offset of
+ // the left most tile
+
+ for (m = minTileX; m < minTileX+numXTiles; m++) {
+
+ // retrieve the raster for the next tile
+ ras = ri.getTile(m,n);
+
+ j = dstBegin;
+ offset = 0;
+
+ //System.out.println("curh = "+curh+" curw = "+curw);
+ //System.out.println("x = "+x+" y = "+y);
+
+ for (h = y; h < (y + curh); h++) {
+ // System.out.println("j = "+j);
+ for (w = x; w < (x + curw); w++) {
+ ras.getDataElements(w, h, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ image[j++] = (byte)cm.getAlpha(pixel);
+ }
+ offset += dstLineBytes;
+ j = dstBegin + offset;
+ }
+
+ // advance the destination buffer offset
+ dstBegin += curw * imageBytesPerPixel;
+
+ // move to the next tile in x direction
+ x = 0;
+
+ // determine the width of copy region of the next tile
+
+ tmpw -= curw;
+ if (tmpw < tilew) {
+ curw = tmpw;
+ } else {
+ curw = tilew;
+ }
+ }
+
+
+ // we are done copying an array of tiles in the x direction
+ // advance the tileStart offset
+
+ tileStart += width * imageBytesPerPixel * curh * sign;
+
+
+ // move to the next set of tiles in y direction
+ y = 0;
+
+ // determine the height of copy region for the next set
+ // of tiles
+ tmph -= curh;
+ if (tmph < tileh) {
+ curh = tmph;
+ } else {
+ curh = tileh;
+ }
+ }
+ }
+ break;
+ case ImageComponent.FORMAT_CHANNEL8: {
+ for (n = minTileY; n < minTileY+numYTiles; n++) {
+
+ dstBegin = tileStart; // destination buffer offset
+ tmpw = copywidth; // reset the width to be copied
+ curw = startw; // reset the width to be copied of
+ // the left most tile
+ x = srcX - startXTile; // reset the starting x offset of
+ // the left most tile
+
+ for (m = minTileX; m < minTileX+numXTiles; m++) {
+
+ // retrieve the raster for the next tile
+ ras = ri.getTile(m,n);
+
+ j = dstBegin;
+ offset = 0;
+
+ //System.out.println("curh = "+curh+" curw = "+curw);
+ //System.out.println("x = "+x+" y = "+y);
+
+ for (h = y; h < (y + curh); h++) {
+ // System.out.println("j = "+j);
+ for (w = x; w < (x + curw); w++) {
+ ras.getDataElements(w, h, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ }
+ offset += dstLineBytes;
+ j = dstBegin + offset;
+ }
+
+ // advance the destination buffer offset
+ dstBegin += curw * imageBytesPerPixel;
+
+ // move to the next tile in x direction
+ x = 0;
+
+ // determine the width of copy region of the next tile
+
+ tmpw -= curw;
+ if (tmpw < tilew) {
+ curw = tmpw;
+ } else {
+ curw = tilew;
+ }
+ }
+
+
+ // we are done copying an array of tiles in the x direction
+ // advance the tileStart offset
+
+ tileStart += width * imageBytesPerPixel * curh * sign;
+
+
+ // move to the next set of tiles in y direction
+ y = 0;
+
+ // determine the height of copy region for the next set
+ // of tiles
+ tmph -= curh;
+ if (tmph < tileh) {
+ curh = tmph;
+ } else {
+ curh = tileh;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ /**
+ * Copy entire image data from Buffered Image to
+ * ImageComponent's internal representation
+ */
+ final void copyImage(BufferedImage bi, byte[] image,
+ boolean usedByTexture, int depth,
+ int imageFormat, int imageBytesPerPixel) {
+ copyImage(bi, 0, 0, image, 0, 0, usedByTexture, depth,
+ width, height, imageFormat, imageBytesPerPixel);
+ }
+
+ /**
+ * Copy specified region of image data from Buffered Image to
+ * ImageComponent's internal representation
+ */
+ final void copyImage(BufferedImage bi, int srcX, int srcY,
+ byte[] image, int dstX, int dstY, boolean usedByTexture,
+ int depth, int copywidth, int copyheight,
+ int imageFormat, int imageBytesPerPixel) {
+
+ int w, h, i, j;
+ int rowBegin, // src begin row index
+ srcBegin, // src begin offset
+ dstBegin, // dst begin offset
+ rowInc, // row increment
+ // -1 --- ydown
+ // 1 --- yup
+ row;
+ Object pixel = null;
+
+ rowBegin = srcY;
+ rowInc = 1;
+
+ int dstBytesPerRow = width * imageBytesPerPixel; // bytes per row
+ // in dst image
+
+ if ((!yUp && usedByTexture) || (yUp && !usedByTexture)) {
+ dstBegin = (depth * width * height +
+ (height - dstY - 1) * width + dstX) *
+ imageBytesPerPixel;
+
+ dstBytesPerRow = - 1 * dstBytesPerRow;
+
+ } else {
+ dstBegin = (dstY * width + dstX) * imageBytesPerPixel;
+ }
+
+ // if the image format matches the format of the incoming
+ // buffered image, then do a straight copy, else do the
+ // format conversion while copying the data
+
+ if (formatMatches(imageFormat, bi)) {
+ byte[] byteData =
+ ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
+ int copyBytes = copywidth * imageBytesPerPixel;
+ int scanline = width * imageBytesPerPixel;
+
+ srcBegin = (rowBegin * width + srcX) * imageBytesPerPixel;
+ for (h = 0; h < copyheight; h++) {
+ System.arraycopy(byteData, srcBegin, image, dstBegin, copyBytes);
+ dstBegin += dstBytesPerRow;
+ srcBegin += scanline;
+ }
+ } else {
+
+ int biType = bi.getType();
+ if ((biType == BufferedImage.TYPE_INT_ARGB ||
+ biType == BufferedImage.TYPE_INT_RGB) &&
+ (format == ImageComponent.FORMAT_RGBA8 ||
+ format == ImageComponent.FORMAT_RGB8)) {
+
+ // optimized cases
+
+ int[] intData =
+ ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
+ int rowOffset = rowInc * width;
+ int intPixel;
+
+ srcBegin = rowBegin * width + srcX;
+
+ if (biType == BufferedImage.TYPE_INT_ARGB &&
+ format == ImageComponent.FORMAT_RGBA8) {
+ for (h = 0; h < copyheight; h++) {
+ i = srcBegin;
+ j = dstBegin;
+ for (w = 0; w < copywidth; w++, i++) {
+ intPixel = intData[i];
+ image[j++] = (byte)((intPixel >> 16) & 0xff);
+ image[j++] = (byte)((intPixel >> 8) & 0xff);
+ image[j++] = (byte)(intPixel & 0xff);
+ image[j++] = (byte)((intPixel >> 24) & 0xff);
+ }
+ srcBegin += rowOffset;
+ dstBegin += dstBytesPerRow;
+ }
+ } else { // format == ImageComponent.FORMAT_RGB8
+ for (h = 0; h < copyheight; h++) {
+ i = srcBegin;
+ j = dstBegin;
+ for (w = 0; w < copywidth; w++, i++) {
+ intPixel = intData[i];
+ image[j++] = (byte)((intPixel >> 16) & 0xff);
+ image[j++] = (byte)((intPixel >> 8) & 0xff);
+ image[j++] = (byte)(intPixel & 0xff);
+ image[j++] = (byte)255;
+ }
+ srcBegin += rowOffset;
+ dstBegin += dstBytesPerRow;
+ }
+ }
+
+ } else if ((biType == BufferedImage.TYPE_BYTE_GRAY) &&
+ (format == ImageComponent.FORMAT_CHANNEL8)) {
+
+ byte[] byteData =
+ ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
+ int rowOffset = rowInc * width;
+
+ j = dstBegin;
+ srcBegin = rowBegin * width + srcX;
+
+ for (h = 0; h < copyheight;
+ h++, j += width, srcBegin += rowOffset) {
+ System.arraycopy(byteData, srcBegin, image, j, copywidth);
+ }
+ } else {
+ // non-optimized cases
+
+ WritableRaster ras = bi.getRaster();
+ ColorModel cm = bi.getColorModel();
+ pixel = getDataElementBuffer(ras);
+
+ switch(format) {
+ case ImageComponent.FORMAT_RGBA8:
+ case ImageComponent.FORMAT_RGB5_A1:
+ case ImageComponent.FORMAT_RGBA4: {
+ for (row = rowBegin, h = 0;
+ h < copyheight; h++, row += rowInc) {
+ j = dstBegin;
+ for (w = srcX; w < (copywidth + srcX); w++) {
+ ras.getDataElements(w, row, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ image[j++] = (byte)cm.getGreen(pixel);
+ image[j++] = (byte)cm.getBlue(pixel);
+ image[j++] = (byte)cm.getAlpha(pixel);
+ }
+ dstBegin += dstBytesPerRow;
+ }
+ }
+ break;
+
+ case ImageComponent.FORMAT_RGB8:
+ case ImageComponent.FORMAT_RGB5:
+ case ImageComponent.FORMAT_RGB4:
+ case ImageComponent.FORMAT_R3_G3_B2: {
+ for (row = rowBegin, h = 0;
+ h < copyheight; h++, row += rowInc) {
+ j = dstBegin;
+ for (w = srcX; w < (copywidth + srcX); w++) {
+ ras.getDataElements(w, row, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ image[j++] = (byte)cm.getGreen(pixel);
+ image[j++] = (byte)cm.getBlue(pixel);
+ image[j++] = (byte)255;
+ }
+ dstBegin += dstBytesPerRow;
+ }
+ }
+ break;
+
+ case ImageComponent.FORMAT_LUM8_ALPHA8:
+ case ImageComponent.FORMAT_LUM4_ALPHA4: {
+ for (row = rowBegin, h = 0;
+ h < copyheight; h++, row += rowInc) {
+ j = dstBegin;
+ for (w = srcX; w < (copywidth + srcX); w++) {
+ ras.getDataElements(w, row, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ image[j++] = (byte)cm.getAlpha(pixel);
+ }
+ dstBegin += dstBytesPerRow;
+ }
+ }
+ break;
+
+ case ImageComponent.FORMAT_CHANNEL8: {
+ for (row = rowBegin, h = 0;
+ h < copyheight; h++, row += rowInc) {
+ j = dstBegin;
+ for (w = srcX; w < (copywidth + srcX); w++) {
+ ras.getDataElements(w, row, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ }
+ dstBegin += dstBytesPerRow;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+
+
+
+ final int getBytesStored(int f) {
+ int val = 0;
+ switch(f) {
+ case BYTE_RGBA:
+ val = 4;
+ break;
+ case BYTE_ABGR:
+ val = 4;
+ break;
+ case BYTE_GRAY:
+ val = 1;
+ break;
+ case USHORT_GRAY:
+ val = 2;
+ break;
+ case BYTE_LA:
+ val = 2;
+ break;
+ case BYTE_BGR:;
+ val = 3;
+ break;
+ case BYTE_RGB:;
+ val = 3;
+ break;
+ }
+ return val;
+ }
+
+
+ boolean is4ByteRGBAOr3ByteRGB(RenderedImage ri) {
+ boolean value = false;
+ int i;
+ int biType = getImageType(ri);
+ if (biType != BufferedImage.TYPE_CUSTOM)
+ return false;
+ ColorModel cm = ri.getColorModel();
+ ColorSpace cs = cm.getColorSpace();
+ SampleModel sm = ri.getSampleModel();
+ boolean isAlphaPre = cm.isAlphaPremultiplied();
+ int csType = cs.getType();
+ if ( csType == ColorSpace.TYPE_RGB) {
+ int numBands = sm.getNumBands();
+ if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
+ if (cm instanceof ComponentColorModel &&
+ sm instanceof PixelInterleavedSampleModel) {
+ PixelInterleavedSampleModel csm =
+ (PixelInterleavedSampleModel) sm;
+ int[] offs = csm.getBandOffsets();
+ ComponentColorModel ccm = (ComponentColorModel)cm;
+ int[] nBits = ccm.getComponentSize();
+ boolean is8Bit = true;
+ for (i=0; i < numBands; i++) {
+ if (nBits[i] != 8) {
+ is8Bit = false;
+ break;
+ }
+ }
+ if (is8Bit &&
+ offs[0] == 0 &&
+ offs[1] == 1 &&
+ offs[2] == 2) {
+ if (numBands == 3) {
+ if (format == ImageComponent.FORMAT_RGB)
+ value = true;
+ }
+ else if (offs[3] == 3 && !isAlphaPre) {
+ if (format == ImageComponent.FORMAT_RGBA)
+ value = true;
+ }
+ }
+ }
+ }
+ }
+ return value;
+ }
+
+ final int getImageType(RenderedImage ri) {
+ int imageType = BufferedImage.TYPE_CUSTOM;
+ int i;
+
+ if (ri instanceof BufferedImage) {
+ return ((BufferedImage)ri).getType();
+ }
+ ColorModel cm = ri.getColorModel();
+ ColorSpace cs = cm.getColorSpace();
+ SampleModel sm = ri.getSampleModel();
+ int csType = cs.getType();
+ boolean isAlphaPre = cm.isAlphaPremultiplied();
+ if ( csType != ColorSpace.TYPE_RGB) {
+ if (csType == ColorSpace.TYPE_GRAY &&
+ cm instanceof ComponentColorModel) {
+ if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
+ imageType = BufferedImage.TYPE_BYTE_GRAY;
+ } else if (sm.getDataType() == DataBuffer.TYPE_USHORT) {
+ imageType = BufferedImage.TYPE_USHORT_GRAY;
+ }
+ }
+ }
+ // RGB , only interested in BYTE ABGR and BGR for now
+ // all others will be copied to a buffered image
+ else {
+ int numBands = sm.getNumBands();
+ if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
+ if (cm instanceof ComponentColorModel &&
+ sm instanceof PixelInterleavedSampleModel) {
+ PixelInterleavedSampleModel csm =
+ (PixelInterleavedSampleModel) sm;
+ int[] offs = csm.getBandOffsets();
+ ComponentColorModel ccm = (ComponentColorModel)cm;
+ int[] nBits = ccm.getComponentSize();
+ boolean is8Bit = true;
+ for (i=0; i < numBands; i++) {
+ if (nBits[i] != 8) {
+ is8Bit = false;
+ break;
+ }
+ }
+ if (is8Bit &&
+ offs[0] == numBands-1 &&
+ offs[1] == numBands-2 &&
+ offs[2] == numBands-3) {
+ if (numBands == 3) {
+ imageType = BufferedImage.TYPE_3BYTE_BGR;
+ }
+ else if (offs[3] == 0) {
+ imageType = (isAlphaPre
+ ? BufferedImage.TYPE_4BYTE_ABGR_PRE
+ : BufferedImage.TYPE_4BYTE_ABGR);
+ }
+ }
+ }
+ }
+ }
+ return imageType;
+ }
+
+
+
+
+ /**
+ * Retrieves the bufferedImage at the specified depth level
+ */
+ final void retrieveBufferedImage(int depth) {
+
+ // create BufferedImage if one doesn't exist
+ if (bImage[depth] == null) {
+ if (format == ImageComponent.FORMAT_RGBA ||
+ format == ImageComponent.FORMAT_RGBA4 ||
+ format == ImageComponent.FORMAT_RGB5_A1 ||
+ format == ImageComponent.FORMAT_LUM4_ALPHA4 ||
+ format == ImageComponent.FORMAT_LUM8_ALPHA8) {
+ bImage[depth] = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_ARGB);
+ }
+ else
+ bImage[depth] = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_RGB);
+ }
+
+ if (usedByTexture || !usedByRaster) {
+ copyToBufferedImage(imageYup, depth, true);
+ } else {
+ copyToBufferedImage(imageYdown[0], depth, false);
+ }
+ imageDirty[depth] = false;
+
+ }
+
+ /**
+ * Copy Image from RGBA to the user defined bufferedImage
+ */
+ final void copyBufferedImageWithFormatConversion(boolean usedByTexture, int depth) {
+ int w, h, i, j;
+ int dstBegin, dstInc, dstIndex, dstIndexInc;
+ // Note that if a copy has been made, then its always a bufferedImage
+ // and not a renderedImage
+ BufferedImage bi = (BufferedImage)bImage[depth];
+ int biType = bi.getType();
+ byte[] buf;
+
+ // convert from Ydown to Yup for texture
+ if (!yUp) {
+ if (usedByTexture == true) {
+ dstInc = -1 * width;
+ dstBegin = (height - 1) * width;
+ dstIndex = height -1;
+ dstIndexInc = -1;
+ buf = imageYup;
+ } else {
+ dstInc = width;
+ dstBegin = 0;
+ dstIndex = 0;
+ dstIndexInc = 1;
+ buf = imageYdown[0];
+ }
+ }
+ else {
+ if (usedByTexture == true) {
+ dstInc = width;
+ dstBegin = 0;
+ dstIndex = 0;
+ dstIndexInc = 1;
+ buf = imageYup;
+ }
+ else {
+ dstInc = -1 * width;
+ dstBegin = (height - 1) * width;
+ dstIndex = height -1;
+ dstIndexInc = -1;
+ buf = imageYdown[0];
+ }
+ }
+
+ switch (biType) {
+ case BufferedImage.TYPE_INT_ARGB:
+ int[] intData =
+ ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
+ // Multiply by 4 to get the byte incr and start point
+ j = 0;
+ for(h = 0; h < height; h++, dstBegin += dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j+=4, i++) {
+ intData[i] = (((buf[j+3] &0xff) << 24) | // a
+ ((buf[j] &0xff) << 16) | // r
+ ((buf[j+1] &0xff) << 8) | // g
+ (buf[j+2] & 0xff)); // b
+
+
+ }
+ }
+ break;
+
+ case BufferedImage.TYPE_INT_RGB:
+ intData =
+ ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
+ // Multiply by 4 to get the byte incr and start point
+ j = 0;
+ for(h = 0; h < height; h++, dstBegin += dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j+=4, i++) {
+ intData[i] = (0xff000000 | // a
+ ((buf[j] &0xff) << 16) | // r
+ ((buf[j+1] &0xff) << 8) | // g
+ (buf[j+2] & 0xff)); // b
+
+
+ }
+ }
+ break;
+
+ case BufferedImage.TYPE_4BYTE_ABGR:
+ byte[] byteData =
+ ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
+ // Multiply by 4 to get the byte incr and start point
+ j = 0;
+ for(h = 0; h < height; h++, dstBegin += (dstInc << 2)) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j+=4) {
+
+ byteData[i++] = buf[j+3]; // a
+ byteData[i++] = buf[j+2]; // b
+ byteData[i++] = buf[j+1];// g
+ byteData[i++] = buf[j]; // r
+ }
+ }
+ break;
+ case BufferedImage.TYPE_INT_BGR:
+ intData =
+ ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
+ // Multiply by 4 to get the byte incr and start point
+ j = 0;
+
+ for(h = 0; h < height; h++, dstBegin += dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j+=4, i++) {
+ intData[i] = (0xff000000 | // a
+ ((buf[j] &0xff) ) | // r
+ ((buf[j+1] &0xff) << 8) | // g
+ (buf[j+2] & 0xff)<< 16); // b
+
+
+ }
+ }
+ break;
+ case BufferedImage.TYPE_BYTE_GRAY:
+ byteData =
+ ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
+ j = 0;
+ for( h = 0; h < height; h++, dstBegin += dstInc) {
+ System.arraycopy(byteData, dstBegin, buf, j, width);
+ j += width;
+ }
+ break;
+ case BufferedImage.TYPE_USHORT_GRAY:
+ int pixel;
+ j = 0;
+ short[] shortData =
+ ((DataBufferShort)bi.getRaster().getDataBuffer()).getData();
+ // Multiply by 4 to get the byte incr and start point
+ for(h = 0; h < height; h++, dstBegin+= dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, i++, j++) {
+ shortData[i] = (short)buf[j];
+ }
+ }
+ break;
+
+ default:
+ j = 0;
+ for( h = 0; h < height; h++, dstIndex += dstIndexInc) {
+ i = dstIndex;
+ for (w = 0; w < width; w++, j+=4) {
+ pixel = (((buf[j+3] &0xff) << 24) | // a
+ ((buf[j] &0xff) << 16) | // r
+ ((buf[j+1] &0xff) << 8) | // g
+ (buf[j+2] & 0xff)); // b
+ bi.setRGB(w, i, pixel);
+
+ }
+ }
+ break;
+ }
+
+ }
+
+ /**
+ * Copy image data from ImageComponent's internal representation
+ * to Buffered Image
+ */
+ final void copyToBufferedImage(byte[] buf, int depth,
+ boolean usedByTexture) {
+
+ int w, h, i, j;
+ int dstBegin, dstInc, srcBegin;
+
+
+ // convert from Ydown to Yup for texture
+ if (!yUp) {
+ if (usedByTexture == true) {
+ srcBegin = depth * width * height * bytesPerYupPixelStored;
+ dstInc = -1 * width;
+ dstBegin = (height - 1) * width;
+ } else {
+ srcBegin = 0;
+ dstInc = width;
+ dstBegin = 0;
+ }
+ }
+ else {
+ if (usedByTexture == true) {
+ srcBegin = 0;
+ dstInc = width;
+ dstBegin = 0;
+ }
+ else {
+ srcBegin = depth * width * height * bytesPerYdownPixelStored;
+ dstInc = -1 * width;
+ dstBegin = (height - 1) * width;
+ }
+ }
+
+ // Note that if a copy has been made, then its always a bufferedImage
+ // and not a renderedImage
+ int[] intData = ((DataBufferInt)
+ ((BufferedImage)bImage[depth]).getRaster().getDataBuffer()).getData();
+
+ switch(format) {
+ case ImageComponent.FORMAT_RGBA8:
+ case ImageComponent.FORMAT_RGB5_A1:
+ case ImageComponent.FORMAT_RGBA4:
+
+ for (j = srcBegin, h = 0; h < height; h++, dstBegin += dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j+=4, i++) {
+ intData[i] = ((buf[j+3] & 0xff) << 24) |
+ ((buf[j] & 0xff) << 16) |
+ ((buf[j+1] & 0xff) << 8) |
+ (buf[j+2] & 0xff);
+ }
+ }
+ break;
+
+
+ case ImageComponent.FORMAT_RGB8:
+ case ImageComponent.FORMAT_RGB5:
+ case ImageComponent.FORMAT_RGB4:
+ case ImageComponent.FORMAT_R3_G3_B2:
+ for (j = srcBegin, h = 0; h < height; h++, dstBegin += dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j+=4, i++) {
+ intData[i] = ((buf[j] & 0xff) << 16) |
+ ((buf[j+1] & 0xff) << 8) |
+ (buf[j+2] & 0xff);
+ }
+ }
+ break;
+
+ case ImageComponent.FORMAT_LUM8_ALPHA8:
+ case ImageComponent.FORMAT_LUM4_ALPHA4:
+ for (j = srcBegin, h = 0; h < height; h++, dstBegin += dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j+=2, i++) {
+ intData[i] = ((buf[j+1] & 0xff) << 24) |
+ ((buf[j] & 0xff) << 16);
+ }
+ }
+ break;
+
+ case ImageComponent.FORMAT_CHANNEL8:
+ for (j = srcBegin, h = 0; h < height; h++, dstBegin += dstInc) {
+ i = dstBegin;
+ for (w = 0; w < width; w++, j++, i++) {
+ intData[i] = ((buf[j] & 0xff) << 16);
+ }
+ }
+ break;
+ }
+ }
+
+ Object getData(DataBuffer buffer) {
+ Object data = null;
+ switch (buffer.getDataType()) {
+ case DataBuffer.TYPE_BYTE:
+ data = ((DataBufferByte)buffer).getData();
+ break;
+ case DataBuffer.TYPE_INT:
+ data = ((DataBufferInt)buffer).getData();
+ break;
+ case DataBuffer.TYPE_SHORT:
+ data = ((DataBufferShort)buffer).getData();
+ break;
+ }
+ return data;
+ }
+
+ final void setByReference(boolean byReference) {
+ this.byReference = byReference;
+ }
+
+ final boolean isByReference() {
+ return byReference;
+ }
+
+ final void setYUp( boolean yUp) {
+ this.yUp = yUp;
+ }
+
+ final boolean isYUp() {
+ return yUp;
+ }
+
+ // Add a user to the userList
+ synchronized void addUser(NodeComponentRetained node) {
+ userList.add(node);
+ }
+
+ // Add a user to the userList
+ synchronized void removeUser(NodeComponentRetained node) {
+ int i = userList.indexOf(node);
+ if (i >= 0) {
+ userList.remove(i);
+ }
+ }
+
+ /**
+ * ImageComponent object doesn't really have mirror object.
+ * But it's using the updateMirrorObject interface to propagate
+ * the changes to the users
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ //System.out.println("ImageComponent.updateMirrorObject");
+
+ Object user;
+
+ if (((component & IMAGE_CHANGED) != 0) ||
+ ((component & SUBIMAGE_CHANGED) != 0)) {
+ synchronized(userList) {
+ for (int i = userList.size()-1; i >=0; i--) {
+ user = userList.get(i);
+ if (user != null) {
+ if (user instanceof TextureRetained) {
+ ((TextureRetained)user).notifyImageComponentImageChanged(this, (ImageComponentUpdateInfo)value);
+ } else if (user instanceof RasterRetained) {
+ ((RasterRetained)user).notifyImageComponentImageChanged(this, (ImageComponentUpdateInfo)value);
+ } else if (user instanceof BackgroundRetained) {
+ ((BackgroundRetained)user).notifyImageComponentImageChanged(this, (ImageComponentUpdateInfo)value);
+ }
+ }
+ }
+ }
+
+ // return the subimage update info to the free list
+ if (value != null) {
+ VirtualUniverse.mc.addFreeImageUpdateInfo(
+ (ImageComponentUpdateInfo)value);
+ }
+ }
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES |
+ J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.IMAGE_COMPONENT_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void handleFrequencyChange(int bit) {
+ if (bit == ImageComponent.ALLOW_IMAGE_WRITE) {
+ setFrequencyChangeMask(ImageComponent.ALLOW_IMAGE_WRITE, 0x1);
+ }
+ }
+
+ static Object getDataElementBuffer(java.awt.image.Raster ras) {
+ int nc = ras.getNumDataElements();
+
+ switch (ras.getTransferType()) {
+ case DataBuffer.TYPE_INT:
+ return new int[nc];
+ case DataBuffer.TYPE_BYTE:
+ return new byte[nc];
+ case DataBuffer.TYPE_USHORT:
+ case DataBuffer.TYPE_SHORT:
+ return new short[nc];
+ case DataBuffer.TYPE_FLOAT:
+ return new float[nc];
+ case DataBuffer.TYPE_DOUBLE:
+ return new double[nc];
+ }
+ // Should not happen
+ return null;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ImageComponentUpdateInfo.java b/src/classes/share/javax/media/j3d/ImageComponentUpdateInfo.java
new file mode 100644
index 0000000..e58caa1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ImageComponentUpdateInfo.java
@@ -0,0 +1,30 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Image Component update information for the users
+ */
+class ImageComponentUpdateInfo extends Object {
+
+ int x = 0;
+ int y = 0;
+ int z = 0;
+ int width = 0;
+ int height = 0;
+ int updateMask = 0; // which resources need to be updated
+ // canvas or renderer
+ boolean entireImage = false;// true if the entire image needs to be updated
+ // then none of the dimension info is to be
+ // applied.
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryArray.java b/src/classes/share/javax/media/j3d/IndexedGeometryArray.java
new file mode 100644
index 0000000..8020849
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryArray.java
@@ -0,0 +1,883 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The IndexedGeometryArray object contains separate integer arrays
+ * that index into the arrays of positional coordinates, colors,
+ * normals, and texture coordinates. These index arrays specify how
+ * vertices are connected to form geometry primitives. This class is
+ * extended to create the various indexed primitive types (e.g.,
+ * lines, triangle strips, etc.).
+ */
+
+public abstract class IndexedGeometryArray extends GeometryArray {
+
+ // non-public, no parameter constructor
+ IndexedGeometryArray() {}
+
+ /**
+ * Specifies that this IndexedGeometryArray allows reading the array of
+ * coordinate indices.
+ */
+ public static final int
+ ALLOW_COORDINATE_INDEX_READ = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_COORDINATE_INDEX_READ;
+
+ /**
+ * Specifies that this IndexedGeometryArray allows writing the array of
+ * coordinate indices.
+ */
+ public static final int
+ ALLOW_COORDINATE_INDEX_WRITE = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_COORDINATE_INDEX_WRITE;
+
+ /**
+ * Specifies that this IndexedGeometryArray allows reading the array of
+ * color indices.
+ */
+ public static final int
+ ALLOW_COLOR_INDEX_READ = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_COLOR_INDEX_READ;
+
+ /**
+ * Specifies that this IndexedGeometryArray allows writing the array of
+ * color indices.
+ */
+ public static final int
+ ALLOW_COLOR_INDEX_WRITE = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_COLOR_INDEX_WRITE;
+
+ /**
+ * Specifies that this IndexedGeometryArray allows reading the array of
+ * normal indices.
+ */
+ public static final int
+ ALLOW_NORMAL_INDEX_READ = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_NORMAL_INDEX_READ;
+
+ /**
+ * Specifies that this IndexedGeometryArray allows writing the array of
+ * normal indices.
+ */
+ public static final int
+ ALLOW_NORMAL_INDEX_WRITE = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_NORMAL_INDEX_WRITE;
+
+ /**
+ * Specifies that this IndexedGeometryArray allows reading the array of
+ * texture coordinate indices.
+ */
+ public static final int
+ ALLOW_TEXCOORD_INDEX_READ = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_TEXCOORD_INDEX_READ;
+
+ /**
+ * Specifies that this IndexedGeometryArray allows writing the array of
+ * texture coordinate indices.
+ */
+ public static final int
+ ALLOW_TEXCOORD_INDEX_WRITE = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_TEXCOORD_INDEX_WRITE;
+
+ /**
+ * Constructs an empty IndexedGeometryArray object with the specified
+ * number of vertices, vertex format, and number of indices.
+ * Defaults are used for all other parameters. The default values
+ * are as follows:
+ *
+ * <ul>
+ * validIndexCount : indexCount<br>
+ * initialIndexIndex : 0<br>
+ * all index array values : 0<br>
+ * </ul>
+ *
+ * @param vertexCount the number of vertex elements in this
+ * IndexedGeometryArray
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or
+ * TEXTURE_COORDINATE_4, to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ */
+ public IndexedGeometryArray(int vertexCount,
+ int vertexFormat,
+ int indexCount) {
+ super(vertexCount, vertexFormat);
+ ((IndexedGeometryArrayRetained)this.retained).createIndexedGeometryArrayData(indexCount);
+ }
+
+ /**
+ * Constructs an empty IndexedGeometryArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and number of indices.
+ * Defaults are used for all other parameters.
+ *
+ * @param vertexCount the number of vertex elements in this
+ * IndexedGeometryArray<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D , 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedGeometryArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount) {
+ super(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap);
+ ((IndexedGeometryArrayRetained)this.retained).createIndexedGeometryArrayData(indexCount);
+ }
+
+ /**
+ * Gets number of indices for this IndexedGeometryArray.
+ * @return indexCount the number of indices
+ */
+ public int getIndexCount(){
+ if (isLiveOrCompiled())
+ if(!this.getCapability(GeometryArray.ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray0"));
+
+ return ((IndexedGeometryArrayRetained)this.retained).getIndexCount();
+ }
+
+ /**
+ * Sets the valid index count for this IndexedGeometryArray object.
+ * This count specifies the number of indexed vertices actually used
+ * in rendering or other operations such as picking and collision.
+ * This attribute is initialized to <code>indexCount</code>.
+ *
+ * @param validIndexCount the new valid index count.
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @exception IllegalArgumentException if either of the following is true:
+ * <ul>
+ * <code>validIndexCount < 0</code>, or<br>
+ * <code>initialIndexIndex + validIndexCount > indexCount</code><br>
+ * </ul>
+ *
+ * @exception ArrayIndexOutOfBoundsException if any element in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the index array associated with any of the enabled vertex
+ * components (coord, color, normal, texcoord) is out of range.
+ * An element is out of range if it is less than 0 or is greater
+ * than or equal to the number of vertices actually defined for
+ * the particular component's array.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setValidIndexCount(int validIndexCount) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray16"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setValidIndexCount(validIndexCount);
+ }
+
+ /**
+ * Gets the valid index count for this IndexedGeometryArray
+ * object. For geometry strip primitives (subclasses of
+ * IndexedGeometryStripArray), the valid index count is defined
+ * to be the sum of the stripIndexCounts array.
+ *
+ * @return the current valid index count
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getValidIndexCount() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray17"));
+
+ return ((IndexedGeometryArrayRetained)this.retained).getValidIndexCount();
+ }
+
+ /**
+ * Sets the initial index index for this IndexedGeometryArray object.
+ * This index specifies the first index within this indexed geometry
+ * array that is actually used in rendering or other operations
+ * such as picking and collision. This attribute is initialized
+ * to 0.
+ *
+ * @param initialIndexIndex the new initial index index.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ * @exception IllegalArgumentException if either of the following is true:
+ * <ul>
+ * <code>initialIndexIndex < 0</code>, or<br>
+ * <code>initialIndexIndex + validIndexCount > indexCount</code><br>
+ * </ul>
+ *
+ * @exception ArrayIndexOutOfBoundsException if any element in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * in the index array associated with any of the enabled vertex
+ * components (coord, color, normal, texcoord) is out of range.
+ * An element is out of range if it is less than 0 or is greater
+ * than or equal to the number of vertices actually defined for
+ * the particular component's array.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setInitialIndexIndex(int initialIndexIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray18"));
+
+ if (initialIndexIndex < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray20"));
+
+
+ ((IndexedGeometryArrayRetained)this.retained).setInitialIndexIndex(initialIndexIndex);
+ }
+
+ /**
+ * Gets the initial index index for this IndexedGeometryArray object.
+ * @return the current initial index index
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this object is part of a live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getInitialIndexIndex() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray19"));
+
+ return ((IndexedGeometryArrayRetained)this.retained).getInitialIndexIndex();
+
+ }
+
+ /**
+ * This method is not supported for indexed geometry arrays.
+ * Indexed primitives use an array of indices to determine how
+ * to access the vertex array.
+ * The initialIndexIndex attribute can be used to set the starting
+ * index within the index arrays.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setInitialVertexIndex(int initialVertexIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported for indexed geometry arrays.
+ * Indexed primitives use an array of indices to determine how
+ * to access the vertex array.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setInitialCoordIndex(int initialCoordIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported for indexed geometry arrays.
+ * Indexed primitives use an array of indices to determine how
+ * to access the vertex array.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setInitialColorIndex(int initialColorIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported for indexed geometry arrays.
+ * Indexed primitives use an array of indices to determine how
+ * to access the vertex array.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setInitialNormalIndex(int initialNormalIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported for indexed geometry arrays.
+ * Indexed primitives use an array of indices to determine how
+ * to access the vertex array.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setInitialTexCoordIndex(int texCoordSet,
+ int initialTexCoordIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported for indexed geometry arrays.
+ * Indexed primitives use an array of indices to determine how
+ * to access the vertex array.
+ * The validIndexCount attribute can be used to set the number of
+ * valid indexed vertices rendered.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setValidVertexCount(int validVertexCount) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Sets the coordinate index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param coordinateIndex the new coordinate index
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is less than 0
+ * or is greater than or equal to indexCount
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * and the specified coordinateIndex is out of range. The
+ * coordinateIndex is out of range if it is less than 0 or is
+ * greater than or equal to the number of vertices actually
+ * defined for the coordinate array.
+ */
+ public void setCoordinateIndex(int index, int coordinateIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray1"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setCoordinateIndex(index, coordinateIndex);
+ }
+
+ /**
+ * Sets the coordinate indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param coordinateIndices an array of coordinate indices
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is less than 0
+ * or is greater than or equal to indexCount
+ *
+ * @exception ArrayIndexOutOfBoundsException if any element of the
+ * coordinateIndices array whose destination position is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * is out of range. An element is out of range if it is less than 0
+ * or is greater than or equal to the number of vertices actually
+ * defined for the coordinate array.
+ */
+ public void setCoordinateIndices(int index, int coordinateIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray1"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setCoordinateIndices(index, coordinateIndices);
+ }
+
+ /**
+ * Sets the color index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param colorIndex the new color index
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is less than 0
+ * or is greater than or equal to indexCount
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * and the specified colorIndex is out of range. The
+ * colorIndex is out of range if it is less than 0 or is
+ * greater than or equal to the number of vertices actually
+ * defined for the color array.
+ */
+ public void setColorIndex(int index, int colorIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray3"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setColorIndex(index, colorIndex);
+ }
+
+ /**
+ * Sets the color indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param colorIndices an array of color indices
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is less than 0
+ * or is greater than or equal to indexCount
+ *
+ * @exception ArrayIndexOutOfBoundsException if any element of the
+ * colorIndices array whose destination position is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * is out of range. An element is out of range if it is less than 0
+ * or is greater than or equal to the number of vertices actually
+ * defined for the color array.
+ */
+ public void setColorIndices(int index, int colorIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray3"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setColorIndices(index, colorIndices);
+ }
+
+ /**
+ * Sets the normal index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param normalIndex the new normal index
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is less than 0
+ * or is greater than or equal to indexCount
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * and the specified normalIndex is out of range. The
+ * normalIndex is out of range if it is less than 0 or is
+ * greater than or equal to the number of vertices actually
+ * defined for the normal array.
+ */
+ public void setNormalIndex(int index, int normalIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray5"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setNormalIndex(index, normalIndex);
+ }
+
+ /**
+ * Sets the normal indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param normalIndices an array of normal indices
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is less than 0
+ * or is greater than or equal to indexCount
+ *
+ * @exception ArrayIndexOutOfBoundsException if any element of the
+ * normalIndices array whose destination position is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * is out of range. An element is out of range if it is less than 0
+ * or is greater than or equal to the number of vertices actually
+ * defined for the normal array.
+ */
+ public void setNormalIndices(int index, int normalIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray5"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setNormalIndices(index, normalIndices);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinateIndex(int texCoordSet, ...)</code>
+ */
+ public void setTextureCoordinateIndex(int index, int texCoordIndex) {
+ setTextureCoordinateIndex(0, index, texCoordIndex);
+ }
+
+ /**
+ * Sets the texture coordinate index associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index the vertex index
+ * @param texCoordIndex the new texture coordinate index
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if neither of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception ArrayIndexOutOfBoundsException if index is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * and the specified texCoordIndex is out of range. The
+ * texCoordIndex is out of range if it is less than 0 or is
+ * greater than or equal to the number of vertices actually
+ * defined for the texture coordinate array.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinateIndex(int texCoordSet,
+ int index,
+ int texCoordIndex) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray7"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setTextureCoordinateIndex(texCoordSet, index, texCoordIndex);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setTextureCoordinateIndices(int texCoordSet, ...)</code>
+ */
+ public void setTextureCoordinateIndices(int index, int texCoordIndices[]) {
+ setTextureCoordinateIndices(0, index, texCoordIndices);
+ }
+
+ /**
+ * Sets the texture coordinate indices associated with the vertices
+ * starting at the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index the vertex index
+ * @param texCoordIndices an array of texture coordinate indices
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if neither of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @exception ArrayIndexOutOfBoundsException if any element of the
+ * texCoordIndices array whose destination position is in the range
+ * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+ * is out of range. An element is out of range if it is less than 0
+ * or is greater than or equal to the number of vertices actually
+ * defined for the texture coordinate array.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureCoordinateIndices(int texCoordSet,
+ int index,
+ int texCoordIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray7"));
+
+ ((IndexedGeometryArrayRetained)this.retained).setTextureCoordinateIndices(texCoordSet, index, texCoordIndices);
+ }
+
+ /**
+ * Retrieves the coordinate index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @return the coordinate index
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getCoordinateIndex(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray9"));
+
+ return ((IndexedGeometryArrayRetained)this.retained).getCoordinateIndex(index);
+ }
+
+ /**
+ * Retrieves the coordinate indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param coordinateIndices array that will receive the coordinate indices
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getCoordinateIndices(int index, int coordinateIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray9"));
+
+ ((IndexedGeometryArrayRetained)this.retained).getCoordinateIndices(index, coordinateIndices);
+ }
+
+ /**
+ * Retrieves the color index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @return the color index
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getColorIndex(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray11"));
+
+ return ((IndexedGeometryArrayRetained)this.retained).getColorIndex(index);
+ }
+
+ /**
+ * Retrieves the color indices associated with the vertices starting at
+ * the specified index for this object. The color indicies are
+ * copied into the specified array. The array must be large enough
+ * to hold all of the indices.
+ * @param index the vertex index
+ * @param colorIndices array that will receive the color indices
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getColorIndices(int index, int colorIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray11"));
+
+ ((IndexedGeometryArrayRetained)this.retained).getColorIndices(index, colorIndices);
+ }
+
+ /**
+ * Retrieves the normal index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @return the normal index
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getNormalIndex(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray13"));
+
+ return ((IndexedGeometryArrayRetained)this.retained).getNormalIndex(index);
+ }
+
+ /**
+ * Retrieves the normal indices associated with the vertices starting at
+ * the specified index for this object. The normal indicies are
+ * copied into the specified array. The array must be large enough
+ * to hold all of the normal indicies.
+ *
+ * @param index the vertex index
+ * @param normalIndices array that will receive the normal indices
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getNormalIndices(int index, int normalIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray13"));
+
+ ((IndexedGeometryArrayRetained)this.retained).getNormalIndices(index, normalIndices);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinateIndex(int texCoordSet, ...)</code>
+ */
+ public int getTextureCoordinateIndex(int index) {
+ return (getTextureCoordinateIndex(0, index));
+ }
+
+ /**
+ * Retrieves the texture coordinate index associated with the vertex at
+ * the specified index in the specified texture coordinate set
+ * for this object.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index the vertex index
+ *
+ * @return the texture coordinate index
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if neither of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @since Java 3D 1.2
+ */
+ public int getTextureCoordinateIndex(int texCoordSet, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray15"));
+
+ return ((IndexedGeometryArrayRetained)this.retained).getTextureCoordinateIndex(texCoordSet, index);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getTextureCoordinateIndices(int texCoordSet, ...)</code>
+ */
+ public void getTextureCoordinateIndices(int index, int texCoordIndices[]) {
+ getTextureCoordinateIndices(0, index, texCoordIndices);
+ }
+
+
+ /**
+ * Retrieves the texture coordinate indices associated with the vertices
+ * starting at the specified index in the specified texture coordinate set
+ * for this object. The texture
+ * coordinate indices are copied into the specified array. The array
+ * must be large enough to hold all of the indices.
+ *
+ * @param texCoordSet texture coordinate set in this geometry array
+ * @param index the vertex index
+ * @param texCoordIndices array that will receive the texture coordinate
+ * indices
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception ArrayIndexOutOfBoundsException if neither of the
+ * <code>TEXTURE_COORDINATE</code> bits are set in the
+ * <code>vertexFormat</code> or if the index or
+ * texCoordSet is out of range.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureCoordinateIndices(int texCoordSet,
+ int index,
+ int texCoordIndices[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COORDINATE_INDEX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray15"));
+
+ ((IndexedGeometryArrayRetained)this.retained).getTextureCoordinateIndices(texCoordSet, index, texCoordIndices);
+ }
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+ // vertexFormat, vertexCount and indexCount are copied in
+ // subclass when constructor
+ // public IndexedGeometryArray(int vertexCount, int vertexFormat,
+ // int indexCount)
+ // is used in cloneNodeComponent()
+ IndexedGeometryArrayRetained ga =
+ (IndexedGeometryArrayRetained) originalNodeComponent.retained;
+ IndexedGeometryArrayRetained rt =
+ (IndexedGeometryArrayRetained) retained;
+
+ int vformat = ga.getVertexFormat();
+ int buffer[] = new int[ga.getIndexCount()];
+
+ if ((vformat & GeometryArray.COORDINATES) != 0) {
+ ga.getCoordinateIndices(0, buffer);
+ rt.setCoordinateIndices(0, buffer);
+ }
+
+ if ((vformat & GeometryArray.NORMALS) != 0) {
+ ga.getNormalIndices(0, buffer);
+ rt.setNormalIndices(0, buffer);
+ }
+
+ if ((vformat & GeometryArray.COLOR) != 0) {
+ ga.getColorIndices(0, buffer);
+ rt.setColorIndices(0, buffer);
+ }
+
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < ga.texCoordSetCount; i++) {
+ ga.getTextureCoordinateIndices(i, 0, buffer);
+ rt.setTextureCoordinateIndices(i, 0, buffer);
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java
new file mode 100644
index 0000000..632e4ec
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java
@@ -0,0 +1,1609 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Vector;
+import java.util.ArrayList;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+
+
+/**
+ * The IndexedGeometryArray object contains arrays of positional coordinates,
+ * colors, normals and/or texture coordinates that describe
+ * point, line, or surface geometry. It is extended to create
+ * the various primitive types (e.g., lines, triangle_strips, etc.)
+ */
+
+abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
+
+ // arrays to save indices for coord, color, normal, texcoord
+ int indexCoord[], indexColor[], indexNormal[];
+ Object indexTexCoord[];
+
+ int indexCount;
+
+ int initialIndexIndex = 0;
+ int validIndexCount = 0;
+
+ // Following variables are only used in compile mode
+ int[] compileIndexCount;
+ int[] compileIndexOffset;
+
+ int maxCoordIndex = 0;
+ int maxColorIndex = 0;
+ int maxNormalIndex = 0;
+ int[] maxTexCoordIndices = null;
+
+ void createIndexedGeometryArrayData(int indexCount) {
+ this.indexCount = indexCount;
+ this.validIndexCount = indexCount;
+
+ if((this.vertexFormat & GeometryArray.COORDINATES) != 0)
+ this.indexCoord = new int[indexCount];
+
+ if((this.vertexFormat & GeometryArray.NORMALS) != 0)
+ this.indexNormal = new int[indexCount];
+
+ if((this.vertexFormat & GeometryArray.COLOR) != 0)
+ this.indexColor = new int[indexCount];
+
+ if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ this.indexTexCoord = new Object[this.texCoordSetCount];
+ for (int i = 0; i < this.texCoordSetCount; i++) {
+ this.indexTexCoord[i] = new int[indexCount];
+ }
+ maxTexCoordIndices = new int[texCoordSetCount];
+ }
+ }
+
+
+ Object cloneNonIndexedGeometry() {
+ GeometryArrayRetained obj = null;
+ int vOffset;
+
+ switch (this.geoType) {
+ case GEO_TYPE_INDEXED_LINE_SET:
+ obj = new LineArrayRetained();
+ break;
+ case GEO_TYPE_INDEXED_POINT_SET:
+ obj = new PointArrayRetained();
+ break;
+ case GEO_TYPE_INDEXED_QUAD_SET:
+ obj = new QuadArrayRetained();
+ break;
+ case GEO_TYPE_INDEXED_TRI_SET:
+ obj = new TriangleArrayRetained();
+ break;
+ }
+ obj.createGeometryArrayData(validIndexCount, (vertexFormat & ~(GeometryArray.BY_REFERENCE|GeometryArray.INTERLEAVED|GeometryArray.USE_NIO_BUFFER)),
+ texCoordSetCount, texCoordSetMap);
+ obj.cloneSourceArray = this;
+ obj.unIndexify(this);
+
+ return (Object)obj;
+ }
+
+ void execute(long ctx, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha) {
+ throw new RuntimeException(J3dI18N.getString("IndexedGeometryArrayRetained0"));
+ }
+
+
+ /**
+ * Gets current number of indices
+ * @return indexCount
+ */
+ int getIndexCount(){
+ return indexCount;
+ }
+
+ void doErrorCheck(int newMax) {
+ doCoordCheck(newMax);
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+ if ((vertexFormat & GeometryArray.COLOR) != 0) {
+ doColorCheck(newMax);
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ doTexCoordCheck(newMax, i);
+ }
+ }
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ doNormalCheck(newMax);
+ }
+ }
+ }
+
+ void doCoordCheck(int newMax) {
+ // Check to make sure that the array length defined by the user is ateast maxCoordIndex long
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ if (newMax >= vertexCount) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ }
+ else {
+ if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case PF:
+ if(floatBufferRefCoords != null && 3 * newMax >= floatBufferRefCoords.limit() ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ break;
+ case PD:
+ if(doubleBufferRefCoords != null && 3 * newMax >= doubleBufferRefCoords.limit() ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ break;
+ }
+ }
+ else {
+ if(interleavedFloatBufferImpl != null && stride * newMax >= interleavedFloatBufferImpl.limit() ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ }
+ } else {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & VERTEX_DEFINED)) {
+ case PF:
+ if (floatRefCoords != null && (3 * newMax >= floatRefCoords.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ break;
+ case PD:
+ if (doubleRefCoords != null && (3 * newMax >= doubleRefCoords.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+
+ break;
+ case P3F:
+ if (p3fRefCoords != null && (newMax >= p3fRefCoords.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ break;
+ case P3D:
+ if (p3dRefCoords != null && (newMax >= p3dRefCoords.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ if (interLeavedVertexData != null && (stride * newMax >= interLeavedVertexData.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
+ }
+ }
+ }
+ }
+
+ }
+
+ void doColorCheck(int newMax) {
+ // If the new Value is greater than the old value, make sure there is array length
+ // to support the change
+ // Check to make sure that the array length defined by the user is ateast maxCoordIndex long
+ if ((vertexFormat & GeometryArray.COLOR) == 0)
+ return;
+
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ if (newMax >= vertexCount) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ }
+ else {
+ int multiplier = getColorStride();
+
+ if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & COLOR_DEFINED)) {
+ case CF:
+ if (floatBufferRefColors != null && multiplier * newMax >= floatBufferRefColors.limit()) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ break;
+ case CUB:
+ if (byteBufferRefColors != null && multiplier * newMax >= byteBufferRefColors.limit()) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ break;
+ }
+ }
+ else {
+ if(interleavedFloatBufferImpl != null &&
+ stride * newMax >= interleavedFloatBufferImpl.limit()) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ }
+ } else {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & COLOR_DEFINED)) {
+ case CF:
+ if (floatRefColors != null && (multiplier * newMax >= floatRefColors.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ break;
+ case CUB:
+ if (byteRefColors != null && (multiplier * newMax >= byteRefColors.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+
+ break;
+ case C3F:
+ if (c3fRefColors != null && (newMax >= c3fRefColors.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ break;
+ case C4F:
+ if (c4fRefColors != null && (newMax >= c4fRefColors.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ break;
+ case C3UB:
+ if (c3bRefColors != null && (newMax >= c3bRefColors.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ break;
+ case C4UB:
+ if (c4bRefColors != null && (newMax >= c4bRefColors.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (interLeavedVertexData != null && (stride * newMax >= interLeavedVertexData.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
+ }
+ }
+ }
+ }
+
+ }
+
+
+ void doNormalCheck(int newMax) {
+ if ((vertexFormat & GeometryArray.NORMALS) == 0)
+ return;
+
+ // Check to make sure that the array length defined by the user is ateast maxCoordIndex long
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ if (newMax >= vertexCount) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ }
+ else {
+ if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & GeometryArrayRetained.NORMAL_DEFINED)) {
+ case NF:
+ if(floatBufferRefNormals != null && 3 * newMax >= floatBufferRefNormals.limit() ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ break;
+ }
+ }
+ else {
+ if(interleavedFloatBufferImpl != null && stride * newMax >= interleavedFloatBufferImpl.limit() ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ }
+ } else {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & NORMAL_DEFINED)) {
+ case NF:
+ if (floatRefNormals != null && (3 * newMax >= floatRefNormals.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ break;
+ case N3F:
+ if (v3fRefNormals != null && (newMax >= v3fRefNormals.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ if (interLeavedVertexData != null && (stride * newMax >= interLeavedVertexData.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
+ }
+ }
+ }
+ }
+
+ }
+
+
+
+ void doTexCoordCheck(int newMax, int texCoordSet) {
+
+ // Check to make sure that the array length defined by the user is ateast maxCoordIndex long
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) == 0)
+ return;
+
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ if (newMax >= vertexCount) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ }
+ else {
+ int multiplier = getTexStride();
+
+ if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & GeometryArrayRetained.TEXCOORD_DEFINED)) {
+ case TF:
+ FloatBufferWrapper texBuffer;
+ texBuffer = (FloatBufferWrapper)(((J3DBuffer) refTexCoordsBuffer[texCoordSet]).getBufferImpl());
+ if(refTexCoords[texCoordSet] != null && multiplier * newMax >= texBuffer.limit()) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ break;
+ }
+ }
+ else {
+ if(interleavedFloatBufferImpl != null && stride * newMax >= interleavedFloatBufferImpl.limit() ) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ }
+ } else {
+
+ if ((vertexFormat & GeometryArray.INTERLEAVED) == 0) {
+ switch ((vertexType & TEXCOORD_DEFINED)) {
+ case TF:
+ if (refTexCoords[texCoordSet] != null && (multiplier * newMax >= ((float[])refTexCoords[texCoordSet]).length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ break;
+ case T2F:
+ if (refTexCoords[texCoordSet] != null && (newMax >= ((TexCoord2f[])refTexCoords[texCoordSet]).length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+
+ break;
+ case T3F:
+ if (refTexCoords[texCoordSet] != null && (newMax >= ((TexCoord3f[])refTexCoords[texCoordSet]).length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ if (interLeavedVertexData != null && (stride * newMax >= interLeavedVertexData.length)) {
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
+ }
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * Sets the coordinate index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param coordinateIndex the new coordinate index
+ */
+ final void setCoordinateIndex(int index, int coordinateIndex) {
+ int newMax;
+ newMax = doIndexCheck(index, maxCoordIndex, indexCoord, coordinateIndex);
+ if (newMax > maxCoordIndex) {
+ doErrorCheck(newMax);
+ }
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+ if ((vertexFormat & GeometryArray.COLOR) != 0) {
+ maxColorIndex = newMax;
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = newMax;
+ }
+ }
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ maxNormalIndex = newMax;
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= INDEX_CHANGED;
+ this.indexCoord[index] = coordinateIndex;
+ maxCoordIndex = newMax;
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(true);
+ }
+ }
+
+ int doIndexCheck(int index, int maxIndex, int[] indices, int dataValue) {
+ int newMax = maxIndex;
+ if (index < initialIndexIndex)
+ return newMax;
+
+ if (index >= (initialIndexIndex+validIndexCount))
+ return newMax;
+
+ if (dataValue < 0) {
+ // Throw an exception, since index is negative
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray27"));
+
+ }
+
+ if (newMax == indices[index]) {
+ if (dataValue >= newMax) {
+ newMax = dataValue;
+ }
+ // Go thru the entire list and look for the max
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ if (indices[i] > newMax) {
+ newMax = indices[i];
+ }
+ }
+ }
+ }
+ else if (dataValue > newMax) {
+ newMax = dataValue;
+ }
+ return newMax;
+ }
+
+ int doIndicesCheck(int index, int maxIndex, int[] indices, int[] newIndices) {
+ int newMax = maxIndex;
+ boolean computeNewMax = false;
+ int i, j, num = newIndices.length;
+ boolean maxReset = false;
+ for (j = 0; j < num; j++) {
+ if ((index+j) < initialIndexIndex)
+ continue;
+
+ if ((index+j) >= (initialIndexIndex+validIndexCount))
+ continue;
+ if (newIndices[j] < 0) {
+ // Throw an exception, since index is negative
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray27"));
+
+ }
+ if (indices[index+j] == maxIndex) {
+ if (newIndices[j] >= newMax) {
+ newMax = newIndices[j];
+ computeNewMax = false;
+ maxReset = true;
+ }
+ // Go thru the entire list and look for the max
+ // If in the new list there is no value that is >=
+ // to the old maximum
+ else if (!maxReset){
+ computeNewMax = true;
+ }
+ }
+ else if (newIndices[j] >= newMax) {
+ newMax = newIndices[j];
+ computeNewMax = false;
+ maxReset = true;
+ }
+ }
+ if (computeNewMax) {
+ for (i = 0; i < indices.length; i++) {
+ if (indices[i] > newMax) {
+ newMax = indices[i];
+ }
+ }
+ }
+ return newMax;
+ }
+
+
+ /**
+ * Sets the coordinate indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param coordinateIndices an array of coordinate indices
+ */
+ final void setCoordinateIndices(int index, int coordinateIndices[]) {
+ int newMax;
+ int i, j, num = coordinateIndices.length;
+ newMax = doIndicesCheck(index, maxCoordIndex, indexCoord, coordinateIndices);
+ if (newMax > maxCoordIndex) {
+ doErrorCheck(newMax);
+ }
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+ if ((vertexFormat & GeometryArray.COLOR) != 0) {
+ maxColorIndex = newMax;
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = newMax;
+ }
+ }
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ maxNormalIndex = newMax;
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= INDEX_CHANGED;
+ maxCoordIndex = newMax;
+ for (i=0, j = index; i < num;i++, j++) {
+ this.indexCoord[j] = coordinateIndices[i];
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(true);
+ }
+ }
+
+ /**
+ * Sets the color index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param colorIndex the new color index
+ */
+ final void setColorIndex(int index, int colorIndex) {
+ int newMax = maxColorIndex;
+
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ newMax = doIndexCheck(index, maxColorIndex, indexColor, colorIndex);
+ if (newMax > maxColorIndex) {
+ doColorCheck(newMax);
+ }
+ geomLock.getLock();
+ // No need to set INDEX_CHANGED since IndexBuffer
+ // is used only when USE_COORD_INDEX_ONLY specified.
+ // In this case only coordinate index array is
+ // considered.
+ this.indexColor[index] = colorIndex;
+ maxColorIndex = newMax;
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+ else {
+ maxColorIndex = maxCoordIndex;
+ this.indexColor[index] = colorIndex;
+ }
+
+ }
+
+ /**
+ * Sets the color indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param colorIndices an array of color indices
+ */
+ final void setColorIndices(int index, int colorIndices[]) {
+ int i, j, num = colorIndices.length;
+ int newMax;
+
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ newMax = doIndicesCheck(index, maxColorIndex, indexColor, colorIndices);
+ if (newMax > maxColorIndex) {
+ doColorCheck(newMax);
+ }
+ geomLock.getLock();
+ maxColorIndex = newMax;
+ for (i=0, j = index; i < num;i++, j++) {
+ this.indexColor[j] = colorIndices[i];
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+ else {
+ maxColorIndex = maxCoordIndex;
+ for (i=0, j = index; i < num;i++, j++) {
+ this.indexColor[j] = colorIndices[i];
+ }
+ }
+
+ }
+
+ /**
+ * Sets the normal index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param normalIndex the new normal index
+ */
+ final void setNormalIndex(int index, int normalIndex) {
+ int newMax;
+
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ newMax = doIndexCheck(index, maxNormalIndex, indexNormal, normalIndex);
+ if (newMax > maxNormalIndex) {
+ doNormalCheck(newMax);
+ }
+ geomLock.getLock();
+ maxNormalIndex = newMax;
+ this.indexNormal[index] = normalIndex;
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+ else {
+ maxNormalIndex = maxCoordIndex;
+ this.indexNormal[index] = normalIndex;
+ }
+
+ }
+
+ /**
+ * Sets the normal indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param normalIndices an array of normal indices
+ */
+ final void setNormalIndices(int index, int normalIndices[]) {
+ int i, j, num = normalIndices.length;
+ int newMax;
+
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ newMax = doIndicesCheck(index, maxNormalIndex, indexNormal, normalIndices);
+ if (newMax > maxNormalIndex) {
+ doNormalCheck(newMax);
+ }
+ geomLock.getLock();
+ for (i=0, j = index; i < num;i++, j++) {
+ this.indexNormal[j] = normalIndices[i];
+ }
+ maxNormalIndex = newMax;
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+ else {
+ maxNormalIndex = maxCoordIndex;
+ for (i=0, j = index; i < num;i++, j++) {
+ this.indexNormal[j] = normalIndices[i];
+ }
+ }
+
+ }
+
+ /**
+ * Sets the texture coordinate index associated with the vertex at
+ * the specified index for this object.
+ * @param texCoordSet the texture coordinate set
+ * @param index the vertex index
+ * @param texCoordIndex the new texture coordinate index
+ */
+ final void setTextureCoordinateIndex(int texCoordSet, int index, int texCoordIndex) {
+ int newMax;
+ int [] indices = (int[])this.indexTexCoord[texCoordSet];
+
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ newMax = doIndexCheck(index, maxTexCoordIndices[texCoordSet],indices, texCoordIndex);
+ if (newMax > maxTexCoordIndices[texCoordSet]) {
+ doTexCoordCheck(newMax, texCoordSet);
+ }
+ geomLock.getLock();
+ maxTexCoordIndices[texCoordSet] = newMax;
+ indices[index] = texCoordIndex;
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+ }
+ else {
+ maxTexCoordIndices[texCoordSet] = maxCoordIndex;
+ indices[index] = texCoordIndex;
+ }
+
+
+ }
+
+ /**
+ * Sets the texture coordinate indices associated with the vertices
+ * starting at the specified index for this object.
+ * @param texCoordSet the texture coordinate set
+ * @param index the vertex index
+ * @param texCoordIndices an array of texture coordinate indices
+ */
+ final void setTextureCoordinateIndices(int texCoordSet, int index, int texCoordIndices[]) {
+ int i, j, num = texCoordIndices.length;
+ int [] indices = (int[])this.indexTexCoord[texCoordSet];
+
+ int newMax;
+
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ newMax = doIndicesCheck(index, maxTexCoordIndices[texCoordSet], indices, texCoordIndices);
+ if (newMax > maxTexCoordIndices[texCoordSet]) {
+ doTexCoordCheck(newMax, texCoordSet);
+ }
+ geomLock.getLock();
+ maxTexCoordIndices[texCoordSet] = newMax;
+ for (i=0, j = index; i < num;i++, j++) {
+ indices[j] = texCoordIndices[i];
+ }
+ geomLock.unLock();
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(false);
+ }
+
+ }
+ else {
+ maxTexCoordIndices[texCoordSet] = maxCoordIndex;
+ for (i=0, j = index; i < num;i++, j++) {
+ indices[j] = texCoordIndices[i];
+ }
+ }
+
+ }
+
+ /**
+ * Retrieves the coordinate index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @return the coordinate index
+ */
+ final int getCoordinateIndex(int index) {
+ return this.indexCoord[index];
+ }
+
+ /**
+ * Retrieves the coordinate indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param coordinateIndices array that will receive the coordinate indices
+ */
+ final void getCoordinateIndices(int index, int coordinateIndices[]) {
+ int i, j, num = coordinateIndices.length;
+
+ for (i=0, j = index;i < num;i++, j++)
+ {
+ coordinateIndices[i] = this.indexCoord[j];
+ }
+ }
+
+ /**
+ * Retrieves the color index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @return the color index
+ */
+ final int getColorIndex(int index) {
+ return this.indexColor[index];
+ }
+
+ /**
+ * Retrieves the color indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param colorIndices array that will receive the color indices
+ */
+ final void getColorIndices(int index, int colorIndices[]) {
+ int i, j, num = colorIndices.length;
+
+ for (i=0, j = index;i < num;i++, j++)
+ {
+ colorIndices[i] = this.indexColor[j];
+ }
+ }
+
+ /**
+ * Retrieves the normal index associated with the vertex at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @return the normal index
+ */
+ final int getNormalIndex(int index) {
+ return this.indexNormal[index];
+ }
+
+ /**
+ * Retrieves the normal indices associated with the vertices starting at
+ * the specified index for this object.
+ * @param index the vertex index
+ * @param normalIndices array that will receive the normal indices
+ */
+ final void getNormalIndices(int index, int normalIndices[]) {
+ int i, j, num = normalIndices.length;
+
+ for (i=0, j = index;i < num;i++, j++)
+ {
+ normalIndices[i] = this.indexNormal[j];
+ }
+ }
+
+ /**
+ * Retrieves the texture coordinate index associated with the vertex at
+ * the specified index for this object.
+ * @param texCoordSet the texture coordinate set
+ * @param index the vertex index
+ * @return the texture coordinate index
+ */
+ final int getTextureCoordinateIndex(int texCoordSet, int index) {
+ return ((int[])this.indexTexCoord[texCoordSet])[index];
+ }
+
+ /**
+ * Retrieves the texture coordinate indices associated with the vertices
+ * starting at the specified index for this object.
+ * @param texCoordSet the texture coordinate set
+ * @param index the vertex index
+ * @param texCoordIndices array that will receive the texture coordinate indices
+ */
+ final void getTextureCoordinateIndices(int texCoordSet, int index, int texCoordIndices[]) {
+ int i, j, num = texCoordIndices.length;
+ int [] indices = (int[])this.indexTexCoord[texCoordSet];
+
+ for (i=0, j = index;i < num;i++, j++)
+ {
+ texCoordIndices[i] = indices[j];
+ }
+ }
+
+ // used for GeometryArrays
+ native void executeIndexedGeometry(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean useAlpha,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int initialIndexIndex,
+ int indexCount,
+ int vertexCount, int vformat,
+ int texCoordSetCount, int texCoordSetMap[],
+ int texCoordSetMapLen,
+ int[] texCoordSetOffset,
+ int numActiveTexUnitState,
+ int[] texUnitStateMap,
+ float[] varray, float[] cdata,
+ int texUnitIndex, int cdirty,
+ int[] indexCoord);
+
+ // used for interleaved, by reference, nio buffer
+ native void executeIndexedGeometryBuffer(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean useAlpha,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int initialIndexIndex,
+ int indexCount,
+ int vertexCount, int vformat,
+ int texCoordSetCount, int texCoordSetMap[],
+ int texCoordSetMapLen,
+ int[] texCoordSetOffset,
+ int numActiveTexUnitState,
+ int[] texUnitStateMap,
+ Object varray, float[] cdata,
+ int texUnitIndex, int cdirty,
+ int[] indexCoord);
+
+
+
+ native void executeIndexedGeometryVA(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int initialIndexIndex,
+ int validIndexCount,
+ int vertexCount,
+ int vformat,
+ int vdefined,
+ float[] vfcoords, double[] vdcoords,
+ float[] cfdata, byte[] cbdata,
+ float[] ndata,
+ int pass, int texcoordmaplength,
+ int[] texcoordoffset,
+ int numActiveTexUnitState, int[] texunitstatemap,
+ int texstride, Object[] texCoords,
+ int cdirty,
+ int[] indexCoord);
+
+ // non interleaved, by reference, nio buffer
+ native void executeIndexedGeometryVABuffer(long ctx,
+ GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale,
+ boolean multiScreen,
+ boolean ignoreVertexColors,
+ int initialIndexIndex,
+ int validIndexCount,
+ int vertexCount,
+ int vformat,
+ int vdefined,
+ Object vcoords,
+ Object cdataBuffer,
+ float[] cfdata, byte[] cbdata,
+ Object normal,
+ int pass, int texcoordmaplength,
+ int[] texcoordoffset,
+ int numActiveTexUnitState, int[] texunitstatemap,
+ int texstride, Object[] texCoords,
+ int cdirty,
+ int[] indexCoord);
+
+ // used for IndexedGeometry
+ native void buildIndexedGeometry(long ctx, GeometryArrayRetained geo, int geo_type,
+ boolean isNonUniformScale, boolean updateAlpha,
+ float alpha,
+ boolean ignoreVertexColors,
+ int initialIndexIndex,
+ int validIndexCount,
+ int vertexCount,
+ int vformat,
+ int texCoordSetCount, int texCoordSetMap[],
+ int texCoordSetMapLen,
+ int[] texCoordSetMapOffset,
+ double[] xform, double[] nxform,
+ float[] varray, int[] indexCoord);
+
+
+ void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha,
+ boolean multiScreen, int screen,
+ boolean ignoreVertexColors, int pass) {
+ int cdirty;
+ boolean useAlpha = false;
+ Object[] retVal;
+ if (mirrorGeometry != null) {
+ mirrorGeometry.execute(cv, ra, isNonUniformScale, updateAlpha, alpha,
+ multiScreen, screen,
+ ignoreVertexColors, pass);
+ return;
+ }
+ //By reference with java array
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ float[] vdata;
+ // System.out.println("by-copy");
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ // update the alpha values
+ retVal = updateAlphaInVertexData(cv, screen, alpha);
+ useAlpha = (retVal[0] == Boolean.TRUE);
+ vdata = (float[])retVal[1];
+
+ // D3D only
+ if (alpha != lastScreenAlpha) {
+ // handle multiple screen case
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ vdata = vertexData;
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ // geomLock is get in MasterControl when
+ // RenderBin render the geometry. So it is safe
+ // just to set the dirty flag here
+ dirtyFlag = 0;
+ }
+
+ executeIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale,
+ useAlpha,
+ multiScreen,
+ ignoreVertexColors,
+ initialIndexIndex,
+ validIndexCount,
+ // Vertex Count is maxCoordIndex + 1
+ maxCoordIndex + 1,
+ ((vertexFormat & GeometryArray.COLOR) != 0)?(vertexFormat|GeometryArray.COLOR_4):vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ cv.numActiveTexUnit, cv.texUnitStateMap,
+ vdata, null,
+ pass, cdirty, indexCoord);
+
+
+ } // end of non by reference
+ else if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ if(interLeavedVertexData == null)
+ return;
+
+ float[] cdata = null;
+
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ // update the alpha values
+ retVal = updateAlphaInInterLeavedData(cv, screen, alpha);
+ useAlpha = (retVal[0] == Boolean.TRUE);
+ cdata = (float[])retVal[1];
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+
+ executeIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale,
+ useAlpha,
+ multiScreen,
+ ignoreVertexColors,
+ initialIndexIndex,
+ validIndexCount,
+ maxCoordIndex + 1,
+ vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ cv.numActiveTexUnit, cv.texUnitStateMap,
+ interLeavedVertexData, cdata,
+ pass, cdirty, indexCoord);
+ } //end of interleaved
+ else {
+ // Check if a vertexformat is set, but the array is null
+ // if yes, don't draw anything
+ if ((vertexType == 0) ||
+ ((vertexType & VERTEX_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.COLOR) != 0) &&
+ (vertexType & COLOR_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.NORMALS) != 0) &&
+ (vertexType & NORMAL_DEFINED) == 0) ||
+ (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
+ (vertexType & TEXCOORD_DEFINED) == 0)) {
+ return;
+ } else {
+ byte[] cbdata = null;
+ float[] cfdata = null;
+
+ if ((vertexType & (CF | C3F | C4F )) != 0) {
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cfdata = updateAlphaInFloatRefColors(cv,
+ screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ cfdata = mirrorFloatRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+
+ }
+ dirtyFlag = 0;
+ }
+ } else if ((vertexType & (CUB| C3UB | C4UB)) != 0) {
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cbdata = updateAlphaInByteRefColors(
+ cv, screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ cbdata = mirrorUnsignedByteRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+ } else {
+ cdirty = dirtyFlag;
+ }
+
+ int vdefined = 0;
+ if((vertexType & (PF | P3F)) != 0)
+ vdefined |= COORD_FLOAT;
+ if((vertexType & (PD | P3D)) != 0)
+ vdefined |= COORD_DOUBLE;
+ if((vertexType & (CF | C3F | C4F)) != 0)
+ vdefined |= COLOR_FLOAT;
+ if((vertexType & (CUB| C3UB | C4UB)) != 0)
+ vdefined |= COLOR_BYTE;
+ if((vertexType & NORMAL_DEFINED) != 0)
+ vdefined |= NORMAL_FLOAT;
+ if((vertexType & TEXCOORD_DEFINED) != 0)
+ vdefined |= TEXCOORD_FLOAT;
+
+ executeIndexedGeometryVA(cv.ctx, this, geoType, isNonUniformScale,
+ multiScreen,
+ ignoreVertexColors,
+ initialIndexIndex,
+ validIndexCount,
+ maxCoordIndex + 1,
+ (vertexFormat | c4fAllocated),
+ vdefined,
+ mirrorFloatRefCoords, mirrorDoubleRefCoords,
+ cfdata, cbdata,
+ mirrorFloatRefNormals,
+ pass,
+ ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+ texCoordSetMap,
+ cv.numActiveTexUnit,
+ cv.texUnitStateMap,
+ texCoordStride,
+ mirrorRefTexCoords, cdirty, indexCoord);
+
+ }
+ } // end of non interleaved and by reference
+ }//end of non io buffer
+
+ else {
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ if( interleavedFloatBufferImpl == null)
+ return;
+
+ float[] cdata = null;
+
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ // update the alpha values
+ retVal = updateAlphaInInterLeavedData(cv, screen, alpha);
+ useAlpha = (retVal[0] == Boolean.TRUE);
+ cdata = (float[])retVal[1];
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+
+ executeIndexedGeometryBuffer(cv.ctx, this, geoType, isNonUniformScale,
+ useAlpha,
+ multiScreen,
+ ignoreVertexColors,
+ initialIndexIndex,
+ validIndexCount,
+ maxCoordIndex + 1,
+ vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ cv.numActiveTexUnit, cv.texUnitStateMap,
+ interleavedFloatBufferImpl.getBufferAsObject(), cdata,
+ pass, cdirty, indexCoord);
+ } //end of interleaved
+ else {
+ // Check if a vertexformat is set, but the array is null
+ // if yes, don't draw anything
+ if ((vertexType == 0) ||
+ ((vertexType & VERTEX_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.COLOR) != 0) &&
+ (vertexType & COLOR_DEFINED) == 0) ||
+ (((vertexFormat & GeometryArray.NORMALS) != 0) &&
+ (vertexType & NORMAL_DEFINED) == 0) ||
+ (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
+ (vertexType & TEXCOORD_DEFINED) == 0)) {
+ return;
+ } else {
+ byte[] cbdata = null;
+ float[] cfdata = null;
+
+ if ((vertexType & CF ) != 0) {
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cfdata = updateAlphaInFloatRefColors(cv,
+ screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // TODO: handle transparency case
+ //cfdata = null;
+ cfdata = mirrorFloatRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+
+ }
+ dirtyFlag = 0;
+ }
+ } else if ((vertexType & CUB ) != 0) {
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ cbdata = updateAlphaInByteRefColors(
+ cv, screen, alpha);
+ if (alpha != lastScreenAlpha) {
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ // TODO: handle transparency case
+ // cbdata = null;
+ cbdata = mirrorUnsignedByteRefColors[0];
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ dirtyFlag = 0;
+ }
+ } else {
+ cdirty = dirtyFlag;
+ }
+
+ Object vcoord = null, cdataBuffer=null, normal=null;
+
+ int vdefined = 0;
+ if((vertexType & PF) != 0) {
+ vdefined |= COORD_FLOAT;
+ vcoord = floatBufferRefCoords.getBufferAsObject();
+ } else if((vertexType & PD ) != 0) {
+ vdefined |= COORD_DOUBLE;
+ vcoord = doubleBufferRefCoords.getBufferAsObject();
+ }
+ if((vertexType & CF ) != 0) {
+ vdefined |= COLOR_FLOAT;
+ cdataBuffer = floatBufferRefColors.getBufferAsObject();
+ } else if((vertexType & CUB) != 0) {
+ vdefined |= COLOR_BYTE;
+ cdataBuffer = byteBufferRefColors.getBufferAsObject();
+ }
+
+ if((vertexType & NORMAL_DEFINED) != 0) {
+ vdefined |= NORMAL_FLOAT;
+ normal = floatBufferRefNormals.getBufferAsObject();
+ }
+
+ if((vertexType & TEXCOORD_DEFINED) != 0)
+ vdefined |= TEXCOORD_FLOAT;
+
+ executeIndexedGeometryVABuffer(cv.ctx, this, geoType, isNonUniformScale,
+ multiScreen,
+ ignoreVertexColors,
+ initialIndexIndex,
+ validIndexCount,
+ maxCoordIndex + 1,
+ (vertexFormat | c4fAllocated),
+ vdefined,
+ vcoord,
+ cdataBuffer,
+ cfdata, cbdata,
+ normal,
+ pass,
+ ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+ texCoordSetMap,
+ cv.numActiveTexUnit,
+ cv.texUnitStateMap,
+ texCoordStride,
+ refTexCoords, cdirty, indexCoord);
+
+ }
+ } // end of non interleaved and by reference
+ } // end of nio buffer
+ }
+
+ void buildGA(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha, boolean ignoreVertexColors,
+ Transform3D xform, Transform3D nxform) {
+ int cdirty;
+ boolean useAlpha = false;
+ Object[] retVal;
+ if (mirrorGeometry != null) {
+ ((GeometryArrayRetained)mirrorGeometry).buildGA(cv, ra, isNonUniformScale, updateAlpha, alpha,
+ ignoreVertexColors, xform, nxform);
+ }
+ else {
+
+ if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+ float[] vdata;
+ // System.out.println("by-copy");
+ synchronized (this) {
+ cdirty = dirtyFlag;
+ if (updateAlpha && !ignoreVertexColors) {
+ // update the alpha values
+ retVal = updateAlphaInVertexData(cv, cv.screen.screen, alpha);
+ useAlpha = (retVal[0] == Boolean.TRUE);
+ vdata = (float[])retVal[1];
+
+ // D3D only
+ if (alpha != lastScreenAlpha) {
+ // handle multiple screen case
+ lastScreenAlpha = alpha;
+ cdirty |= COLOR_CHANGED;
+ }
+ } else {
+ vdata = vertexData;
+ // if transparency switch between on/off
+ if (lastScreenAlpha != -1) {
+ lastScreenAlpha = -1;
+ cdirty |= COLOR_CHANGED;
+ }
+ }
+ // geomLock is get in MasterControl when
+ // RenderBin render the geometry. So it is safe
+ // just to set the dirty flag here
+ dirtyFlag = 0;
+ }
+
+ buildIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale,
+ updateAlpha, alpha, ignoreVertexColors,
+ initialIndexIndex,
+ validIndexCount,
+ maxCoordIndex + 1,
+ vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+ texCoordSetMapOffset,
+ (xform == null) ? null : xform.mat,
+ (nxform == null) ? null : nxform.mat,
+ vdata, indexCoord);
+ }
+ }
+ }
+
+ void mergeGeometryArrays(ArrayList list) {
+ int numMerge = list.size();
+ int[] texCoord = null;
+ indexCount = 0;
+ for (int i=0; i < numMerge; i++) {
+ IndexedGeometryArrayRetained geo= (IndexedGeometryArrayRetained)list.get(i);
+ indexCount += geo.validIndexCount;
+ }
+ validIndexCount = indexCount;
+ initialIndexIndex = 0;
+ compileIndexCount = new int[numMerge];
+ compileIndexOffset = new int[numMerge];
+ indexCoord = new int[indexCount];
+ if ((vertexFormat & GeometryArray.COLOR) != 0)
+ indexColor = new int[indexCount];
+ if ((vertexFormat & GeometryArray.NORMALS) != 0)
+ indexNormal = new int[indexCount];
+ // We only merge if texCoordSetCount = 1
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ indexTexCoord = new Object[1];
+ indexTexCoord[0] = new int[indexCount];
+ texCoord = (int[])indexTexCoord[0];
+ }
+ int curDataOffset = 0;
+ int curIndexOffset = 0;
+ for (int i = 0; i < numMerge; i++) {
+ IndexedGeometryArrayRetained geo= (IndexedGeometryArrayRetained)list.get(i);
+ int curIndexCount = geo.validIndexCount;
+ compileIndexCount[i] = curIndexCount;
+ // Copy all the indices
+ for (int j = 0; j < curIndexCount; j++) {
+ indexCoord[j+curIndexOffset] = geo.indexCoord[j+geo.initialIndexIndex]+curDataOffset;
+ if ((vertexFormat & GeometryArray.COLOR) != 0)
+ indexColor[j+curIndexOffset] = geo.indexColor[j+geo.initialIndexIndex]+curDataOffset;
+ if ((vertexFormat & GeometryArray.NORMALS) != 0)
+ indexNormal[j+curIndexOffset] = geo.indexNormal[j+geo.initialIndexIndex]+curDataOffset;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0)
+ texCoord[j+curIndexOffset] = ((int[])geo.indexTexCoord[0])[j+geo.initialIndexIndex]+curDataOffset;
+ }
+ maxCoordIndex = geo.maxCoordIndex +curDataOffset;
+ compileIndexOffset[i] = curIndexOffset;
+ curDataOffset += geo.vertexCount;
+ curIndexOffset += curIndexCount;
+ }
+ // reset the max Values
+
+ // call the super to merge the vertex data
+ super.mergeGeometryArrays(list);
+ }
+
+
+ boolean isWriteStatic() {
+
+ if (!super.isWriteStatic() ||
+ source.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_WRITE ) ||
+ source.getCapability(IndexedGeometryArray.ALLOW_COLOR_INDEX_WRITE) ||
+ source.getCapability(IndexedGeometryArray.ALLOW_NORMAL_INDEX_WRITE) ||
+ source.getCapability(IndexedGeometryArray.ALLOW_TEXCOORD_INDEX_WRITE))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Gets current number of indices
+ * @return indexCount
+ */
+ int getIndexCount(int id){
+ return compileIndexCount[id];
+ }
+
+ int computeMaxIndex(int initial, int count, int[] indices) {
+ int maxIndex = 0;
+ for (int i = initial; i < (initial+count); i++) {
+ if (indices[i] > maxIndex) {
+ maxIndex = indices[i];
+ }
+ }
+ return maxIndex;
+
+ }
+
+ void setValidIndexCount(int validIndexCount) {
+ if (validIndexCount < 0) {
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray21"));
+ }
+ if ((initialIndexIndex + validIndexCount) > indexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray22"));
+ }
+ int newCoordMax =0;
+ int newColorIndex=0;
+ int newNormalIndex=0;
+ int newTexCoordIndex[]=null;
+
+ newCoordMax = computeMaxIndex(initialIndexIndex, validIndexCount,indexCoord );
+ doErrorCheck(newCoordMax);
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ if ((vertexFormat & GeometryArray.COLOR) != 0) {
+ newColorIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexColor);
+ doColorCheck(newColorIndex);
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ newTexCoordIndex = new int[texCoordSetCount];
+ for (int i = 0; i < texCoordSetCount; i++) {
+ newTexCoordIndex[i] = computeMaxIndex(initialIndexIndex,validIndexCount,
+ (int[])indexTexCoord[i]);
+ doTexCoordCheck(newTexCoordIndex[i], i);
+ }
+ }
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ newNormalIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexNormal);
+ doNormalCheck(newNormalIndex);
+ }
+ }
+
+ geomLock.getLock();
+ this.validIndexCount = validIndexCount;
+ maxCoordIndex = newCoordMax;
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ maxColorIndex = newColorIndex;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = newTexCoordIndex[i];
+ }
+ }
+ maxNormalIndex = newNormalIndex;
+ }
+ else {
+ maxColorIndex = maxCoordIndex;
+ maxNormalIndex = maxCoordIndex;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = maxCoordIndex;
+ }
+ }
+ }
+ geomLock.unLock();
+ // bbox is computed for the entries list.
+ // so, send as false
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(true);
+ }
+
+ }
+
+ void setInitialIndexIndex(int initialIndexIndex) {
+ if ((initialIndexIndex + validIndexCount) > indexCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray22"));
+ }
+ int newCoordMax =0;
+ int newColorIndex=0;
+ int newNormalIndex=0;
+ int newTexCoordIndex[]=null;
+
+ newCoordMax = computeMaxIndex(initialIndexIndex, validIndexCount, indexCoord);
+ doErrorCheck(newCoordMax);
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ if ((vertexFormat & GeometryArray.COLOR) != 0) {
+ newColorIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexColor);
+ doColorCheck(newColorIndex);
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ newTexCoordIndex = new int[texCoordSetCount];
+ for (int i = 0; i < texCoordSetCount; i++) {
+ newTexCoordIndex[i] = computeMaxIndex(initialIndexIndex,validIndexCount,
+ (int[])indexTexCoord[i]);
+ doTexCoordCheck(newTexCoordIndex[i], i);
+ }
+ }
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ newNormalIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexNormal);
+ doNormalCheck(newNormalIndex);
+ }
+ }
+
+ geomLock.getLock();
+ dirtyFlag |= INDEX_CHANGED;
+ this.initialIndexIndex = initialIndexIndex;
+ maxCoordIndex = newCoordMax;
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ maxColorIndex = newColorIndex;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = newTexCoordIndex[i];
+ }
+ }
+ maxNormalIndex = newNormalIndex;
+ }
+ else {
+ maxColorIndex = maxCoordIndex;
+ maxNormalIndex = maxCoordIndex;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (int i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = maxCoordIndex;
+ }
+ }
+ }
+ geomLock.unLock();
+ // bbox is computed for the entries list.
+ // so, send as false
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(true);
+ }
+ }
+
+ int getInitialIndexIndex() {
+ return initialIndexIndex;
+ }
+
+ int getValidIndexCount() {
+ return validIndexCount;
+ }
+ void handleFrequencyChange(int bit) {
+ if ((bit == IndexedGeometryArray.ALLOW_COORDINATE_INDEX_WRITE) ||
+ (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) &&
+ ((vertexFormat & GeometryArray.COLOR) != 0) &&
+ bit == IndexedGeometryArray.ALLOW_COLOR_INDEX_WRITE) ||
+ (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) &&
+ ((vertexFormat & GeometryArray.NORMALS) != 0) &&
+ bit == IndexedGeometryArray.ALLOW_NORMAL_INDEX_WRITE) ||
+ (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0)&&
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0)&&
+ bit == IndexedGeometryArray.ALLOW_TEXCOORD_INDEX_WRITE)) {
+ setFrequencyChangeMask(bit, 0x1);
+ }
+ else {
+ super.handleFrequencyChange(bit);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java b/src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java
new file mode 100644
index 0000000..a3b6e0f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java
@@ -0,0 +1,210 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedGeometryStripArray object is an abstract class that is extended for
+ * a set of IndexedGeometryArray strip primitives. These include LINE_STRIP,
+ * TRIANGLE_STRIP, and TRIANGLE_FAN.
+ */
+
+public abstract class IndexedGeometryStripArray extends IndexedGeometryArray {
+
+ // non-public, no parameter constructor
+ IndexedGeometryStripArray() {}
+
+ /**
+ * Constructs an empty IndexedGeometryStripArray object with the specified
+ * number of vertices, vertex format, number of indices, and
+ * array of per-strip index counts.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ * The sum of the elements in this array defines the total number
+ * of valid indexed vertices that are rendered (validIndexCount).
+ *
+ * @exception IllegalArgumentException if
+ * <code>validIndexCount > indexCount</code>
+ */
+ public IndexedGeometryStripArray(int vertexCount,
+ int vertexFormat,
+ int indexCount,
+ int[] stripIndexCounts) {
+
+ super(vertexCount, vertexFormat, indexCount);
+ ((IndexedGeometryStripArrayRetained)this.retained).
+ setStripIndexCounts(stripIndexCounts);
+ }
+
+ /**
+ * Constructs an empty IndexedGeometryStripArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, number of indices, and
+ * array of per-strip index counts.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.<p>
+ *
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ * The sum of the elements in this array defines the total number
+ * of valid indexed vertices that are rendered (validIndexCount).
+ *
+ * @exception IllegalArgumentException if
+ * <code>validIndexCount > indexCount</code>
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedGeometryStripArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount,
+ int[] stripIndexCounts) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount);
+ ((IndexedGeometryStripArrayRetained)this.retained).
+ setStripIndexCounts(stripIndexCounts);
+ }
+
+ /**
+ * Get number of strips in the GeometryStripArray
+ * @return numStrips number of strips
+ */
+ public int getNumStrips(){
+ if (isLiveOrCompiled())
+ if(!this.getCapability(GeometryArray.ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryStripArray0"));
+
+ return ((IndexedGeometryStripArrayRetained)this.retained).getNumStrips();
+ }
+
+ /**
+ * Sets the array of strip index counts. The length of this
+ * array is the number of separate strips. The elements in this
+ * array specify the number of indices for each separate strip.
+ * The sum of the elements in this array defines the total number
+ * of valid indexed vertices that are rendered (validIndexCount).
+ *
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ *
+ * @exception IllegalArgumentException if
+ * <code>initialIndexIndex + validIndexCount > indexCount</code>
+ *
+ * @since Java 3D 1.3
+ */
+ public void setStripIndexCounts(int[] stripIndexCounts) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(GeometryArray.ALLOW_COUNT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryStripArray2"));
+
+ ((IndexedGeometryStripArrayRetained)this.retained).setStripIndexCounts(stripIndexCounts);
+
+ }
+
+ /**
+ * Gets a list of indexCounts for each strip. The list is
+ * copied into the specified array. The array must be
+ * large enough to hold all of the ints.
+ * @param stripIndexCounts an array that will receive indexCounts
+ */
+ public void getStripIndexCounts(int[] stripIndexCounts) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(GeometryArray.ALLOW_COUNT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryStripArray1"));
+
+ ((IndexedGeometryStripArrayRetained)this.retained).
+ getStripIndexCounts(stripIndexCounts);
+ }
+
+ /**
+ * This method is not supported for indexed geometry strip arrays.
+ * The sum of the elements in the strip index counts array defines
+ * the valid index count.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setValidIndexCount(int validIndexCount) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java
new file mode 100644
index 0000000..5246cf8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java
@@ -0,0 +1,207 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.Vector;
+import java.util.ArrayList;
+
+/**
+ * The IndexedGeometryStripArray object is an abstract class that is extended for
+ * a set of IndexedGeometryArray strip primitives. These include LINE_STRIP,
+ * TRIANGLE_STRIP, and TRIANGLE_FAN.
+ */
+
+abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRetained {
+
+ // Array of per-strip vertex counts
+ int stripIndexCounts[];
+
+ // Following variables are only used in compile mode
+ int[] compileStripICOffset;
+ int[] compileIndexLength;
+
+ /**
+ * Set stripIndexCount data into local array
+ */
+ void setStripIndexCounts(int stripIndexCounts[]){
+
+ int i, num = stripIndexCounts.length, total = 0;
+
+
+
+
+
+ for (i=0; i < num; i++) {
+ total += stripIndexCounts[i];
+ if (this instanceof IndexedLineStripArrayRetained) {
+ if (stripIndexCounts[i] < 2) {
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArrayRetained1"));
+ }
+ }
+ else if (this instanceof IndexedTriangleStripArrayRetained) {
+ if (stripIndexCounts[i] < 3) {
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArrayRetained1"));
+ }
+ }
+ else if (this instanceof IndexedTriangleFanArrayRetained) {
+ if (stripIndexCounts[i] < 3) {
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArrayRetained1"));
+ }
+ }
+ }
+
+ // Sum of all stripIndexCounts MUST be same as indexCount
+ if ((initialIndexIndex + total) > indexCount)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryStripArrayRetained0"));
+ int newCoordMax =0;
+ int newColorIndex=0;
+ int newNormalIndex=0;
+ int newTexCoordIndex[]=null;
+
+ newCoordMax = computeMaxIndex(initialIndexIndex, total, indexCoord);
+ doErrorCheck(newCoordMax);
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ if ((vertexFormat & GeometryArray.COLOR) != 0) {
+ newColorIndex = computeMaxIndex(initialIndexIndex, total, indexColor);
+ doColorCheck(newColorIndex);
+ }
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ newTexCoordIndex = new int[texCoordSetCount];
+ for (i = 0; i < texCoordSetCount; i++) {
+ newTexCoordIndex[i] = computeMaxIndex(initialIndexIndex,total,
+ (int[])indexTexCoord[i]);
+ doTexCoordCheck(newTexCoordIndex[i], i);
+ }
+ }
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ newNormalIndex = computeMaxIndex(initialIndexIndex, total, indexNormal);
+ doNormalCheck(newNormalIndex);
+ }
+ }
+
+ geomLock.getLock();
+ validIndexCount = total;
+ this.stripIndexCounts = new int[num];
+ for (i=0;i < num;i++)
+ {
+ this.stripIndexCounts[i] = stripIndexCounts[i];
+ }
+ maxCoordIndex = newCoordMax;
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
+ maxColorIndex = newColorIndex;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = newTexCoordIndex[i];
+ }
+ }
+ maxNormalIndex = newNormalIndex;
+ }
+ else {
+ maxColorIndex = maxCoordIndex;
+ maxNormalIndex = maxCoordIndex;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (i = 0; i < texCoordSetCount; i++) {
+ maxTexCoordIndices[i] = maxCoordIndex;
+ }
+ }
+ }
+ geomLock.unLock();
+ // bbox is computed for the entries list.
+ // so, send as false
+ if (!inUpdater && source != null && source.isLive()) {
+ sendDataChangedMessage(true);
+ }
+
+ }
+
+ Object cloneNonIndexedGeometry() {
+ GeometryStripArrayRetained obj = null;
+ int i;
+ switch (this.geoType) {
+ case GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ obj = new LineStripArrayRetained();
+ break;
+ case GEO_TYPE_INDEXED_TRI_FAN_SET:
+ obj = new TriangleFanArrayRetained();
+ break;
+ case GEO_TYPE_INDEXED_TRI_STRIP_SET:
+ obj = new TriangleStripArrayRetained();
+ break;
+ }
+ obj.createGeometryArrayData(validIndexCount, (vertexFormat & ~(GeometryArray.BY_REFERENCE|GeometryArray.INTERLEAVED|GeometryArray.USE_NIO_BUFFER)), texCoordSetCount, texCoordSetMap);
+ obj.unIndexify(this);
+ obj.setStripVertexCounts(stripIndexCounts);
+
+ return (Object)obj;
+ }
+
+
+ /**
+ * Get number of strips in the GeometryStripArray
+ * @return numStrips number of strips
+ */
+ int getNumStrips(){
+ return stripIndexCounts.length;
+ }
+
+ /**
+ * Get a list of vertexCounts for each strip
+ * @param stripIndexCounts an array that will receive vertexCounts
+ */
+ void getStripIndexCounts(int stripIndexCounts[]){
+ for (int i=stripIndexCounts.length-1;i >= 0; i--) {
+ stripIndexCounts[i] = this.stripIndexCounts[i];
+ }
+ }
+
+ void mergeGeometryArrays(ArrayList list) {
+ int numMerge = list.size();
+ int numCount = 0;
+ int i, j;
+
+ for (i = 0; i < numMerge; i++) {
+ IndexedGeometryStripArrayRetained geo = (IndexedGeometryStripArrayRetained) list.get(i);
+ numCount += geo.stripIndexCounts.length;
+ }
+
+ stripIndexCounts = new int[numCount];
+ compileIndexLength = new int[numCount];
+ compileStripICOffset = new int[numMerge];
+ int curICOffset = 0;
+ for (i = 0; i < numMerge; i++) {
+ IndexedGeometryStripArrayRetained geo = (IndexedGeometryStripArrayRetained) list.get(i);
+ compileStripICOffset[i] = curICOffset;
+ compileIndexLength[i] = geo.stripIndexCounts.length;
+ System.arraycopy(geo.stripIndexCounts, 0, stripIndexCounts,
+ curICOffset, geo.stripIndexCounts.length);
+ curICOffset += geo.stripIndexCounts.length;
+ }
+ super.mergeGeometryArrays(list);
+
+ }
+ int getNumStrips(int id){
+ return compileIndexLength[id];
+ }
+
+ /**
+ * Get a list of vertexCounts for each strip
+ * @param stripIndexCounts an array that will receive vertexCounts
+ */
+ void getStripIndexCounts(int id, int stripIndexCounts[]){
+ int count = compileIndexLength[id];
+ int coffset = compileStripICOffset[id];
+ for (int i=0;i < count; i++) {
+ stripIndexCounts[i] = this.stripIndexCounts[coffset+1];
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedLineArray.java b/src/classes/share/javax/media/j3d/IndexedLineArray.java
new file mode 100644
index 0000000..e9723aa
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedLineArray.java
@@ -0,0 +1,170 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedLineArray object draws the array of vertices as individual
+ * line segments. Each pair of vertices defines a line to be drawn.
+ */
+
+public class IndexedLineArray extends IndexedGeometryArray {
+ /**
+ * Package scoped default constructor.
+ */
+ IndexedLineArray() {
+ }
+
+ /**
+ * Constructs an empty IndexedLineArray object with the specified
+ * number of vertices, vertex format, and number of indices.
+ * @param vertexCount the number of vertex elements in this object
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 2, or indexCount is <i>not</i>
+ * a multiple of 2
+ */
+ public IndexedLineArray(int vertexCount, int vertexFormat, int indexCount) {
+ super(vertexCount,vertexFormat, indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray0"));
+
+ if (indexCount < 2 || ((indexCount%2) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray1"));
+ }
+
+ /**
+ * Constructs an empty IndexedLineArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and number of indices.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3 or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 2, or indexCount is <i>not</i>
+ * a multiple of 2
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedLineArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray0"));
+
+ if (indexCount < 2 || ((indexCount%2) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray1"));
+ }
+
+ /**
+ * Creates the retained mode IndexedLineArrayRetained object that this
+ * IndexedLineArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new IndexedLineArrayRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ IndexedLineArrayRetained rt = (IndexedLineArrayRetained) retained;
+ int texSetCount = rt.getTexCoordSetCount();
+ IndexedLineArray l;
+ if (texSetCount == 0) {
+ l = new IndexedLineArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ rt.getIndexCount());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ l = new IndexedLineArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ rt.getIndexCount());
+ }
+ l.duplicateNodeComponent(this);
+ return l;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java
new file mode 100644
index 0000000..b613e54
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java
@@ -0,0 +1,335 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The IndexedLineArray object draws the array of vertices as individual
+ * line segments. Each pair of vertices defines a line to be drawn.
+ */
+
+class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
+
+ IndexedLineArrayRetained() {
+ this.geoType = GEO_TYPE_INDEXED_LINE_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[2];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+
+ if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
+ pickRay.direction, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ Vector3d dir =
+ new Vector3d(pickSegment.end.x - pickSegment.start.x,
+ pickSegment.end.y - pickSegment.start.y,
+ pickSegment.end.z - pickSegment.start.z);
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectLineAndRay(pnts[0], pnts[1],
+ pickSegment.start,
+ dir, sdist, iPnt) &&
+ (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+
+ }
+
+ boolean intersect(Point3d[] pnts) {
+ Point3d[] points = new Point3d[2];
+ Vector3d dir;
+ double dist[] = new double[1];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle/Quad , common case first
+ case 4:
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ if (intersectSegment(pnts, points[0], points[1], dist,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ dir = new Vector3d();
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectLineAndRay(pnts[0], pnts[1], points[0],
+ dir, dist, null) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ dir = new Vector3d();
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectPntAndRay(pnts[0], points[0], dir, dist) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+
+ Point3d[] pnts = new Point3d[2];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ Point3d[] pnts = new Point3d[2];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while(i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while(i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, null, null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while(i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectBoundingPolytope(pnts, bpolytope, null, null)) {
+ return true;
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ int getClassType() {
+ return LINE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedLineStripArray.java b/src/classes/share/javax/media/j3d/IndexedLineStripArray.java
new file mode 100644
index 0000000..52760a8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedLineStripArray.java
@@ -0,0 +1,197 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedLineStripArray object draws an array of vertices as a set of
+ * connected line strips. An array of per-strip index counts specifies
+ * where the separate strips appear in the indexed vertex array.
+ * For every strip in the set, each vertex, beginning with
+ * the second vertex in the array, defines a line segment to be drawn
+ * from the previous vertex to the current vertex.
+ */
+
+public class IndexedLineStripArray extends IndexedGeometryStripArray {
+
+ /**
+ * Package scoped default constructor.
+ */
+ IndexedLineStripArray() {
+ }
+
+ /**
+ * Constructs an empty IndexedLineStripArray object with the specified
+ * number of vertices, vertex format, number of indices, and
+ * array of per-strip index counts.
+ * @param vertexCount the number of vertex elements in this object
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 2,
+ * or any element in the stripIndexCounts array is less than 2
+ */
+ public IndexedLineStripArray(int vertexCount,
+ int vertexFormat,
+ int indexCount,
+ int stripIndexCounts[]) {
+
+ super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArray0"));
+
+ if (indexCount < 2 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArray1"));
+ }
+
+ /**
+ * Constructs an empty IndexedLineStripArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, number of indices, and
+ * array of per-strip index counts.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.<p>
+ *
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 2,
+ * or any element in the stripIndexCounts array is less than 2
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedLineStripArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount,
+ int stripIndexCounts[]) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount, stripIndexCounts);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArray0"));
+
+ if (indexCount < 2 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArray1"));
+ }
+
+ /**
+ * Creates the retained mode IndexedLineStripArrayRetained object that this
+ * IndexedLineStripArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new IndexedLineStripArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ IndexedLineStripArrayRetained rt =
+ (IndexedLineStripArrayRetained) retained;
+ int stripIndexCounts[] = new int[rt.getNumStrips()];
+ rt.getStripIndexCounts(stripIndexCounts);
+ int texSetCount = rt.getTexCoordSetCount();
+ IndexedLineStripArray l;
+
+ if (texSetCount == 0) {
+ l = new IndexedLineStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ rt.getIndexCount(),
+ stripIndexCounts);
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ l = new IndexedLineStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ rt.getIndexCount(),
+ stripIndexCounts);
+
+ }
+ l.duplicateNodeComponent(this);
+ return l;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java
new file mode 100644
index 0000000..846c959
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java
@@ -0,0 +1,412 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The IndexedLineStripArray object draws an array of vertices as a set of
+ * connected line strips. An array of per-strip vertex counts specifies
+ * where the separate strips appear in the vertex array.
+ * For every strip in the set, each vertex, beginning with
+ * the second vertex in the array, defines a line segment to be drawn
+ * from the previous vertex to the current vertex.
+ */
+
+class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
+
+ IndexedLineStripArrayRetained() {
+ geoType = GEO_TYPE_INDEXED_LINE_STRIP_SET;
+ }
+
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[2];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int scount, j, i = 0;
+ int count = 0;
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+ if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
+ pickRay.direction, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ Vector3d dir =
+ new Vector3d(pickSegment.end.x - pickSegment.start.x,
+ pickSegment.end.y - pickSegment.start.y,
+ pickSegment.end.z - pickSegment.start.z);
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+ if (intersectLineAndRay(pnts[0], pnts[1],
+ pickSegment.start,
+ dir, sdist, iPnt) &&
+ (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+
+ if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+
+ }
+
+ // intersect pnts[] with every triangle in this object
+ boolean intersect(Point3d[] pnts) {
+ int i = 0;
+ int j, count=0;
+ int scount;
+ Point3d[] points = new Point3d[2];
+ double dist[] = new double[1];
+ Vector3d dir;
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+
+ switch (pnts.length) {
+ case 3:
+ case 4: // Triangle, Quad
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ scount = stripIndexCounts[i++];
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[1]);
+ if (intersectSegment(pnts, points[0], points[1],
+ dist, null)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ }
+ }
+ break;
+ case 2: // line
+ dir = new Vector3d();
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ scount = stripIndexCounts[i++];
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectLineAndRay(pnts[0], pnts[1],
+ points[0], dir, dist, null)
+ && (dist[0] <= 1.0)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ }
+ }
+ break;
+ case 1: // point
+ dir = new Vector3d();
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ scount = stripIndexCounts[i++];
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectPntAndRay(pnts[0], points[0], dir,
+ dist) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ }
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+ int i = 0;
+ int j, count=0;
+ Point3d[] pnts = new Point3d[2];
+ int scount;
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ thisToOtherVworld.transform(pnts[0]);
+ scount = stripIndexCounts[i++];
+
+ for (j = 1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+ thisToOtherVworld.transform(pnts[1]);
+ if (geom.intersect( pnts)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = 0;
+ int j, count=0;
+ Point3d[] pnts = new Point3d[2];
+ int scount;
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ scount = stripIndexCounts[i++];
+ for (j=1; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[1]);
+ if (intersectBoundingPolytope(pnts, bpolytope, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ int getClassType() {
+ return LINE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedObject.java b/src/classes/share/javax/media/j3d/IndexedObject.java
new file mode 100644
index 0000000..712d5d5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedObject.java
@@ -0,0 +1,54 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class used for IndexedUnorderedList
+ */
+
+abstract class IndexedObject extends Object {
+
+ /**
+ * A 2D array listIdx[3][len] is used.
+ * The entry listIdx[0][], listIdx[0][1] is used for each VirtualUniverse.
+ * The entry listIdx[2][0] is used for index to which one to use.
+ *
+ * This is used to handle the case the Node Object move from
+ * one VirtualUniverse A to another VirtualUniverse B.
+ * It is possible that another Structures in B may get the add
+ * message first before the Structures in A get the remove
+ * message to clear the entry. This cause MT problem. So a
+ * 2D array is used to resolve it.
+ */
+ int[][] listIdx;
+
+ abstract VirtualUniverse getVirtualUniverse();
+
+ synchronized int getIdxUsed(VirtualUniverse u) {
+ int idx = listIdx[2][0];
+ if (u == getVirtualUniverse()) {
+ return idx;
+ }
+ return (idx == 0 ? 1 : 0);
+ }
+
+ void incIdxUsed() {
+ if (listIdx[2][0] == 0) {
+ listIdx[2][0] = 1;
+ } else {
+ listIdx[2][0] = 0;
+ }
+ }
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/IndexedPointArray.java b/src/classes/share/javax/media/j3d/IndexedPointArray.java
new file mode 100644
index 0000000..2703595
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedPointArray.java
@@ -0,0 +1,170 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedPointArray object draws the array of vertices as
+ * individual points.
+ */
+
+public class IndexedPointArray extends IndexedGeometryArray {
+
+ /**
+ * Package scoped default constructor.
+ */
+ IndexedPointArray() {
+ }
+
+ /**
+ * Constructs an empty IndexedPointArray object with the specified
+ * number of vertices, vertex format, and number of indices.
+ * @param vertexCount the number of vertex elements in this object
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @exception IllegalArgumentException if vertexCount is less than 1
+ * or indexCount is less than 1
+ */
+ public IndexedPointArray(int vertexCount, int vertexFormat, int indexCount) {
+ super(vertexCount,vertexFormat, indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArray0"));
+
+ if (indexCount < 1 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArray1"));
+ }
+
+ /**
+ * Constructs an empty IndexedPointArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and number of indices.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3 or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1
+ * or indexCount is less than 1
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedPointArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArray0"));
+
+ if (indexCount < 1 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArray1"));
+ }
+
+ /**
+ * Creates the retained mode IndexedPointArrayRetained object that this
+ * IndexedPointArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new IndexedPointArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ IndexedPointArrayRetained rt = (IndexedPointArrayRetained) retained;
+ int texSetCount = rt.getTexCoordSetCount();
+ IndexedPointArray p;
+ if (texSetCount == 0) {
+ p = new IndexedPointArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ rt.getIndexCount());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ p = new IndexedPointArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ rt.getIndexCount());
+ }
+ p.duplicateNodeComponent(this);
+ return p;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java
new file mode 100644
index 0000000..14b0710
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java
@@ -0,0 +1,247 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The IndexedPointArray object draws the array of vertices as individual points.
+ */
+
+class IndexedPointArrayRetained extends IndexedGeometryArrayRetained {
+
+ IndexedPointArrayRetained() {
+ this.geoType = GEO_TYPE_INDEXED_POINT_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ Point3d pnt = new Point3d();
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnt);
+ if (intersectPntAndRay(pnt, pickRay.origin,
+ pickRay.direction, sdist)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ Vector3d dir =
+ new Vector3d(pickSegment.end.x - pickSegment.start.x,
+ pickSegment.end.y - pickSegment.start.y,
+ pickSegment.end.z - pickSegment.start.z);
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnt);
+ if (intersectPntAndRay(pnt, pickSegment.start,
+ dir, sdist) &&
+ (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ case PickShape.PICKBOUNDINGSPHERE:
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ Bounds bounds = ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnt);
+ if (bounds.intersect(pnt)) {
+ if (dist == null) {
+ return true;
+ }
+ sdist[0] = pickShape.distance(pnt);
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnt);
+
+ if (intersectCylinder(pnt, pickCylinder, sdist)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnt);
+
+ if (intersectCone(pnt, pickCone, sdist)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+ }
+
+ boolean intersect(Point3d[] pnts) {
+ Point3d point = new Point3d();
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], point);
+ if (intersectTriPnt(pnts[0], pnts[1], pnts[2], point)) {
+ return true;
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], point);
+ if (intersectTriPnt(pnts[0], pnts[1], pnts[2], point) ||
+ intersectTriPnt(pnts[0], pnts[2], pnts[3], point)) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ double dist[] = new double[1];
+ Vector3d dir = new Vector3d();
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], point);
+ dir.x = pnts[1].x - pnts[0].x;
+ dir.y = pnts[1].y - pnts[0].y;
+ dir.z = pnts[1].z - pnts[0].z;
+ if (intersectPntAndRay(point, pnts[0], dir, dist) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], point);
+ if ((pnts[0].x == point.x) &&
+ (pnts[0].y == point.y) &&
+ (pnts[0].z == point.z)) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+ Point3d[] pnt = new Point3d[1];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnt[0] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnt[0]);
+ thisToOtherVworld.transform(pnt[0]);
+ if (geom.intersect(pnt)) {
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ Point3d pnt = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnt);
+ if (targetBound.intersect(pnt)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int getClassType() {
+ return POINT_TYPE;
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/IndexedQuadArray.java b/src/classes/share/javax/media/j3d/IndexedQuadArray.java
new file mode 100644
index 0000000..d4d1610
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedQuadArray.java
@@ -0,0 +1,174 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedQuadArray object draws the array of vertices as individual
+ * quadrilaterals. Each group
+ * of four vertices defines a quadrilateral to be drawn.
+ */
+
+public class IndexedQuadArray extends IndexedGeometryArray {
+
+ /**
+ * Package scoped default constructor.
+ */
+ IndexedQuadArray() {
+ }
+
+ /**
+ * Constructs an empty IndexedQuadArray object with the specified
+ * number of vertices, vertex format, and number of indices.
+ * @param vertexCount the number of vertex elements in this object
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 4, or indexCount is <i>not</i>
+ * a multiple of 4
+ */
+ public IndexedQuadArray(int vertexCount, int vertexFormat, int indexCount) {
+ super(vertexCount,vertexFormat, indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArray0"));
+
+ if (indexCount < 4 || ((indexCount%4) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArray1"));
+ }
+
+ /**
+ * Constructs an empty IndexedQuadArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and number of indices.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 4, or indexCount is <i>not</i>
+ * a multiple of 4
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedQuadArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArray0"));
+
+ if (indexCount < 4 || ((indexCount%4) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArray1"));
+ }
+
+ /**
+ * Creates the retained mode IndexedQuadArrayRetained object that this
+ * IndexedQuadArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new IndexedQuadArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ IndexedQuadArrayRetained rt = (IndexedQuadArrayRetained) retained;
+ int texSetCount = rt.getTexCoordSetCount();
+ IndexedQuadArray q;
+
+ if (texSetCount == 0) {
+ q = new IndexedQuadArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ rt.getIndexCount());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ q = new IndexedQuadArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ rt.getIndexCount());
+ }
+ q.duplicateNodeComponent(this);
+ return q;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java
new file mode 100644
index 0000000..26d885e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java
@@ -0,0 +1,387 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The IndexedQuadArray object draws the array of vertices as individual
+ * quadrilaterals. Each group
+ * of four vertices defines a quadrilateral to be drawn.
+ */
+
+class IndexedQuadArrayRetained extends IndexedGeometryArrayRetained {
+
+ IndexedQuadArrayRetained() {
+ this.geoType = GEO_TYPE_INDEXED_QUAD_SET;
+ }
+
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[4];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+ pnts[3] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ getVertexData(indexCoord[i++], pnts[3]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ getVertexData(indexCoord[i++], pnts[3]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ getVertexData(indexCoord[i++], pnts[3]);
+
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ getVertexData(indexCoord[i++], pnts[3]);
+
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ getVertexData(indexCoord[i++], pnts[3]);
+
+ if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ getVertexData(indexCoord[i++], pnts[3]);
+
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ getVertexData(indexCoord[i++], pnts[3]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArrayRetained0"));
+ default:
+ throw new RuntimeException("PickShape not supported for intersection ");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+
+ }
+
+
+ // intersect pnts[] with every quad in this object
+ boolean intersect(Point3d[] pnts) {
+ Point3d[] points = new Point3d[4];
+ double dist[] = new double[1];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+ points[3] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[2], points[3],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3]) ||
+ intersectTriTri(points[0], points[2], points[3],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[2], points[3],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ if (intersectSegment(points, pnts[0], pnts[1], dist,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0]) ||
+ intersectTriPnt(points[0], points[2], points[3],
+ pnts[0])) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+
+ Point3d[] points = new Point3d[4];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+ points[3] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ thisToOtherVworld.transform(points[0]);
+ thisToOtherVworld.transform(points[1]);
+ thisToOtherVworld.transform(points[2]);
+ thisToOtherVworld.transform(points[3]);
+ if (geom.intersect(points)) {
+ return true;
+ }
+ } // for each quad
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ Point3d[] points = new Point3d[4];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+ points[3] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ if (intersectBoundingBox(points, box, null, null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ if (intersectBoundingSphere(points, bsphere, null,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ getVertexData(indexCoord[i++], points[3]);
+ if (intersectBoundingPolytope(points, bpolytope, null, null)) {
+ return true;
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+
+ int getClassType() {
+ return QUAD_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleArray.java b/src/classes/share/javax/media/j3d/IndexedTriangleArray.java
new file mode 100644
index 0000000..ab4c8d5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleArray.java
@@ -0,0 +1,175 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedTriangleArray object draws the array of vertices as individual
+ * triangles. Each group
+ * of three vertices defines a triangle to be drawn.
+ */
+
+public class IndexedTriangleArray extends IndexedGeometryArray {
+
+ /**
+ * Package scoped default constructor.
+ */
+ IndexedTriangleArray() {
+ }
+
+ /**
+ * Constructs an empty IndexedTriangleArray object with the specified
+ * number of vertices, vertex format, and number of indices.
+ * @param vertexCount the number of vertex elements in this object
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 3, or indexCount is <i>not</i>
+ * a multiple of 3
+ */
+ public IndexedTriangleArray(int vertexCount, int vertexFormat,
+ int indexCount) {
+ super(vertexCount,vertexFormat, indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray0"));
+
+ if (indexCount < 3 || ((indexCount%3) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray1"));
+ }
+
+ /**
+ * Constructs an empty IndexedTriangleArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and number of indices.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 3, or indexCount is <i>not</i>
+ * a multiple of 3
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedTriangleArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray0"));
+
+ if (indexCount < 3 || ((indexCount%3) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray1"));
+ }
+
+ /**
+ * Creates the retained mode IndexedTriangleArrayRetained object that this
+ * IndexedTriangleArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new IndexedTriangleArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ IndexedTriangleArrayRetained rt = (IndexedTriangleArrayRetained) retained;
+ int texSetCount = rt.getTexCoordSetCount();
+ IndexedTriangleArray t;
+
+ if (texSetCount == 0) {
+ t = new IndexedTriangleArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ rt.getIndexCount());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ t = new IndexedTriangleArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ rt.getIndexCount());
+ }
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java
new file mode 100644
index 0000000..cd5ebe5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java
@@ -0,0 +1,350 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The IndexedTriangleArray object draws the array of vertices as individual
+ * triangles. Each group
+ * of three vertices defines a triangle to be drawn.
+ */
+
+class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
+
+ IndexedTriangleArrayRetained() {
+ this.geoType = GEO_TYPE_INDEXED_TRI_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[3];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)
+ && (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ sdist,iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectCylinder(pnts, pickCylinder, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+ }
+
+
+ // intersect pnts[] with every triangle in this object
+ boolean intersect(Point3d[] pnts) {
+ Point3d[] points = new Point3d[3];
+ double dist[] = new double[1];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i<validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i<validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ while (i<validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ if (intersectSegment(points, pnts[0], pnts[1], dist,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ while (i<validVertexCount) {
+ getVertexData(indexCoord[i++], points[0]);
+ getVertexData(indexCoord[i++], points[1]);
+ getVertexData(indexCoord[i++], points[2]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0])) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) {
+ Point3d[] pnts = new Point3d[3];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ thisToOtherVworld.transform(pnts[2]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ Point3d[] pnts = new Point3d[3];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, null,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(indexCoord[i++], pnts[0]);
+ getVertexData(indexCoord[i++], pnts[1]);
+ getVertexData(indexCoord[i++], pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ null, null)) {
+ return true;
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ int getClassType() {
+ return TRIANGLE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java b/src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java
new file mode 100644
index 0000000..030cac7
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java
@@ -0,0 +1,197 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedTriangleFanArray object draws an array of vertices as a set of
+ * connected triangle fans. An array of per-strip
+ * index counts specifies where the separate strips (fans) appear
+ * in the indexed vertex array. For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex,
+ * the previous vertex and the first vertex. This can be thought of
+ * as a collection of convex polygons.
+ */
+
+public class IndexedTriangleFanArray extends IndexedGeometryStripArray {
+
+ // non-public, no parameter constructor
+ IndexedTriangleFanArray() {}
+
+ /**
+ * Constructs an empty IndexedTriangleFanArray object with the specified
+ * number of vertices, vertex format, number of indices, and
+ * array of per-strip index counts.
+ * @param vertexCount the number of vertex elements in this object
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 3,
+ * or any element in the stripIndexCounts array is less than 3
+ */
+ public IndexedTriangleFanArray(int vertexCount,
+ int vertexFormat,
+ int indexCount,
+ int stripIndexCounts[]) {
+
+ super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray0"));
+
+ if (indexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray1"));
+ }
+
+ /**
+ * Constructs an empty IndexedTriangleFanArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, number of indices, and
+ * array of per-strip index counts.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3 or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.<p>
+ *
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 3,
+ * or any element in the stripIndexCounts array is less than 3
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedTriangleFanArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount,
+ int stripIndexCounts[]) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount, stripIndexCounts);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray0"));
+
+ if (indexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray1"));
+ }
+
+ /**
+ * Creates the retained mode IndexedTriangleFanArrayRetained object that this
+ * IndexedTriangleFanArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new IndexedTriangleFanArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ IndexedTriangleFanArrayRetained rt =
+ (IndexedTriangleFanArrayRetained) retained;
+ int stripIndexCounts[] = new int[rt.getNumStrips()];
+ rt.getStripIndexCounts(stripIndexCounts);
+ int texSetCount = rt.getTexCoordSetCount();
+ IndexedTriangleFanArray t;
+
+ if (texSetCount == 0) {
+ t = new IndexedTriangleFanArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ rt.getIndexCount(),
+ stripIndexCounts);
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ t = new IndexedTriangleFanArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ rt.getIndexCount(),
+ stripIndexCounts);
+ }
+
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java
new file mode 100644
index 0000000..484f5b5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java
@@ -0,0 +1,415 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The IndexedTriangleFanArray object draws an array of vertices as a set of
+ * connected triangle fans. An array of per-strip
+ * vertex counts specifies where the separate strips (fans) appear
+ * in the vertex array. For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex,
+ * the previous vertex and the first vertex. This can be thought of
+ * as a collection of convex polygons.
+ */
+
+class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained {
+
+ IndexedTriangleFanArrayRetained(){
+ geoType = GEO_TYPE_INDEXED_TRI_FAN_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[3];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = 0;
+ int j, scount, count = 0;
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(j++, pnts[2]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+ }
+
+ // intersect pnts[] with every triangle in this object
+ boolean intersect(Point3d[] pnts) {
+ int j, end;
+ Point3d[] points = new Point3d[3];
+ double dist[] = new double[1];
+ int i = 0, scount, count = 0;
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 2: // Line
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectSegment(points, pnts[0], pnts[1],
+ dist, null)) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0])) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) {
+ int i = 0, j, scount, count = 0;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ thisToOtherVworld.transform(pnts[2]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = 0;
+ int j, scount, count = 0;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, null, null)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope, null, null)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ int getClassType() {
+ return TRIANGLE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java b/src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java
new file mode 100644
index 0000000..5957e99
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java
@@ -0,0 +1,196 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The IndexedTriangleStripArray object draws an array of vertices as a set of
+ * connected triangle strips. An array of per-strip index counts specifies
+ * where the separate strips appear in the indexed vertex array.
+ * For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex and
+ * the two previous vertices.
+ */
+
+public class IndexedTriangleStripArray extends IndexedGeometryStripArray {
+
+ /**
+ * Package scoped default constructor
+ */
+ IndexedTriangleStripArray() {
+ }
+
+ /**
+ * Constructs an empty IndexedTriangleStripArray object with the specified
+ * number of vertices, vertex format, number of indices, and
+ * array of per-strip index counts.
+ * @param vertexCount the number of vertex elements in this object
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 3,
+ * or any element in the stripIndexCounts array is less than 3
+ */
+ public IndexedTriangleStripArray(int vertexCount,
+ int vertexFormat,
+ int indexCount,
+ int stripIndexCounts[]) {
+
+ super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArray0"));
+
+ if (indexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArray1"));
+ }
+
+ /**
+ * Constructs an empty IndexedTriangleStripArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, number of indices, and
+ * array of per-strip index counts.
+ *
+ * @param vertexCount the number of vertex elements in this object<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code> or
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param indexCount the number of indices in this object. This
+ * count is the maximum number of vertices that will be rendered.<p>
+ *
+ * @param stripIndexCounts array that specifies
+ * the count of the number of indices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1,
+ * or indexCount is less than 3,
+ * or any element in the stripIndexCounts array is less than 3
+ *
+ * @since Java 3D 1.2
+ */
+ public IndexedTriangleStripArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int indexCount,
+ int stripIndexCounts[]) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ indexCount, stripIndexCounts);
+
+ if (vertexCount < 1)
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArray0"));
+
+ if (indexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArray1"));
+ }
+
+ /**
+ * Creates the retained mode IndexedTriangleStripArrayRetained object that this
+ * IndexedTriangleStripArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new IndexedTriangleStripArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ IndexedTriangleStripArrayRetained rt =
+ (IndexedTriangleStripArrayRetained) retained;
+ int stripIndexCounts[] = new int[rt.getNumStrips()];
+ rt.getStripIndexCounts(stripIndexCounts);
+ int texSetCount = rt.getTexCoordSetCount();
+ IndexedTriangleStripArray l;
+ if (texSetCount == 0) {
+ l = new IndexedTriangleStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ rt.getIndexCount(),
+ stripIndexCounts);
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ l = new IndexedTriangleStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ rt.getIndexCount(),
+ stripIndexCounts);
+ }
+ l.duplicateNodeComponent(this);
+ return l;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java
new file mode 100644
index 0000000..493c5e9
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java
@@ -0,0 +1,430 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The IndexedTriangleStripArray object draws an array of vertices as a set of
+ * connected triangle strips. An array of per-strip vertex counts specifies
+ * where the separate strips appear in the vertex array.
+ * For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex and
+ * the two previous vertices.
+ */
+
+class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetained {
+
+ IndexedTriangleStripArrayRetained(){
+ geoType = GEO_TYPE_INDEXED_TRI_STRIP_SET;
+ }
+
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[3];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = 0;
+ int j, scount, count = 0;
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(j++, pnts[2]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+
+ for (j=2; j < scount; j++) {
+ getVertexData(count++, pnts[2]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+ }
+
+ // intersect pnts[] with every triangle in this object
+ boolean intersect(Point3d[] pnts) {
+ int j, end;
+ Point3d[] points = new Point3d[3];
+ double dist[] = new double[1];
+ int i = 0, scount, count = 0;
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 2: // Line
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectSegment(points, pnts[0], pnts[1],
+ dist, null)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], points[0]);
+ getVertexData(indexCoord[count++], points[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], points[2]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0])) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) {
+ int i = 0, j, scount, count = 0;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ thisToOtherVworld.transform(pnts[2]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = 0;
+ int j, scount, count = 0;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < stripIndexCounts.length) {
+ getVertexData(indexCoord[count++], pnts[0]);
+ getVertexData(indexCoord[count++], pnts[1]);
+ scount = stripIndexCounts[i++];
+ for (j=2; j < scount; j++) {
+ getVertexData(indexCoord[count++], pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ int getClassType() {
+ return TRIANGLE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/IndexedUnorderSet.java b/src/classes/share/javax/media/j3d/IndexedUnorderSet.java
new file mode 100644
index 0000000..01910af
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IndexedUnorderSet.java
@@ -0,0 +1,598 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A strongly type indexed unorder set.
+ * All operations remove(IndexedObject, ListType), add(IndexedObject, ListType),
+ * contains(IndexedObject, ListType) etc. take O(1) time.
+ * The class is designed to optimize speed. So many reductance
+ * procedures call and range check as found in ArrayList are
+ * removed.
+ *
+ * <p>
+ * Use the following code to iterate through an array.
+ *
+ * <pre>
+ * IndexedUnorderSet IUset =
+ * new IndexedUnorderSet(YourClass.class, listType);
+ * // add element here
+ *
+ * YourClass[] arr = (YourClass []) IUset.toArray();
+ * int size = IUset.arraySize();
+ * for (int i=0; i < size; i++) {
+ * YourClass obj = arr[i];
+ * ....
+ * }
+ * </pre>
+ *
+ * <p>
+ * Note:
+ * <ul>
+ * 1) The array return is a copied of internal array.<br>
+ * 2) Don't use arr.length , use IUset.arraySize();<br>
+ * 3) IndexedObject contains an array of listIndex, the number of
+ * array elements depends on the number of different types of
+ * IndexedUnorderSet that use it.<br>
+ * 4) No need to do casting for individual element as in ArrayList.<br>
+ * 5) IndexedUnorderSet is thread safe.<br>
+ * 6) Object implement this interface MUST initialize the index to -1.<br>
+ * </ul>
+ *
+ * <p>
+ * Limitation:
+ * <ul>
+ * 1) Order of IndexedObject in list is not important<br>
+ * 2) Can't modify the clone() copy.<br>
+ * 3) IndexedObject can't be null<br>
+ * </ul>
+ */
+
+class IndexedUnorderSet implements Cloneable, java.io.Serializable {
+
+ // TODO: set to false when release
+ final static boolean debug = false;
+
+ /**
+ * The array buffer into which the elements of the ArrayList are stored.
+ * The capacity of the ArrayList is the length of this array buffer.
+ *
+ * It is non-private to enable compiler do inlining for get(),
+ * set(), remove() when -O flag turn on.
+ */
+ transient IndexedObject elementData[];
+
+ /**
+ * Clone copy of elementData return by toArray(true);
+ */
+ transient Object cloneData[];
+ // size of the above clone objec.
+ transient int cloneSize;
+
+ transient boolean isDirty = true;
+
+ /**
+ * Component Type of individual array element entry
+ */
+ Class componentType;
+
+ /**
+ * The size of the ArrayList (the number of elements it contains).
+ *
+ * We make it non-private to enable compiler do inlining for
+ * getSize() when -O flag turn on.
+ */
+ int size;
+
+ int listType;
+
+ // Current VirtualUniverse using this structure
+ VirtualUniverse univ;
+
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ * and the class data Type
+ *
+ * @param initialCapacity the initial capacity of the list.
+ * @param componentType class type of element in the list.
+ */
+ IndexedUnorderSet(int initialCapacity, Class componentType,
+ int listType, VirtualUniverse univ) {
+ this.componentType = componentType;
+ this.elementData = (IndexedObject[])java.lang.reflect.Array.newInstance(
+ componentType, initialCapacity);
+ this.listType = listType;
+ this.univ = univ;
+ }
+
+ /**
+ * Constructs an empty list.
+ * @param componentType class type of element in the list.
+ */
+ IndexedUnorderSet(Class componentType, int listType,
+ VirtualUniverse univ) {
+ this(10, componentType, listType, univ);
+ }
+
+
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ *
+ * @param initialCapacity the initial capacity of the list.
+ */
+ IndexedUnorderSet(int initialCapacity, int listType,
+ VirtualUniverse univ) {
+ this(initialCapacity, IndexedObject.class, listType, univ);
+ }
+
+ /**
+ * Constructs an empty list.
+ * @param listType default to Object.
+ */
+ IndexedUnorderSet(int listType, VirtualUniverse univ) {
+ this(10, IndexedObject.class, listType, univ);
+ }
+
+ /**
+ * Initialize all indexes to -1
+ */
+ final static void init(IndexedObject obj, int len) {
+ obj.listIdx = new int[3][];
+
+ obj.listIdx[0] = new int[len];
+ obj.listIdx[1] = new int[len];
+ obj.listIdx[2] = new int[1];
+
+ for (int i=0; i < len; i++) {
+ obj.listIdx[0][i] = -1;
+ obj.listIdx[1][i] = -1;
+ }
+
+ // Just want to set both RenderMolecule idx
+ // and BehaviorRetained idx to 0 by default
+ // It is OK without the following lines
+ if (obj instanceof SceneGraphObjectRetained) {
+ // setlive() will change this back to 0
+ obj.listIdx[2][0] = 1;
+ } else {
+ obj.listIdx[2][0] = 0;
+ }
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ final int size() {
+ return size;
+ }
+
+
+ /**
+ * Returns the size of entry use in toArray() number of elements
+ * in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ final int arraySize() {
+ return cloneSize;
+ }
+
+ /**
+ * Tests if this list has no elements.
+ *
+ * @return <tt>true</tt> if this list has no elements;
+ * <tt>false</tt> otherwise.
+ */
+ final boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this list contains the specified element.
+ *
+ * @param o element whose presence in this List is to be tested.
+ */
+ synchronized final boolean contains(IndexedObject o) {
+ return (o.listIdx[o.getIdxUsed(univ)][listType] >= 0);
+ }
+
+
+ /**
+ * Searches for the last occurence of the given argument, testing
+ * for equality using the <tt>equals</tt> method.
+ *
+ * @param o an object.
+ * @return the index of the first occurrence of the argument in this
+ * list; returns <tt>-1</tt> if the object is not found.
+ * @see Object#equals(Object)
+ */
+ synchronized final int indexOf(IndexedObject o) {
+ return o.listIdx[o.getIdxUsed(univ)][listType];
+ }
+
+ /**
+ * Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
+ * elements themselves are not copied.)
+ *
+ * @return a clone of this <tt>ArrayList</tt> instance.
+ */
+ synchronized protected final Object clone() {
+ try {
+ IndexedUnorderSet v = (IndexedUnorderSet)super.clone();
+ v.elementData = (IndexedObject[])java.lang.reflect.Array.newInstance(
+ componentType, size);
+ System.arraycopy(elementData, 0, v.elementData, 0, size);
+ isDirty = true; // can't use the old cloneData reference
+ return v;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+ }
+
+
+ /**
+ * Returns an array containing all of the elements in this list.
+ * The size of the array may longer than the actual size. Use
+ * arraySize() to retrieve the size.
+ * The array return is a copied of internal array. if copy
+ * is true.
+ *
+ * @return an array containing all of the elements in this list
+ */
+ synchronized final Object[] toArray(boolean copy) {
+ if (copy) {
+ if (isDirty) {
+ if ((cloneData == null) || cloneData.length < size) {
+ cloneData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType, size);
+ }
+ System.arraycopy(elementData, 0, cloneData, 0, size);
+ cloneSize = size;
+ isDirty = false;
+ }
+ return cloneData;
+ } else {
+ cloneSize = size;
+ return elementData;
+ }
+
+ }
+
+ /**
+ * Returns an array containing all of the elements in this list.
+ * The size of the array may longer than the actual size. Use
+ * arraySize() to retrieve the size.
+ * The array return is a copied of internal array. So another
+ * thread can continue add/delete the current list. However,
+ * it should be noticed that two call to toArray() may return
+ * the same copy.
+ *
+ * @return an array containing all of the elements in this list
+ */
+ synchronized final Object[] toArray() {
+ return toArray(true);
+ }
+
+
+ /**
+ * Returns an array containing elements starting from startElement
+ * all of the elements in this list. A new array of exact size
+ * is always allocated.
+ *
+ * @param startElement starting element to copy
+ *
+ * @return an array containing elements starting from
+ * startElement, null if element not found.
+ *
+ */
+ synchronized final Object[] toArray(IndexedObject startElement) {
+ int idx = indexOf(startElement);
+ if (idx < 0) {
+ return (Object[])java.lang.reflect.Array.newInstance(componentType, 0);
+ }
+
+ int s = size - idx;
+ Object data[] = (Object[])java.lang.reflect.Array.newInstance(componentType, s);
+ System.arraycopy(elementData, idx, data, 0, s);
+ return data;
+ }
+
+ /**
+ * Trims the capacity of this <tt>ArrayList</tt> instance to be the
+ * list's current size. An application can use this operation to minimize
+ * the storage of an <tt>ArrayList</tt> instance.
+ */
+ synchronized final void trimToSize() {
+ if (elementData.length > size) {
+ Object oldData[] = elementData;
+ elementData = (IndexedObject[])java.lang.reflect.Array.newInstance(
+ componentType,
+ size);
+ System.arraycopy(oldData, 0, elementData, 0, size);
+ }
+ }
+
+
+ // Positional Access Operations
+
+ /**
+ * Returns the element at the specified position in this list.
+ *
+ * @param index index of element to return.
+ * @return the element at the specified position in this list.
+ * @throws IndexOutOfBoundsException if index is out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final Object get(int index) {
+ return elementData[index];
+ }
+
+ /**
+ * Replaces the element at the specified position in this list with
+ * the specified element.
+ *
+ * @param index index of element to replace.
+ * @param o element to be stored at the specified position.
+ * @return the element previously at the specified position.
+ * @throws IndexOutOfBoundsException if index out of range
+ * <tt>(index &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final void set(int index, IndexedObject o) {
+ IndexedObject oldElm = elementData[index];
+ if (oldElm != null) {
+ oldElm.listIdx[oldElm.getIdxUsed(univ)][listType] = -1;
+ }
+ elementData[index] = o;
+
+ int univIdx = o.getIdxUsed(univ);
+
+ if (debug) {
+ if (o.listIdx[univIdx][listType] != -1) {
+ System.out.println("Illegal use of UnorderIndexedList idx in set " +
+ o.listIdx[univIdx][listType]);
+ Thread.dumpStack();
+ }
+ }
+
+ o.listIdx[univIdx][listType] = index;
+ isDirty = true;
+ }
+
+ /**
+ * Appends the specified element to the end of this list.
+ * It is the user responsible to ensure that the element add is of
+ * the same type as array componentType.
+ *
+ * @param o element to be appended to this list.
+ */
+ synchronized final void add(IndexedObject o) {
+
+ if (elementData.length == size) {
+ IndexedObject oldData[] = elementData;
+ elementData = (IndexedObject[])java.lang.reflect.Array.newInstance(
+ componentType,
+ (size << 1));
+ System.arraycopy(oldData, 0, elementData, 0, size);
+
+ }
+
+ int univIdx = o.getIdxUsed(univ);
+
+ if (debug) {
+ int idx = o.listIdx[univIdx][listType];
+ if (idx >= 0) {
+ if (elementData[idx] != o) {
+ System.out.println("Illegal use of UnorderIndexedList idx in add " + idx);
+ Thread.dumpStack();
+ }
+ }
+ }
+
+ int idx = size++;
+ elementData[idx] = o;
+ o.listIdx[univIdx][listType] = idx;
+ isDirty = true;
+ }
+
+
+ /**
+ * Removes the element at the specified position in this list.
+ * Replace the removed element by the last one.
+ *
+ * @param index the index of the element to removed.
+ * @throws IndexOutOfBoundsException if index out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final void remove(int index) {
+ IndexedObject elm = elementData[index];
+
+ int univIdx = elm.getIdxUsed(univ);
+
+ if (debug) {
+ if (elm.listIdx[univIdx][listType] != index) {
+ System.out.println("Inconsistent idx in remove, expect " + index + " actual " + elm.listIdx[univIdx][listType]);
+ Thread.dumpStack();
+ }
+ }
+
+ elm.listIdx[univIdx][listType] = -1;
+ size--;
+ if (index != size) {
+ elm = elementData[size];
+ elm.listIdx[univIdx][listType] = index;
+ elementData[index] = elm;
+ }
+ elementData[size] = null;
+ isDirty = true;
+ /*
+ if ((cloneData != null) && (index < cloneData.length)) {
+ cloneData[index] = null; // for gc
+ }
+ */
+ }
+
+
+ /**
+ * Removes the element at the last position in this list.
+ * @return The element remove
+ * @throws IndexOutOfBoundsException if array is empty
+ */
+ synchronized final Object removeLastElement() {
+ IndexedObject elm = elementData[--size];
+ elementData[size] = null;
+ elm.listIdx[elm.getIdxUsed(univ)][listType] = -1;
+ isDirty = true;
+ /*
+ if ((cloneData != null) && (size < cloneData.length)) {
+ cloneData[size] = null; // for gc
+ }
+ */
+ return elm;
+ }
+
+
+ /**
+ * Removes the specified element in this list.
+ * Replace the removed element by the last one.
+ *
+ * @param o the element to removed.
+ * @return true if object remove
+ * @throws IndexOutOfBoundsException if index out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final boolean remove(IndexedObject o) {
+ int univIdx = o.getIdxUsed(univ);
+ int idx = o.listIdx[univIdx][listType];
+
+ if (idx >= 0) {
+ if (debug) {
+ if (o != elementData[idx]) {
+ System.out.println(this + " Illegal use of UnorderIndexedList in remove expect " + o + " actual " + elementData[idx] + " idx = " + idx);
+ Thread.dumpStack();
+ }
+ }
+ // Object in the container
+ size--;
+ if (idx != size) {
+ IndexedObject elm = elementData[size];
+ elementData[idx] = elm;
+ elm.listIdx[elm.getIdxUsed(univ)][listType] = idx;
+ }
+ elementData[size] = null;
+ o.listIdx[univIdx][listType] = -1;
+ isDirty = true;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Removes all of the elements from this list. The list will
+ * be empty after this call returns.
+ */
+ synchronized final void clear() {
+ IndexedObject o;
+ for (int i = size-1; i >= 0; i--) {
+ o = elementData[i];
+ o.listIdx[o.getIdxUsed(univ)][listType] = -1;
+ elementData[i] = null; // Let gc do its work
+ }
+ size = 0;
+ isDirty = true;
+ }
+
+ synchronized final void clearMirror() {
+
+ if (cloneData != null) {
+ for (int i = cloneData.length-1; i >= 0; i--) {
+ // don't set index to -1 since the original
+ // copy is using this.
+ cloneData[i] = null; // Let gc do its work
+ }
+ }
+ cloneSize = 0;
+ isDirty = true;
+ }
+
+ final Class getComponentType() {
+ return componentType;
+ }
+
+ /*
+ synchronized public String toString() {
+ StringBuffer sb = new StringBuffer("Size = " + size + "\n[");
+ int len = size-1;
+ Object obj;
+
+ for (int i=0; i < size; i++) {
+ obj = elementData[i];
+ if (obj != null) {
+ sb.append(elementData[i].toString());
+ } else {
+ sb.append("NULL");
+ }
+ if (i != len) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]\n");
+ return sb.toString();
+ }
+ */
+
+
+ /**
+ * Save the state of the <tt>ArrayList</tt> instance to a stream (that
+ * is, serialize it).
+ *
+ * @serialData The length of the array backing the <tt>ArrayList</tt>
+ * instance is emitted (int), followed by all of its elements
+ * (each an <tt>Object</tt>) in the proper order.
+ */
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException{
+ // Write out element count, and any hidden stuff
+ s.defaultWriteObject();
+
+ // Write out array length
+ s.writeInt(elementData.length);
+
+ // Write out all elements in the proper order.
+ for (int i=0; i<size; i++)
+ s.writeObject(elementData[i]);
+
+ }
+
+ /**
+ * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
+ * deserialize it).
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ // Read in size, and any hidden stuff
+ s.defaultReadObject();
+
+ // Read in array length and allocate array
+ int arrayLength = s.readInt();
+ elementData = (IndexedObject[])java.lang.reflect.Array.newInstance(
+ componentType, arrayLength);
+
+ // Read in all elements in the proper order.
+ for (int i=0; i<size; i++)
+ elementData[i] = (IndexedObject) s.readObject();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/InputDevice.java b/src/classes/share/javax/media/j3d/InputDevice.java
new file mode 100644
index 0000000..117a515
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/InputDevice.java
@@ -0,0 +1,153 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+
+/**
+ * InputDevice is the interface through which Java 3D and Java 3D
+ * application programs communicate with a device driver. All input
+ * devices that Java 3D uses must implement the InputDevice interface and
+ * be registered with Java 3D via a call to
+ * PhysicalEnvironment.addInputDevice(InputDevice). An input device
+ * transfers information to the Java 3D implementation and Java 3D
+ * applications by writing transform information to sensors that the
+ * device driver has created and manages. The driver can update its
+ * sensor information each time the pollAndProcessInput method is
+ * called.
+ */
+
+public interface InputDevice {
+
+ /**
+ * Signifies that the driver for a device is a blocking driver and that
+ * it should be scheduled for regular reads by Java 3D. A blocking driver
+ * is defined as a driver that can cause the thread accessing the driver
+ * (the Java 3D implementation thread calling the pollAndProcessInput
+ * method) to block while the data is being accessed from the driver.
+ */
+ public static final int BLOCKING = 3;
+
+
+ /**
+ * Signifies that the driver for a device is a non-blocking driver and
+ * that it should be scheduled for regular reads by Java 3D. A
+ * non-blocking driver is defined as a driver that does not cause the
+ * calling thread to block while data is being retrieved from the
+ * driver. If no data is available from the device, pollAndProcessInput
+ * should return without updating the sensor read value.
+ */
+ public static final int NON_BLOCKING = 4;
+
+ /**
+ * Signifies that the Java 3D implementation should not schedule
+ * regular reads on the sensors of this device; the Java 3D
+ * implementation will only call pollAndProcessInput when one of the
+ * device's sensors' getRead methods is called. A DEMAND_DRIVEN driver
+ * must always provide the current value of the sensor on demand whenever
+ * pollAndProcessInput is called. This means that DEMAND_DRIVEN drivers
+ * are non-blocking by definition.
+ */
+ public static final int DEMAND_DRIVEN = 5;
+
+
+ /**
+ * This method initializes the device. A device should be initialized
+ * before it is registered with Java 3D via the
+ * PhysicalEnvironment.addInputDevice(InputDevice) method call.
+ * @return return true for succesful initialization, false for failure
+ */
+ public abstract boolean initialize();
+
+ /**
+ * This method sets the device's current position and orientation as the
+ * devices nominal position and orientation (establish its reference
+ * frame relative to the "Tracker base" reference frame).
+ */
+ public abstract void setNominalPositionAndOrientation();
+
+
+ /**
+ * This method causes the device's sensor readings to be updated by the
+ * device driver. For BLOCKING and NON_BLOCKING drivers, this method is
+ * called regularly and the Java 3D implementation can cache the sensor
+ * values. For DEMAND_DRIVEN drivers this method is called each time one
+ * of the Sensor.getRead methods is called, and is not otherwise called.
+ */
+ public abstract void pollAndProcessInput();
+
+ /**
+ * This method will not be called by the Java 3D implementation and
+ * should be implemented as an empty method.
+ */
+ public abstract void processStreamInput();
+
+ /**
+ * Code to process the clean up of the device and relinquish associated
+ * resources. This method should be called after the device has been
+ * unregistered from Java 3D via the
+ * PhysicalEnvironment.removeInputDevice(InputDevice) method call.
+ */
+ public abstract void close();
+
+ /**
+ * This method retrieves the device's processing mode: one of BLOCKING,
+ * NON_BLOCKING, or DEMAND_DRIVEN. The Java 3D implementation calls
+ * this method when PhysicalEnvironment.addInputDevice(InputDevice) is
+ * called to register the device with Java 3D. If this method returns
+ * any value other than BLOCKING, NON_BLOCKING, or DEMAND_DRIVEN,
+ * addInputDevice will throw an IllegalArgumentException.
+ * @return Returns the devices processing mode, one of BLOCKING,
+ * NON_BLOCKING, or DEMAND_DRIVEN
+ */
+ public abstract int getProcessingMode();
+
+
+ /**
+ * This method sets the device's processing mode to one of: BLOCKING,
+ * NON_BLOCKING, or DEMAND_DRIVEN. Many drivers will be written to run
+ * in only one mode. Applications using such drivers should not attempt
+ * to set the processing mode. This method should throw an
+ * IllegalArgumentException if there is an attempt to set the processing
+ * mode to anything other than the aforementioned three values.
+ *
+ * <p>
+ * NOTE: this method should <i>not</i> be called after the input
+ * device has been added to a PhysicalEnvironment. The
+ * processingMode must remain constant while a device is attached
+ * to a PhysicalEnvironment.
+ *
+ * @param mode One of BLOCKING, NON_BLOCKING, or DEMAND_DRIVEN
+ */
+ public abstract void setProcessingMode(int mode);
+
+
+ /**
+ * This method gets the number of sensors associated with the device.
+ * @return the device's sensor count.
+ */
+ public int getSensorCount();
+
+ /**
+ * Gets the specified Sensor associated with the device. Each InputDevice
+ * implementation is responsible for creating and managing its own set of
+ * sensors. The sensor indices begin at zero and end at number of
+ * sensors minus one. Each sensor should have had
+ * Sensor.setDevice(InputDevice) set properly before addInputDevice
+ * is called.
+ * @param sensorIndex the sensor to retrieve
+ * @return Returns the specified sensor.
+ */
+ public Sensor getSensor(int sensorIndex);
+
+}
diff --git a/src/classes/share/javax/media/j3d/InputDeviceBlockingThread.java b/src/classes/share/javax/media/j3d/InputDeviceBlockingThread.java
new file mode 100644
index 0000000..6bdff8a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/InputDeviceBlockingThread.java
@@ -0,0 +1,99 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class InputDeviceBlockingThread extends Thread {
+
+ // action flag for runMonitor
+ private static final int WAIT = 0;
+ private static final int NOTIFY = 1;
+ private static final int STOP = 2;
+
+ // blocking device that this thread manages
+ private InputDevice device;
+ private boolean running = true;
+ private boolean waiting = false;
+ private volatile boolean stop = false;
+ private static int numInstances = 0;
+ private int instanceNum = -1;
+
+ InputDeviceBlockingThread(ThreadGroup threadGroup, InputDevice device) {
+ super(threadGroup, "");
+ setName("J3D-InputDeviceBlockingThread-" + getInstanceNum());
+ this.device = device;
+ }
+
+ private synchronized int newInstanceNum() {
+ return (++numInstances);
+ }
+
+ private int getInstanceNum() {
+ if (instanceNum == -1)
+ instanceNum = newInstanceNum();
+ return instanceNum;
+ }
+
+
+ public void run() {
+ // Since this thread is blocking, this thread should not be
+ // taking an inordinate amount of CPU time. Note that the
+ // yield() call should not be necessary (and may be ineffective),
+ // but we can't call MasterControl.threadYield() because it will
+ // sleep for at least a millisecond.
+ while (running) {
+ while (!stop) {
+ device.pollAndProcessInput();
+ Thread.yield();
+ }
+ runMonitor(WAIT);
+ }
+ }
+
+ void sleep() {
+ stop = true;
+ }
+
+ void restart() {
+ stop = false;
+ runMonitor(NOTIFY);
+ }
+
+ void finish() {
+ stop = true;
+ while (!waiting) {
+ MasterControl.threadYield();
+ }
+
+ runMonitor(STOP);
+ }
+
+ synchronized void runMonitor(int action) {
+
+ switch (action) {
+ case WAIT:
+ try {
+ waiting = true;
+ wait();
+ } catch (InterruptedException e) {}
+ waiting = false;
+ break;
+ case NOTIFY:
+ notify();
+ break;
+ case STOP:
+ running = false;
+ notify();
+ break;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/InputDeviceScheduler.java b/src/classes/share/javax/media/j3d/InputDeviceScheduler.java
new file mode 100644
index 0000000..f438e37
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/InputDeviceScheduler.java
@@ -0,0 +1,204 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * This thread manages all input device scheduling. It monitors and caches
+ * all device additions and removals. It spawns new threads for blocking
+ * devices, manages all non-blocking drivers itself, and tags the sensors
+ * of demand_driven devices. This implementation assume that
+ * processMode of InputDevice will not change after addInputDevice().
+ *
+ */
+
+class InputDeviceScheduler extends J3dThread {
+
+ // list of devices that have been added with the phys env interface
+ ArrayList nonBlockingDevices = new ArrayList(1);
+
+ // This condition holds blockingDevices.size() == threads.size()
+ ArrayList blockingDevices = new ArrayList(1);
+ ArrayList threads = new ArrayList(1);
+
+ // This is used by MasterControl to keep track activeViewRef
+ PhysicalEnvironment physicalEnv;
+
+ // store all inputDevices
+ Vector devices = new Vector(1);
+
+ J3dThreadData threadData = new J3dThreadData();
+ boolean active = false;
+
+ // The time to sleep before next processAndProcess() is invoked
+ // for non-blocking input device
+ static int samplingTime = 5;
+
+ // Some variables used to name threads correctly
+ private static int numInstances = 0;
+ private int instanceNum = -1;
+
+ private synchronized int newInstanceNum() {
+ return (++numInstances);
+ }
+
+ int getInstanceNum() {
+ if (instanceNum == -1)
+ instanceNum = newInstanceNum();
+ return instanceNum;
+ }
+
+ InputDeviceScheduler(ThreadGroup threadGroup,
+ PhysicalEnvironment physicalEnv) {
+ super(threadGroup);
+ setName("J3D-InputDeviceScheduler-" + getInstanceNum());
+ threadData.threadType = J3dThread.INPUT_DEVICE_SCHEDULER;
+ threadData.thread = this;
+ this.physicalEnv = physicalEnv;
+
+ synchronized (physicalEnv.devices) {
+ Enumeration elm = physicalEnv.devices.elements();
+ while (elm.hasMoreElements()) {
+ addInputDevice((InputDevice) elm.nextElement());
+ }
+ physicalEnv.inputsched = this;
+ }
+
+ }
+
+
+ void addInputDevice(InputDevice device) {
+
+ switch(device.getProcessingMode()) {
+ case InputDevice.BLOCKING:
+ InputDeviceBlockingThread thread =
+ VirtualUniverse.mc.getInputDeviceBlockingThread(device);
+ thread.start();
+ synchronized (blockingDevices) {
+ threads.add(thread);
+ blockingDevices.add(device);
+ }
+ break;
+ case InputDevice.NON_BLOCKING:
+ synchronized (nonBlockingDevices) {
+ nonBlockingDevices.add(device);
+ if (active && (nonBlockingDevices.size() == 1)) {
+ VirtualUniverse.mc.addInputDeviceScheduler(this);
+ }
+ }
+ break;
+ default: // InputDevice.DEMAND_DRIVEN:
+ // tag the sensors
+ for (int i=device.getSensorCount()-1; i>=0; i--) {
+ device.getSensor(i).demand_driven = true;
+ }
+ break;
+ }
+
+ }
+
+
+ void removeInputDevice(InputDevice device) {
+
+ switch(device.getProcessingMode()) {
+ case InputDevice.BLOCKING:
+ // tell the thread to clean up and permanently block
+ synchronized (blockingDevices) {
+ int idx = blockingDevices.indexOf(device);
+ InputDeviceBlockingThread thread =
+ (InputDeviceBlockingThread) threads.remove(idx);
+ thread.finish();
+ blockingDevices.remove(idx);
+ }
+ break;
+ case InputDevice.NON_BLOCKING:
+ // remove references that are in this thread
+ synchronized (nonBlockingDevices) {
+ nonBlockingDevices.remove(nonBlockingDevices.indexOf(device));
+ if (active && (nonBlockingDevices.size() == 0)) {
+ VirtualUniverse.mc.removeInputDeviceScheduler(this);
+ }
+ }
+ break;
+ default: // InputDevice.DEMAND_DRIVEN:
+ // untag the sensors
+ for (int i=device.getSensorCount()-1; i>=0; i--) {
+ device.getSensor(i).demand_driven = false;
+ }
+ }
+ }
+
+ // Add this thread to MC (Callback from MC thread)
+ void activate() {
+ if (!active) {
+ active = true;
+
+ synchronized (nonBlockingDevices) {
+ if (nonBlockingDevices.size() > 0) {
+ VirtualUniverse.mc.addInputDeviceScheduler(this);
+ }
+ }
+ // run all spawn threads
+ synchronized (blockingDevices) {
+ for (int i=threads.size()-1; i >=0; i--) {
+ ((InputDeviceBlockingThread)threads.get(i)).restart();
+ }
+ }
+ }
+ }
+
+ // Remove this thread from MC (Callback from MC thread)
+ void deactivate() {
+ if (active) {
+ synchronized (nonBlockingDevices) {
+ if (nonBlockingDevices.size() > 0) {
+ VirtualUniverse.mc.removeInputDeviceScheduler(this);
+ }
+ }
+
+ // stop all spawn threads
+ synchronized (blockingDevices) {
+ for (int i=threads.size()-1; i >=0; i--) {
+ ((InputDeviceBlockingThread)threads.get(i)).sleep();
+ }
+ }
+ active = false;
+ }
+ }
+
+ J3dThreadData getThreadData() {
+ return threadData;
+ }
+
+ void doWork(long referenceTime) {
+ synchronized (nonBlockingDevices) {
+ for (int i = nonBlockingDevices.size()-1; i >=0; i--) {
+ ((InputDevice)nonBlockingDevices.get(i)).pollAndProcessInput();
+ }
+ }
+ }
+
+ void shutdown() {
+ // stop all spawn threads
+ for (int i=threads.size()-1; i >=0; i--) {
+ ((InputDeviceBlockingThread)threads.get(i)).finish();
+ }
+ // for gc
+ threads.clear();
+ blockingDevices.clear();
+ nonBlockingDevices.clear();
+ devices.clear();
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/IntegerFreeList.java b/src/classes/share/javax/media/j3d/IntegerFreeList.java
new file mode 100644
index 0000000..c739038
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/IntegerFreeList.java
@@ -0,0 +1,40 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class IntegerFreeList extends MemoryFreeList {
+
+ int count = 0;
+
+ // default the initial count to 1
+ IntegerFreeList() {
+ super("java.lang.Integer");
+ }
+
+ // sets up an initial count and an initial capacity for the freelist
+ IntegerFreeList(int initialCount, int initCapacity) {
+ super("java.lang.Integer", initCapacity);
+ count = initialCount;
+ }
+
+ synchronized Object getObject() {
+ if (size > 0) return super.removeLastElement();
+ else return new Integer(++count);
+ }
+
+ public synchronized void clear() {
+ super.clear();
+ count = 0;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Interpolator.java b/src/classes/share/javax/media/j3d/Interpolator.java
new file mode 100644
index 0000000..fb97d0b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Interpolator.java
@@ -0,0 +1,126 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+import java.util.Vector;
+
+/**
+ * Interpolator is an abstract class that extends Behavior to provide
+ * common methods used by various interpolation subclasses. These
+ * include methods to convert a time value into an alpha value (A
+ * value in the range 0 to 1) and a method to initialize the behavior.
+ * Subclasses provide the methods that convert alpha values into
+ * values within that subclass' output range.
+ */
+
+public abstract class Interpolator extends Behavior {
+
+ // This interpolator's alpha generator
+ Alpha alpha;
+
+
+ /**
+ * Default WakeupCondition for all interpolators. The
+ * wakeupOn method of Behavior, which takes a WakeupCondition as
+ * the method parameter, will need to be called at the end
+ * of the processStimulus method of any class that subclasses
+ * Interpolator; this can be done with the following method call:
+ * wakeupOn(defaultWakeupCriterion).
+ */
+ protected WakeupCriterion defaultWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0);
+
+
+ /**
+ * Constructs an Interpolator node with a null alpha value.
+ */
+ public Interpolator() {
+ }
+
+
+ /**
+ * Constructs an Interpolator node with the specified alpha value.
+ * @param alpha the alpha object used by this interpolator.
+ * If it is null, then this interpolator will not run.
+ */
+ public Interpolator(Alpha alpha){
+ this.alpha = alpha;
+ }
+
+
+ /**
+ * Retrieves this interpolator's alpha object.
+ * @return this interpolator's alpha object
+ */
+ public Alpha getAlpha() {
+ return this.alpha;
+ }
+
+
+ /**
+ * Set this interpolator's alpha to the specified alpha object.
+ * @param alpha the new alpha object. If set to null,
+ * then this interpolator will stop running.
+ */
+ public void setAlpha(Alpha alpha) {
+ this.alpha = alpha;
+ VirtualUniverse.mc.sendRunMessage(J3dThread.RENDER_THREAD);
+ }
+
+
+ /**
+ * This is the default Interpolator behavior initialization routine.
+ * It schedules the behavior to awaken at the next frame.
+ */
+ public void initialize() {
+ // Reset alpha
+ //alpha.setStartTime(System.currentTimeMillis());
+
+ // Insert wakeup condition into queue
+ wakeupOn(defaultWakeupCriterion);
+ }
+
+
+ /**
+ * Copies all Interpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ Interpolator it = (Interpolator) originalNode;
+
+ Alpha a = it.getAlpha();
+ if (a != null) {
+ setAlpha(a.cloneAlpha());
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/J3DBuffer.java b/src/classes/share/javax/media/j3d/J3DBuffer.java
new file mode 100644
index 0000000..10c3aaf
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3DBuffer.java
@@ -0,0 +1,243 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+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;
+
+/**
+ * Java 3D wrapper class for java.nio.Buffer objects.
+ * When used to wrap a non-null NIO buffer object, this class will
+ * create a read-only view of the wrapped NIO buffer, and will call
+ * <code>rewind</code> on the read-only view, so that elements 0
+ * through <code>buffer.limit()-1</code> will be available internally.
+ *
+ * <p>
+ * NOTE: Use of this class requires version 1.4 of the
+ * Java<sup><font size="-2">TM</font></sup>&nbsp;2 Platform.
+ * Any attempt to access this class on an earlier version of the
+ * Java&nbsp;2 Platform will fail with a NoClassDefFoundError.
+ * Refer to the documentation for version 1.4 of the Java&nbsp;2 SDK
+ * for a description of the
+ * <code>
+ * <a href="http://java.sun.com/j2se/1.4/docs/api/java/nio/package-summary.html">java.nio</a>
+ * </code> package.
+ *
+ * @see GeometryArray#setCoordRefBuffer(J3DBuffer)
+ * @see GeometryArray#setColorRefBuffer(J3DBuffer)
+ * @see GeometryArray#setNormalRefBuffer(J3DBuffer)
+ * @see GeometryArray#setTexCoordRefBuffer(int,J3DBuffer)
+ * @see GeometryArray#setInterleavedVertexBuffer(J3DBuffer)
+ * @see CompressedGeometry#CompressedGeometry(CompressedGeometryHeader,J3DBuffer)
+ *
+ * @since Java 3D 1.3
+ */
+
+public class J3DBuffer {
+ static final int TYPE_NULL = 0;
+ static final int TYPE_UNKNOWN = 1;
+ static final int TYPE_BYTE = 2;
+ static final int TYPE_CHAR = 3;
+ static final int TYPE_SHORT = 4;
+ static final int TYPE_INT = 5;
+ static final int TYPE_LONG = 6;
+ static final int TYPE_FLOAT = 7;
+ static final int TYPE_DOUBLE = 8;
+
+ static boolean unsupportedOperation = false;
+
+ private java.nio.Buffer originalBuffer = null;
+ private BufferWrapper bufferImpl = null;
+ private int bufferType = TYPE_NULL;
+
+ /**
+ * Constructs a J3DBuffer object and initializes it with
+ * a null NIO buffer object. The NIO buffer object
+ * must be set to a non-null value before using this J3DBuffer
+ * object in a Java 3D node component.
+ *
+ * @exception UnsupportedOperationException if the JVM does not
+ * support native access to direct NIO buffers
+ */
+ public J3DBuffer() {
+ this(null);
+ }
+
+
+ /**
+ * Constructs a J3DBuffer object and initializes it with
+ * the specified NIO buffer object.
+ *
+ * @param buffer the NIO buffer wrapped by this J3DBuffer
+ *
+ * @exception UnsupportedOperationException if the JVM does not
+ * support native access to direct NIO buffers
+ *
+ * @exception IllegalArgumentException if the specified buffer is
+ * not a direct buffer, or if the byte order of the specified
+ * buffer does not match the native byte order of the underlying
+ * platform.
+ */
+ public J3DBuffer(java.nio.Buffer buffer) {
+ if (unsupportedOperation)
+ throw new UnsupportedOperationException(J3dI18N.getString("J3DBuffer0"));
+
+ setBuffer(buffer);
+ }
+
+
+ /**
+ * Sets the NIO buffer object in this J3DBuffer to
+ * the specified object.
+ *
+ * @param buffer the NIO buffer wrapped by this J3DBuffer
+ *
+ * @exception IllegalArgumentException if the specified buffer is
+ * not a direct buffer, or if the byte order of the specified
+ * buffer does not match the native byte order of the underlying
+ * platform.
+ */
+ public void setBuffer(java.nio.Buffer buffer) {
+ int bType = TYPE_NULL;
+ boolean direct = false;
+ java.nio.ByteOrder order = java.nio.ByteOrder.BIG_ENDIAN;
+
+ if (buffer == null) {
+ bType = TYPE_NULL;
+ }
+ else if (buffer instanceof java.nio.ByteBuffer) {
+ bType = TYPE_BYTE;
+ direct = ((java.nio.ByteBuffer)buffer).isDirect();
+ order = ((java.nio.ByteBuffer)buffer).order();
+ }
+ else if (buffer instanceof java.nio.CharBuffer) {
+ bType = TYPE_CHAR;
+ direct = ((java.nio.CharBuffer)buffer).isDirect();
+ order = ((java.nio.CharBuffer)buffer).order();
+ }
+ else if (buffer instanceof java.nio.ShortBuffer) {
+ bType = TYPE_SHORT;
+ direct = ((java.nio.ShortBuffer)buffer).isDirect();
+ order = ((java.nio.ShortBuffer)buffer).order();
+ }
+ else if (buffer instanceof java.nio.IntBuffer) {
+ bType = TYPE_INT;
+ direct = ((java.nio.IntBuffer)buffer).isDirect();
+ order = ((java.nio.IntBuffer)buffer).order();
+ }
+ else if (buffer instanceof java.nio.LongBuffer) {
+ bType = TYPE_LONG;
+ direct = ((java.nio.LongBuffer)buffer).isDirect();
+ order = ((java.nio.LongBuffer)buffer).order();
+ }
+ else if (buffer instanceof java.nio.FloatBuffer) {
+ bType = TYPE_FLOAT;
+ direct = ((java.nio.FloatBuffer)buffer).isDirect();
+ order = ((java.nio.FloatBuffer)buffer).order();
+ }
+ else if (buffer instanceof java.nio.DoubleBuffer) {
+ bType = TYPE_DOUBLE;
+ direct = ((java.nio.DoubleBuffer)buffer).isDirect();
+ order = ((java.nio.DoubleBuffer)buffer).order();
+ }
+ else {
+ bType = TYPE_UNKNOWN;
+ }
+
+ // Verify that the buffer is direct and has the correct byte order
+ if (buffer != null) {
+ if (!direct) {
+ throw new IllegalArgumentException(J3dI18N.getString("J3DBuffer1"));
+ }
+
+ if (order != java.nio.ByteOrder.nativeOrder()) {
+ throw new IllegalArgumentException(J3dI18N.getString("J3DBuffer2"));
+ }
+ }
+
+ bufferType = bType;
+ originalBuffer = buffer;
+
+ // Make a read-only view of the buffer if the type is one
+ // of the internally supported types: byte, float, or double
+ switch (bufferType) {
+ case TYPE_BYTE:
+ java.nio.ByteBuffer byteBuffer =
+ ((java.nio.ByteBuffer)buffer).asReadOnlyBuffer();
+ byteBuffer.rewind();
+ bufferImpl = new ByteBufferWrapper(byteBuffer);
+ break;
+ case TYPE_FLOAT:
+ java.nio.FloatBuffer floatBuffer =
+ ((java.nio.FloatBuffer)buffer).asReadOnlyBuffer();
+ floatBuffer.rewind();
+ bufferImpl = new FloatBufferWrapper(floatBuffer);
+ break;
+ case TYPE_DOUBLE:
+ java.nio.DoubleBuffer doubleBuffer =
+ ((java.nio.DoubleBuffer)buffer).asReadOnlyBuffer();
+ doubleBuffer.rewind();
+ bufferImpl = new DoubleBufferWrapper(doubleBuffer);
+ break;
+ default:
+ bufferImpl = null;
+ }
+ }
+
+
+ /**
+ * Retrieves the NIO buffer object from this J3DBuffer.
+ *
+ * @return the current NIO buffer wrapped by this J3DBuffer
+ */
+ public java.nio.Buffer getBuffer() {
+ return originalBuffer;
+ }
+
+
+ int getBufferType() {
+ return bufferType;
+ }
+
+
+ BufferWrapper getBufferImpl() {
+ return bufferImpl;
+ }
+
+ // Native method to verify that we can access a direct NIO buffer
+ // from native code
+ private native static long getNativeAddress(java.nio.Buffer buffer);
+
+ private static boolean checkNativeBufferAccess(java.nio.Buffer buffer) {
+ // TODO: uncomment out the call to getNativeAddress and implement it
+ if (buffer == null /*|| getNativeAddress(buffer) == 0L*/) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ static {
+ // Allocate a direct byte buffer and verify that we can
+ // access the data pointer from native code
+ java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocateDirect(8);
+
+ if (!checkNativeBufferAccess(buffer)) {
+ unsupportedOperation = true;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/J3DGraphics2D.java b/src/classes/share/javax/media/j3d/J3DGraphics2D.java
new file mode 100644
index 0000000..61bf6ab
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3DGraphics2D.java
@@ -0,0 +1,171 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+
+
+/**
+ * The J3DGraphics2D class extends Graphics2D to provide 2D rendering
+ * into a Canvas3D. It is an abstract base class that is further
+ * extended by a non-public Java 3D implementation class. This class
+ * allows Java 2D rendering to be mixed with Java 3D rendering in the
+ * same Canvas3D, subject to the same restrictions as imposed for 3D
+ * immediate-mode rendering: In mixed-mode rendering, all Java 2D
+ * requests must be done from one of the Canvas3D callback methods; in
+ * pure-immediate mode, the Java 3D renderer must be stopped for the
+ * Canvas3D being rendered into.
+ *
+ * <p>
+ * An application obtains a J3D 2D graphics context object from the
+ * Canvas3D object that the application wishes to render into by using
+ * the getGraphics2D method. A new J3DGraphics2D object is created if
+ * one does not already exist.
+ *
+ * <p>
+ * Note that the drawing methods in this class, including those
+ * inherited from Graphics2D, are not necessarily executed
+ * immediately. They may be buffered up for future execution.
+ * Applications must call the <code><a
+ * href="#flush(boolean)">flush</a>(boolean)</code> method to ensure
+ * that the rendering actually happens. The flush method is implicitly
+ * called in the following cases:
+ *
+ * <ul>
+ * <li>The <code>Canvas3D.swap</code> method calls
+ * <code>flush(true)</code></li>
+ * <li>The Java 3D renderer calls <code>flush(true)</code> prior to
+ * swapping the buffer for a double buffered on-screen Canvas3D</li>
+ * <li>The Java 3D renderer calls <code>flush(true)</code> prior to
+ * copying into the off-screen buffer of an off-screen Canvas3D</li>
+ * <li>The Java 3D renderer calls <code>flush(false)</code> after
+ * calling the preRender, renderField, postRender, and postSwap
+ * Canvas3D callback methods.</li>
+ * </ul>
+ *
+ * <p>
+ * A single-buffered, pure-immediate mode application must explicitly
+ * call flush to ensure that the graphics will be rendered to the
+ * Canvas3D.
+ *
+ * @see Canvas3D#getGraphics2D
+ *
+ * @since Java 3D 1.2
+ */
+
+public abstract class J3DGraphics2D extends Graphics2D {
+
+ // Package scope contructor
+ J3DGraphics2D() {
+ }
+
+ /**
+ * This method is not supported. The only way to obtain a
+ * J3DGraphics2D is from the associated Canvas3D.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @see Canvas3D#getGraphics2D
+ */
+ public final Graphics create() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported. The only way to obtain a
+ * J3DGraphics2D is from the associated Canvas3D.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @see Canvas3D#getGraphics2D
+ */
+ public final Graphics create(int x, int y, int width, int height) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported. Clearing a Canvas3D is done implicitly
+ * via a Background node in the scene graph or explicitly via the clear
+ * method in a 3D graphics context.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @see Background
+ * @see GraphicsContext3D#setBackground
+ * @see GraphicsContext3D#clear
+ */
+ public final void setBackground(Color color) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported. Clearing a Canvas3D is done implicitly
+ * via a Background node in the scene graph or explicitly via the clear
+ * method in a 3D graphics context.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @see Background
+ * @see GraphicsContext3D#getBackground
+ * @see GraphicsContext3D#clear
+ */
+ public final Color getBackground() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported. Clearing a Canvas3D is done implicitly
+ * via a Background node in the scene graph or explicitly via the clear
+ * method in a 3D graphics context.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @see Background
+ * @see GraphicsContext3D#setBackground
+ * @see GraphicsContext3D#clear
+ */
+ public final void clearRect(int x, int y, int width, int height) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Flushes all previously executed rendering operations to the
+ * drawing buffer for this 2D graphics object.
+ *
+ * @param wait flag indicating whether or not to wait for the
+ * rendering to be complete before returning from this call.
+ */
+ public abstract void flush(boolean wait);
+
+ /**
+ * Draws the specified image and flushes the buffer. This is
+ * functionally equivalent to calling <code>drawImage(...)</code>
+ * followed by <code>flush(false)</code>, but can avoid the cost
+ * of making an extra copy of the image in some cases. Anything
+ * previously drawn to this J3DGraphics2D will be flushed before
+ * the image is drawn.
+ *
+ * @param img The image to draw
+ * @param x The x location to draw at
+ * @param y The y location to draw at
+ * @param observer The ImageObserver
+ *
+ * @since Java 3D 1.3
+ */
+ public abstract void drawAndFlushImage(BufferedImage img, int x, int y,
+ ImageObserver observer);
+
+}
diff --git a/src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java b/src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java
new file mode 100644
index 0000000..c0e7425
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java
@@ -0,0 +1,1056 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.io.*;
+import java.util.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.text.AttributedCharacterIterator;
+import java.awt.RenderingHints.Key;
+import java.awt.geom.*;
+import java.awt.image.*;
+import java.awt.image.renderable.RenderableImage;
+import java.awt.font.*;
+
+/**
+ * Implementation class for J3DGraphics2D
+ */
+
+final class J3DGraphics2DImpl extends J3DGraphics2D {
+ private Graphics2D offScreenGraphics2D;
+ private BufferedImage g3dImage = null;
+ private byte[] data = null;
+ private boolean isFlushed = true;
+ private Canvas3D canvas3d;
+ private int width, height;
+ private int texWidth, texHeight;
+ private int xmin, ymin, xmax, ymax;
+ private Object extentLock = new Object();
+ private boolean abgr;
+ private boolean initTexMap = false;
+ private boolean strokeSet=false;
+ private Point2D.Float ptSrc = new Point2D.Float();
+ private Point2D.Float ptDst1 = new Point2D.Float();
+ private Point2D.Float ptDst2 = new Point2D.Float();
+ private Color xOrModeColor = null;
+ private volatile boolean initCtx = false;
+ private boolean threadWaiting = false;
+ static final Color blackTransparent = new Color(0,0,0,0);
+ private boolean useDrawPixel = VirtualUniverse.mc.isJ3dG2dDrawPixel;
+ int objectId = -1;
+
+ // Package scope contructor
+ J3DGraphics2DImpl(Canvas3D c) {
+ canvas3d = c;
+
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ if (c.ctx == 0) {
+ // create a dummy bufferImage
+ width = 1;
+ height = 1;
+ g3dImage = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_ARGB);
+ offScreenGraphics2D = g3dImage.createGraphics();
+ } else {
+ init();
+ }
+ }
+
+ }
+
+ // This is invoke from Renderer callback when the first
+ // time createContext() finish which set
+ // canvas3d.extensionSupported correctly.
+ void init() {
+ // if ABGR extension is supported, we want to use
+ // TYPE_4BYTE_ABGR to make a fast copy
+ if (!initCtx) {
+ abgr = ((canvas3d.extensionsSupported & Canvas3D.EXT_ABGR) != 0);
+
+ width = canvas3d.getWidth();
+ height = canvas3d.getHeight();
+ initTexMap = false;
+
+ if (width <= 0) {
+ width = 1;
+ }
+ if (height <= 0) {
+ height = 1;
+ }
+
+ synchronized (extentLock) {
+ xmax = width;
+ ymax = height;
+ xmin = 0;
+ ymin = 0;
+ }
+ g3dImage = new BufferedImage(width, height,
+ (abgr ? BufferedImage.TYPE_4BYTE_ABGR:
+ BufferedImage.TYPE_INT_ARGB));
+ offScreenGraphics2D = g3dImage.createGraphics();
+ clearOffScreen();
+ if (!abgr) {
+ data = new byte[width*height*4];
+ }
+
+ // should be the last flag to set
+ initCtx = true;
+ }
+ }
+
+ /**
+ * Flushes all previously executed rendering operations to the
+ * drawing buffer for this 2D graphics object.
+ *
+ * @param wait flag indicating whether or not to wait for the
+ * rendering to be complete before returning from this call.
+ */
+ public void flush(boolean waiting) {
+
+ if (!isFlushed) {
+ // Composite g3dImage into Canvas3D
+ if (Thread.currentThread() == canvas3d.screen.renderer) {
+ if (!initCtx) {
+ return;
+ }
+ doFlush();
+ } else {
+ if (!initCtx) {
+ if (waiting &&
+ (canvas3d.pendingView != null) &&
+ canvas3d.pendingView.activeStatus) {
+ // wait until Renderer init() this context
+
+ while (!initCtx) {
+ MasterControl.threadYield();
+ }
+ } else {
+ return;
+ }
+ }
+ // Behavior Scheduler or other threads
+ // TODO: may not be legal for behaviorScheduler
+ // May cause deadlock if it is in behaviorScheduler
+ // and we wait for Renderer to finish
+ boolean renderRun = (Thread.currentThread() !=
+ canvas3d.view.universe.behaviorScheduler);
+ // This must put before sendRenderMessage()
+ threadWaiting = true;
+ sendRenderMessage(renderRun, GraphicsContext3D.FLUSH2D, null,
+ null, null);
+ if (waiting) {
+ // It is possible that thread got notify BEFORE
+ // the following runMonitor invoke.
+ runMonitor(J3dThread.WAIT);
+ }
+ }
+ isFlushed = true;
+
+ }
+ }
+
+ // copy the data into a byte buffer that will be passed to opengl
+ void doFlush() {
+ // clip to offscreen buffer size
+ if (canvas3d.ctx == 0) {
+ canvas3d.getGraphicsContext3D().doClear();
+ }
+
+ synchronized (extentLock) {
+ if (xmin < 0) {
+ xmin = 0;
+ }
+ if (xmax > width) {
+ xmax = width;
+ }
+ if (ymin < 0) {
+ ymin = 0;
+ }
+ if (ymax > height) {
+ ymax = height;
+ }
+
+ if ((xmax - xmin > 0) &&
+ (ymax - ymin > 0)) {
+ if (abgr) {
+ data = ((DataBufferByte)g3dImage.getRaster().getDataBuffer()).getData();
+ } else {
+ copyImage(g3dImage, data, width, height, xmin, ymin, xmax, ymax);
+ }
+ copyDataToCanvas(0, 0, xmin, ymin, xmax, ymax, width, height);
+ } else {
+
+ runMonitor(J3dThread.NOTIFY);
+ }
+ // this define an empty region
+ xmax = 0;
+ ymax = 0;
+ xmin = width;
+ ymin = height;
+ }
+
+ }
+
+
+ // borrowed from ImageComponentRetained since ImageComponent2D
+ // seems to do stuff we don't need to
+ final void copyImage(BufferedImage bi, byte[] image,
+ int width, int height,
+ int x1, int y1, int x2, int y2) {
+ int biType = bi.getType();
+ int w, h, i, j;
+ int row, rowBegin, rowInc, dstBegin;
+
+ dstBegin = 0;
+ rowInc = 1;
+ rowBegin = 0;
+
+ // convert format to RGBA for underlying OGL use
+ if ((biType == BufferedImage.TYPE_INT_ARGB) ||
+ (biType == BufferedImage.TYPE_INT_RGB)) {
+ // optimized cases
+ rowBegin = y1;
+
+ int colBegin = x1;
+
+ int[] intData =
+ ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
+ int rowOffset = rowInc * width;
+ int intPixel;
+
+ rowBegin = rowBegin*width + colBegin;
+ dstBegin = rowBegin*4;
+
+ if (biType == BufferedImage.TYPE_INT_ARGB) {
+ for (h = y1; h < y2; h++) {
+ i = rowBegin;
+ j = dstBegin;
+ for (w = x1; w < x2; w++, i++) {
+ intPixel = intData[i];
+ image[j++] = (byte)((intPixel >> 16) & 0xff);
+ image[j++] = (byte)((intPixel >> 8) & 0xff);
+ image[j++] = (byte)(intPixel & 0xff);
+ image[j++] = (byte)((intPixel >> 24) & 0xff);
+ }
+ rowBegin += rowOffset;
+ dstBegin += (rowOffset*4);
+ }
+ } else {
+ for (h = y1; h < y2; h++) {
+ i = rowBegin;
+ j = dstBegin;
+ for (w = x1; w < x2; w++, i++) {
+ intPixel = intData[i];
+ image[j++] = (byte)((intPixel >> 16) & 0xff);
+ image[j++] = (byte)((intPixel >> 8) & 0xff);
+ image[j++] = (byte)(intPixel & 0xff);
+ image[j++] = (byte)255;
+ }
+ rowBegin += rowOffset;
+ dstBegin += (rowOffset*4);
+ }
+ }
+ } else {
+ // non-optimized cases
+ WritableRaster ras = bi.getRaster();
+ ColorModel cm = bi.getColorModel();
+ Object pixel = ImageComponentRetained.getDataElementBuffer(ras);
+
+ j = (y1*width + x1)*4;
+ for (h = y1; h < y2; h++) {
+ i = j;
+ for (w = x1; w < x2; w++) {
+ ras.getDataElements(w, h, pixel);
+ image[j++] = (byte)cm.getRed(pixel);
+ image[j++] = (byte)cm.getGreen(pixel);
+ image[j++] = (byte)cm.getBlue(pixel);
+ image[j++] = (byte)cm.getAlpha(pixel);
+
+ }
+ j = i+ width*4;
+ }
+ }
+ }
+
+ void sendRenderMessage(boolean renderRun, int command,
+ Object arg1, Object arg2, Object arg3) {
+ // send a message to the request renderer
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = canvas3d;
+ renderMessage.args[1] = new Integer(command);
+ renderMessage.args[2] = arg1;
+ renderMessage.args[3] = arg2;
+ renderMessage.args[4] = arg3;
+
+ while (!canvas3d.view.inRenderThreadData) {
+ // wait until the renderer thread data in added in
+ // MC:RenderThreadData array ready to receive message
+ MasterControl.threadYield();
+ }
+
+ canvas3d.screen.renderer.rendererStructure.addMessage(renderMessage);
+
+ if (renderRun) {
+ // notify mc that there is work to do
+ VirtualUniverse.mc.sendRunMessage(canvas3d.view, J3dThread.RENDER_THREAD);
+ } else {
+ // notify mc that there is work for the request renderer
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+ }
+
+ final void validate() {
+ validate(0, 0, width, height);
+ }
+
+ void validate(float x1, float y1, float x2, float y2,
+ AffineTransform xform) {
+ float t;
+
+ if (xform == null) {
+ validate(x1, y1, x2, y2);
+ } else {
+ ptSrc.x = x1;
+ ptSrc.y = y1;
+ xform.transform(ptSrc, ptDst1);
+ ptSrc.x = x2;
+ ptSrc.y = y2;
+ xform.transform(ptSrc, ptDst2);
+
+ if (ptDst1.x > ptDst2.x) {
+ t = ptDst1.x;
+ ptDst1.x = ptDst2.x;
+ ptDst2.x = t;
+ }
+ if (ptDst1.y > ptDst2.y) {
+ t = ptDst1.y;
+ ptDst1.y = ptDst2.y;
+ ptDst2.y = t;
+ }
+ // take care of numerical error by adding 1
+ validate(ptDst1.x-1, ptDst1.y-1, ptDst2.x+1, ptDst2.y+1);
+ }
+ }
+
+ void validate(float x1, float y1, float x2, float y2) {
+ boolean doResize = false;
+ isFlushed = false;
+
+ synchronized(canvas3d) {
+ if (initCtx && canvas3d.resizeGraphics2D) {
+ doResize = true;
+ canvas3d.resizeGraphics2D = false;
+ }
+ }
+ if (doResize) {
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ Graphics2D oldOffScreenGraphics2D = offScreenGraphics2D;
+ initCtx = false;
+ init();
+ copyGraphics2D(oldOffScreenGraphics2D);
+ }
+ } else {
+ AffineTransform tr = getTransform();
+ ptSrc.x = x1;
+ ptSrc.y = y1;
+ tr.transform(ptSrc, ptDst1);
+ ptSrc.x = x2;
+ ptSrc.y = y2;
+ tr.transform(ptSrc, ptDst2);
+
+ synchronized (extentLock) {
+ if (ptDst1.x < xmin) {
+ xmin = (int) ptDst1.x;
+ }
+ if (ptDst1.y < ymin) {
+ ymin = (int) ptDst1.y;
+ }
+ if (ptDst2.x > xmax) {
+ xmax = (int) ptDst2.x;
+ }
+ if (ptDst2.y > ymax) {
+ ymax = (int) ptDst2.y;
+ }
+ }
+ }
+ }
+
+ void copyGraphics2D(Graphics2D oldg) {
+ // restore the original setting of Graphics2D when resizing the windows
+ setColor(oldg.getColor());
+ setFont(oldg.getFont());
+ setClip(oldg.getClip());
+ setComposite(oldg.getComposite());
+ setTransform(oldg.getTransform());
+ setPaint(oldg.getPaint());
+ setStroke(oldg.getStroke());
+ if (xOrModeColor != null) {
+ setXORMode(xOrModeColor);
+ }
+
+ }
+
+ // Implementation of Graphics2D methods
+ public final void clip(Shape s) {
+ offScreenGraphics2D.clip(s);
+ }
+
+ public FontMetrics getFontMetrics() {
+ return offScreenGraphics2D.getFontMetrics();
+ }
+
+ public Rectangle getClipBounds(Rectangle r) {
+ return offScreenGraphics2D.getClipBounds(r);
+ }
+
+ public Rectangle getClipRect() {
+ return offScreenGraphics2D.getClipRect();
+ }
+
+ public String toString() {
+ return offScreenGraphics2D.toString();
+
+ }
+
+ public final AffineTransform getTransform() {
+ return offScreenGraphics2D.getTransform();
+ }
+
+ public final Color getColor() {
+ return offScreenGraphics2D.getColor();
+ }
+
+ public final Composite getComposite() {
+ return offScreenGraphics2D.getComposite();
+ }
+
+ public final Font getFont() {
+ return offScreenGraphics2D.getFont();
+ }
+
+ public final FontMetrics getFontMetrics(Font f) {
+ return offScreenGraphics2D.getFontMetrics(f);
+ }
+
+ public final FontRenderContext getFontRenderContext() {
+ return offScreenGraphics2D.getFontRenderContext();
+ }
+
+ public final GraphicsConfiguration getDeviceConfiguration() {
+ return offScreenGraphics2D.getDeviceConfiguration();
+ }
+
+ public final Object getRenderingHint(Key hintKey) {
+ return offScreenGraphics2D.getRenderingHint(hintKey);
+ }
+
+ public final Paint getPaint() {
+ return offScreenGraphics2D.getPaint();
+ }
+
+ public final Rectangle getClipBounds() {
+ return offScreenGraphics2D.getClipBounds();
+ }
+
+ public final RenderingHints getRenderingHints() {
+ return offScreenGraphics2D.getRenderingHints();
+ }
+
+ public final Shape getClip() {
+ return offScreenGraphics2D.getClip();
+ }
+
+ public final Stroke getStroke() {
+ return offScreenGraphics2D.getStroke();
+ }
+
+ public final boolean drawImage(Image img, AffineTransform xform,
+ ImageObserver obs) {
+
+ validate(0, 0, img.getWidth(obs), img.getHeight(obs), xform);
+ return offScreenGraphics2D.drawImage(img, xform, obs);
+ }
+
+ public final void drawImage(BufferedImage img, BufferedImageOp op,
+ int x, int y) {
+ if (op != null) {
+ img = op.filter(img, null);
+ }
+ validate(x, y, x+img.getWidth(), y+img.getHeight());
+ offScreenGraphics2D.drawImage(img, null, x, y);
+ }
+
+ public final boolean drawImage(Image img,
+ int x, int y,
+ ImageObserver observer) {
+
+ validate(x, y,
+ x + img.getWidth(observer),
+ y + img.getWidth(observer));
+ return offScreenGraphics2D.drawImage(img, x, y, observer);
+ }
+
+ public final boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ ImageObserver observer) {
+ validate(x, y, x+width, y+height);
+ return offScreenGraphics2D.drawImage(img, x, y, width, height,
+ observer);
+ }
+
+ public final boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ Color bgcolor,
+ ImageObserver observer) {
+ validate(x, y, x+width, y+height);
+ return offScreenGraphics2D.drawImage(img, x, y, width, height, bgcolor,
+ observer);
+ }
+
+ public final void drawImage(BufferedImage img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer) {
+ validate(dx1, dy1, dx2, dy2);
+ offScreenGraphics2D.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1,
+ sx2, sy2, observer);
+ }
+
+ public final boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer) {
+ validate(dx1, dy1, dx2, dy2);
+ return offScreenGraphics2D.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1,
+ sx2, sy2, observer);
+ }
+
+ public final boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ Color bgcolor,
+ ImageObserver observer) {
+ validate(dx1, dy1, dx2, dy2);
+ return offScreenGraphics2D.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1,
+ sx2, sy2, bgcolor, observer);
+ }
+
+ public final boolean drawImage(Image img, int x, int y,
+ Color bgcolor,
+ ImageObserver observer) {
+ validate(x, y, x+img.getWidth(observer), y+img.getHeight(observer));
+ return offScreenGraphics2D.drawImage(img, x, y, bgcolor, observer);
+ }
+
+ public final boolean hit(Rectangle rect, Shape s, boolean onStroke) {
+ return offScreenGraphics2D.hit(rect, s, onStroke);
+ }
+
+ public final void addRenderingHints(Map hints) {
+ offScreenGraphics2D.addRenderingHints(hints);
+ }
+
+ public final void clipRect(int x, int y, int width, int height) {
+ offScreenGraphics2D.clipRect(x, y, width, height);
+ }
+
+ public final void copyArea(int x, int y, int width, int height,
+ int dx, int dy) {
+ validate(x+dx, y+dy, x+dx+width, y+dy+height);
+ offScreenGraphics2D.copyArea(x, y, width, height, dx, dy);
+ }
+
+ public final void dispose() {
+ offScreenGraphics2D.dispose();
+ }
+
+ public final void draw(Shape s) {
+ Rectangle rect = s.getBounds();
+ validate(rect.x, rect.y,
+ rect.x + rect.width,
+ rect.y + rect.height);
+ offScreenGraphics2D.draw(s);
+ }
+
+ public final void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawArc(x, y, width, height, startAngle, arcAngle);
+ }
+
+ public final void drawGlyphVector(GlyphVector g, float x, float y) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawGlyphVector(g, x, y);
+ }
+
+ public final void drawLine(int x1, int y1, int x2, int y2) {
+ int minx, miny, maxx, maxy;
+ if (!strokeSet) {
+ if (x1 > x2) {
+ minx = x2;
+ maxx = x1;
+ } else {
+ minx = x1;
+ maxx = x2;
+ }
+ if (y1 > y2) {
+ miny = y2;
+ maxy = y1;
+ } else {
+ miny = y1;
+ maxy = y2;
+ }
+ validate(minx, miny, maxx, maxy);
+ } else {
+ // TODO: call validate with bounding box of primitive
+ // TODO: Need to consider Stroke width
+ validate();
+ }
+ offScreenGraphics2D.drawLine(x1, y1, x2, y2);
+ }
+
+ public final void drawOval(int x, int y, int width, int height) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawOval(x, y, width, height);
+ }
+
+ public final void drawPolygon(int xPoints[], int yPoints[],
+ int nPoints) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawPolygon(xPoints, yPoints, nPoints);
+ }
+
+ public final void drawPolyline(int xPoints[], int yPoints[],
+ int nPoints) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawPolyline(xPoints, yPoints, nPoints);
+ }
+
+ public final void drawRenderableImage(RenderableImage img,
+ AffineTransform xform) {
+
+ validate(0, 0, img.getWidth(), img.getHeight(), xform);
+ offScreenGraphics2D.drawRenderableImage(img, xform);
+ }
+
+ public final void drawRenderedImage(RenderedImage img,
+ AffineTransform xform) {
+ validate(0, 0, img.getWidth(), img.getHeight(), xform);
+ offScreenGraphics2D.drawRenderedImage(img, xform);
+ }
+
+ public final void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawRoundRect(x, y, width, height, arcWidth,
+ arcHeight);
+ }
+
+ public final void drawString(AttributedCharacterIterator iterator,
+ int x, int y) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawString(iterator, x, y);
+ }
+
+ public final void drawString(AttributedCharacterIterator iterator,
+ float x, float y) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawString(iterator, x, y);
+ }
+
+ public final void drawString(String s, float x, float y) {
+ TextLayout layout = new TextLayout(s, getFont(),
+ getFontRenderContext());
+ Rectangle2D bounds = layout.getBounds();
+ float x1 = (float) bounds.getX();
+ float y1 = (float) bounds.getY();
+ validate(x1+x, y1+y,
+ x1 + x + (float) bounds.getWidth(),
+ y1 + y + (float) bounds.getHeight());
+ offScreenGraphics2D.drawString(s, x, y);
+
+ }
+
+ public final void drawString(String s, int x, int y) {
+ drawString(s, (float) x, (float) y);
+ }
+
+ public final void fill(Shape s) {
+ Rectangle rect = s.getBounds();
+ validate(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
+ offScreenGraphics2D.fill(s);
+ }
+
+ public final void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.fillArc(x, y, width, height, startAngle, arcAngle);
+ }
+
+ public final void fillOval(int x, int y, int width, int height) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.fillOval(x, y, width, height);
+ }
+
+ public final void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.fillRoundRect(x, y, width, height, arcWidth,
+ arcHeight);
+ }
+
+ public final void rotate(double theta) {
+ offScreenGraphics2D.rotate(theta);
+ }
+
+ public final void rotate(double theta, double x, double y) {
+ offScreenGraphics2D.rotate(theta, x, y);
+ }
+
+ public final void scale(double sx, double sy) {
+ offScreenGraphics2D.scale(sx, sy);
+ }
+
+ public final void setClip(Shape clip) {
+ offScreenGraphics2D.setClip(clip);
+ }
+
+
+ public final void setClip(int x, int y, int width, int height) {
+ offScreenGraphics2D.setClip(x, y, width, height);
+ }
+
+ public final void setColor(Color c) {
+ offScreenGraphics2D.setColor(c);
+ }
+
+ public final void setComposite(Composite comp) {
+ offScreenGraphics2D.setComposite(comp);
+ }
+
+ public final void setFont(Font font) {
+ offScreenGraphics2D.setFont(font);
+ }
+
+ public final void setPaint( Paint paint ) {
+ offScreenGraphics2D.setPaint(paint);
+ }
+
+ public final void setPaintMode() {
+ xOrModeColor = null;
+ offScreenGraphics2D.setPaintMode();
+ }
+
+ public final void setRenderingHint(Key hintKey, Object hintValue) {
+ offScreenGraphics2D.setRenderingHint(hintKey, hintValue);
+ }
+
+ public final void setRenderingHints(Map hints) {
+ offScreenGraphics2D.setRenderingHints(hints);
+ }
+
+ public final void setStroke(Stroke s) {
+ strokeSet = (s != null);
+ offScreenGraphics2D.setStroke(s);
+ }
+
+ public final void setTransform(AffineTransform Tx) {
+ offScreenGraphics2D.setTransform(Tx);
+ }
+
+ public final void setXORMode(Color c1) {
+ xOrModeColor = c1;
+ offScreenGraphics2D.setXORMode(c1);
+ }
+
+ public final void shear(double shx, double shy) {
+ offScreenGraphics2D.shear(shx, shy);
+ }
+
+ public final void transform(AffineTransform Tx) {
+ offScreenGraphics2D.transform(Tx);
+ }
+
+ public final void translate(double tx, double ty) {
+ offScreenGraphics2D.translate(tx, ty);
+ }
+
+ public final void translate(int x, int y) {
+ offScreenGraphics2D.translate(x, y);
+ }
+ public boolean hitClip(int x, int y, int width, int height) {
+ return offScreenGraphics2D.hitClip(x, y, width, height);
+ }
+
+ public void draw3DRect(int x, int y, int width, int height,
+ boolean raised) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.draw3DRect(x, y, width, height, raised);
+ }
+
+ public void drawBytes(byte data[], int offset, int length, int x, int y) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawBytes(data, offset, length, x, y);
+ }
+
+ public void drawChars(char data[], int offset, int length, int x, int y) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawChars(data, offset, length, x, y);
+ }
+
+ public void drawPolygon(Polygon p) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.drawPolygon(p);
+ }
+
+ public void drawRect(int x, int y, int width, int height) {
+ // TODO: call validate with bounding box of primitive
+ // TODO: need to consider Stroke width
+ validate();
+ offScreenGraphics2D.drawRect(x, y, width, height);
+ }
+
+ public void fill3DRect(int x, int y, int width, int height,
+ boolean raised) {
+ // TODO: call validate with bounding box of primitive
+ // TODO: need to consider Stroke width
+ validate();
+ offScreenGraphics2D.fill3DRect(x, y, width, height, raised);
+ }
+
+ public void fillPolygon(Polygon p) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.fillPolygon(p);
+ }
+
+ public final void fillPolygon(int xPoints[], int yPoints[],
+ int nPoints) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.fillPolygon(xPoints, yPoints, nPoints);
+ }
+
+ public final void fillRect(int x, int y, int width, int height) {
+ // TODO: call validate with bounding box of primitive
+ validate();
+ offScreenGraphics2D.fillRect(x, y, width, height);
+ }
+
+ public void finalize() {
+ if (objectId >= 0) {
+ VirtualUniverse.mc.freeTexture2DId(objectId);
+ }
+ offScreenGraphics2D.finalize();
+ }
+
+ public void drawAndFlushImage(BufferedImage img, int x, int y,
+ ImageObserver observer) {
+
+ if (!(initCtx && abgr &&
+ (img.getType() == BufferedImage.TYPE_4BYTE_ABGR))) {
+ drawImage(img, x, y, observer);
+ flush(false);
+ return;
+ }
+
+ if (Thread.currentThread() == canvas3d.screen.renderer) {
+ doDrawAndFlushImage(img, x, y, observer);
+ } else {
+ // Behavior Scheduler or other threads
+ // TODO: may not be legal for behaviorScheduler
+ // May cause deadlock if it is in behaviorScheduler
+ // and we wait for Renderer to finish
+ boolean renderRun = (Thread.currentThread() !=
+ canvas3d.view.universe.behaviorScheduler);
+ sendRenderMessage(renderRun, GraphicsContext3D.DRAWANDFLUSH2D,
+ img, new Point(x, y), observer);
+ }
+ }
+
+ void doDrawAndFlushImage(BufferedImage img, int x, int y,
+ ImageObserver observer) {
+ int imgWidth = img.getWidth(observer);
+ int imgHeight = img.getHeight(observer);
+ int px, py, x1, y1, x2, y2;
+
+ if (canvas3d.ctx == 0) {
+ canvas3d.getGraphicsContext3D().doClear();
+ }
+
+ // format needs to be 4BYTE_ABGR and abgr needs to be supported
+ // also must be in canvas callback
+ data = ((DataBufferByte)img.getRaster().getDataBuffer()).getData();
+
+ // Transform the affine transform,
+ // note we do not handle scale/rotate etc.
+ AffineTransform tr = getTransform();
+ ptSrc.x = x;
+ ptSrc.y = y;
+ tr.transform(ptSrc, ptDst1);
+ px = (int) ptDst1.x;
+ py = (int) ptDst1.y;
+
+ // clip to offscreen buffer size
+
+ if (px + imgWidth > width) {
+ x2 = width - px;
+ } else {
+ x2 = imgWidth;
+ }
+
+ if (px < 0) {
+ x1 = -px;
+ px = 0;
+ } else {
+ x1 = 0;
+ }
+
+ if (py + imgHeight > height) {
+ y2 = height - py;
+ } else {
+ y2 = imgHeight;
+ }
+
+ if (py < 0) {
+ y1 = -py;
+ py = 0;
+ } else {
+ y1 = 0;
+ }
+
+ if ((y2 - y1 > 0) && (x2 - x1 > 0)) {
+ copyDataToCanvas(px, py, x1,y1, x2, y2,imgWidth, imgHeight);
+ }
+
+ }
+
+
+ void copyDataToCanvas(int px, int py, int x1, int y1,
+ int x2, int y2, int w, int h) {
+ try {
+ if (!canvas3d.drawingSurfaceObject.renderLock()) {
+ return;
+ }
+
+ if (useDrawPixel) {
+ canvas3d.composite(canvas3d.ctx, px, py,
+ x1, y1, x2, y2, w, data, width, height);
+ } else {
+ if (!initTexMap) {
+ if (objectId == -1) {
+ objectId = VirtualUniverse.mc.getTexture2DId();
+ }
+ texWidth = getGreaterPowerOf2(w);
+ texHeight = getGreaterPowerOf2(h);
+
+ // Canvas got resize, need to init texture map again
+ // in Renderer thread
+ if (!canvas3d.initTexturemapping(canvas3d.ctx,
+ texWidth, texHeight,
+ objectId)) {
+ // Fail to get the texture surface, most likely
+ // there is not enough texture memory
+ initTexMap = false;
+ VirtualUniverse.mc.freeTexture2DId(objectId);
+ objectId = -1;
+ // Use DrawPixel next time
+ useDrawPixel = true;
+ } else {
+ initTexMap = true;
+ }
+ }
+ if (initTexMap) {
+ canvas3d.texturemapping(canvas3d.ctx, px, py,
+ x1, y1, x2, y2,
+ texWidth, texHeight, w,
+ (abgr ? ImageComponentRetained.BYTE_ABGR:
+ ImageComponentRetained.BYTE_RGBA),
+ objectId, data, width, height);
+ } else {
+ // Fall back to composite for this round
+ canvas3d.composite(canvas3d.ctx, px, py,
+ x1, y1, x2, y2, w, data,
+ width, height);
+
+ }
+ }
+ canvas3d.drawingSurfaceObject.unLock();
+ } catch (NullPointerException ne) {
+ canvas3d.drawingSurfaceObject.unLock();
+ throw ne;
+ }
+
+ clearOffScreen();
+ runMonitor(J3dThread.NOTIFY);
+ }
+
+ void clearOffScreen() {
+ Composite comp = offScreenGraphics2D.getComposite();
+ Color c = offScreenGraphics2D.getColor();
+ offScreenGraphics2D.setComposite(AlphaComposite.Src);
+ offScreenGraphics2D.setColor(blackTransparent);
+ offScreenGraphics2D.fillRect(xmin, ymin, (xmax-xmin), (ymax-ymin));
+ offScreenGraphics2D.setComposite(comp);
+ offScreenGraphics2D.setColor(c);
+ }
+
+ /**
+ * Return an integer of power 2 greater than x
+ */
+ static int getGreaterPowerOf2(int x) {
+ int i = -1;
+ if (x >= 0) {
+ for (i = 1; i < x; i <<= 1);
+ }
+ return i;
+ }
+
+ /**
+ * MC may not scheduler Renderer thread or Renderer thread
+ * may not process message FLUSH. This will hang user
+ * thread.
+ */
+ synchronized void runMonitor(int action) {
+ if (action == J3dThread.WAIT) {
+ if (threadWaiting) {
+ try {
+ wait();
+ } catch (InterruptedException e){}
+ }
+ } else if (action == J3dThread.NOTIFY) {
+ notify();
+ threadWaiting = false;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/J3dDebug.java b/src/classes/share/javax/media/j3d/J3dDebug.java
new file mode 100644
index 0000000..3b056a5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dDebug.java
@@ -0,0 +1,442 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class J3dDebug {
+
+ // For production release devPhase must be set to false.
+
+ // Do no debugging.
+ static final int NO_DEBUG = 0;
+
+ // How much debugging information do we want ?
+ // (LEVEL_1 is very terse, LEVEL_5 is very verbose)
+ static final int LEVEL_1 = 1;
+ static final int LEVEL_2 = 2;
+ static final int LEVEL_3 = 3;
+ static final int LEVEL_4 = 4;
+ static final int LEVEL_5 = 5;
+
+ // This static final variable is used to turn on/off debugging,
+ // checking, and initializing codes that may be preferred in
+ // development phase but not necessarily required in the
+ // production release.
+ //
+ // Beside for debugging, use this variable to do initialization,
+ // checking objects existence, and other checks that may help in
+ // uncovering potential bugs during code development. This
+ // variable should be turned off during production release as it
+ // may cause performance hit.
+ static final boolean devPhase = true;
+
+ // This is a property variable. It allows a true/false be sent to
+ // J3d from command line, to on/off code segments. To avoid
+ // performance hit in production release, this variable MUST be
+ // used with devPhase when guarding code segments for execution.
+ // eg. if(J3dDebug.devPhase && J3dDebug.debug)
+ // do code_segment;
+ // Note: devPhase is a static final variable and debug isn't. If
+ // devPhase is put before debug, smart compiler will not include
+ // code_segment when devPhase is false.
+ static boolean debug;
+
+ // Class debug variable, there is one debug variable per class.
+ // Set one of the 5 debug levels to the class debug variable when
+ // debugging.
+ // For example, alpha = !devPhase?NO_DEBUG:LEVEL_2; will cause
+ // code segments guarded by LEVEL_1 and LEVEL_2 be executed. And
+ // alpha = !devPhase?NO_DEBUG:NO_DEBUG; means do no debug.
+ static final int alpha = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int alternateAppearance = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int ambientLight = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int ambientLightRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int appearance = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int appearanceRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int assertionFailureException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int attributeBin = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int audioDevice = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int audioDevice3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int audioDeviceEnumerator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int auralAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int auralAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int bHInsertStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int bHInternalNode = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int bHLeafInterface = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int bHLeafNode = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int bHNode = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int bHTree = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int background = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int backgroundRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int backgroundSound = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int backgroundSoundRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int badTransformException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int behavior = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int behaviorRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int behaviorScheduler = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int behaviorStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int billboard = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int boundingBox = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int boundingLeaf = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int boundingLeafRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int boundingPolytope = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int boundingSphere = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int bounds = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int branchGroup = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int branchGroupRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int cachedFrustum = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int canvas3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int canvasViewCache = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int canvasViewEventCatcher = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int capabilityBits = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int capabilityNotSetException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int clip = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int clipRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int colorInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int coloringAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int coloringAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int compileState = !devPhase?NO_DEBUG:LEVEL_3;
+ static final int compressedGeometry = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int compressedGeometryHeader = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int compressedGeometryRenderMethod = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int compressedGeometryRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int coneSound = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int coneSoundRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int danglingReferenceException = !devPhase?NO_DEBUG:NO_DEBUG;
+
+ static final int decalGroup = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int decalGroupRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int defaultRenderMethod = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponent = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponentFloat = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponentFloatRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponentInt = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponentIntRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponentNative = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponentNativeRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int depthComponentRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int directionalLight = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int directionalLightRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int displayListRenderMethod = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int distanceLOD = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int environmentSet = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int eventCatcher = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int exponentialFog = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int exponentialFogRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int fog = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int fogRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int font3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int fontExtrusion = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int generalizedStrip = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int generalizedStripFlags = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int generalizedVertexList = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometry = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryAtom = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryDecompressor = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryDecompressorRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryDecompressorShape3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryLock = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryLockInterface = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryStripArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryStripArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int geometryUpdater = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int graphicsConfigTemplate3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int graphicsContext3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int group = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int groupRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int hashKey = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int hiResCoord = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int illegalRenderingStateException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int illegalSharingException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int imageComponent = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int imageComponent2D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int imageComponent2DRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int imageComponent3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int imageComponent3DRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int imageComponentRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedGeometryArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedGeometryArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedGeometryStripArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedGeometryStripArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedLineArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedLineArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedLineStripArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedLineStripArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedPointArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedPointArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedQuadArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedQuadArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedTriangleArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedTriangleArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedTriangleFanArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedTriangleFanArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedTriangleStripArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int indexedTriangleStripArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int inputDevice = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int inputDeviceBlockingThread = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int inputDeviceScheduler = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int interpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dDataInputStream = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dDataOutputStream = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dDebug = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dI18N = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dMessage = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dNodeTable = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dQueryProps = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dThread = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int j3dThreadData = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lOD = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int leaf = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int leafRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int light = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lightBin = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lightRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lightSet = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lineArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lineArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lineAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lineAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lineStripArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int lineStripArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int linearFog = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int linearFogRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int link = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int linkRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int locale = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int mRSWLock = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int masterControl = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int material = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int materialRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int mediaContainer = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int mediaContainerRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int modelClip = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int modelClipRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int morph = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int morphRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int multipleParentException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int node = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int nodeComponent = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int nodeComponentRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int nodeReferenceTable = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int nodeRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int objectUpdate = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int orderedBin = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int orderedCollection = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int orderedGroup = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int orderedGroupRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pathInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int physicalBody = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int physicalEnvironment = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickBounds = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickCone = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickCylinderRay = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickCylinderSegment = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickPoint = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickRay = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickSegment = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pickShape = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int picking = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointLight = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointLightRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointSound = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int pointSoundRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int polygonAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int polygonAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int positionInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int positionPathInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int quadArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int quadArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int raster = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int rasterRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderAtom = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderBin = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderBinLock = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderMethod = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderMolecule = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderer = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int rendererStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderingAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderingAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderingAttributesStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int renderingEnvironmentStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int restrictedAccessException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int rotPosPathInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int rotPosScalePathInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int rotationInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int rotationPathInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int scaleInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sceneGraphCycleException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sceneGraphObject = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sceneGraphObjectRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sceneGraphPath = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int screen3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int screenViewCache = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sensor = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sensorRead = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int setLiveState = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int shape3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int shape3DRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sharedGroup = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sharedGroupRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int sound = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int soundException = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int soundRenderer = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int soundRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int soundScheduler = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int soundStructure = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int soundscape = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int soundscapeRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int spotLight = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int spotLightRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int structureUpdateThread = !devPhase?NO_DEBUG:NO_DEBUG;
+
+ // switch is a reserved word.
+ static final int Switch = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int switchRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int switchValueInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int table = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int texCoordGeneration = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int texCoordGenerationRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int text3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int text3DRenderMethod = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int text3DRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int texture = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int texture2D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int texture2DRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int texture3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int texture3DRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int textureAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int textureAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int textureBin = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int textureRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int textureSetting = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int timerThread = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int transform3D = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int transformGroup = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int transformGroupRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int transformStructure = !devPhase?NO_DEBUG:J3dDebug.LEVEL_3;
+ static final int transparencyAttributes = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int transparencyAttributesRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int transparencyInterpolator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int triangleArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int triangleArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int triangleFanArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int triangleFanArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int triangleStripArray = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int triangleStripArrayRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int unorderList = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int vertexArrayRenderMethod = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int view = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int viewCache = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int viewPlatform = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int viewPlatformRetained = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int virtualUniverse = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupAnd = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupAndOfOrs = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupCondition = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupCriteriaEnumerator = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupCriterion = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnAWTEvent = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnActivation = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnBehaviorPost = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnCollisionEntry = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnCollisionExit = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnCollisionMovement = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnDeactivation = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnElapsedFrames = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnElapsedTime = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnElapsedTimeHeap = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnSensorEntry = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnSensorExit = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnTransformChange = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnViewPlatformEntry = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOnViewPlatformExit = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOr = !devPhase?NO_DEBUG:NO_DEBUG;
+ static final int wakeupOrOfAnds = !devPhase?NO_DEBUG:NO_DEBUG;
+
+
+ static boolean doDebug(int j3dClassLevel, int level, String str) {
+ if(j3dClassLevel >= level) {
+ System.out.print(str);
+ return true;
+ }
+ return false;
+ }
+
+ static boolean doDebug(int j3dClassLevel, int level) {
+ if(j3dClassLevel >= level) {
+ return true;
+ }
+ return false;
+ }
+
+ static void doAssert(boolean expr, String str) {
+ if (! expr) {
+ throw new AssertionFailureException("(" + str + ")" + "is false");
+ }
+ }
+
+ static void pkgInfo(ClassLoader classLoader,
+ String pkgName,
+ String className) {
+
+ try {
+ classLoader.loadClass(pkgName + "." + className);
+
+ Package p = Package.getPackage(pkgName);
+ if (p == null) {
+ System.out.println("WARNING: Package.getPackage(" +
+ pkgName +
+ ") is null");
+ }
+ else {
+ if(devPhase && debug) {
+ System.out.println(p);
+ System.out.println("Specification Title = " +
+ p.getSpecificationTitle());
+ System.out.println("Specification Vendor = " +
+ p.getSpecificationVendor());
+ System.out.println("Specification Version = " +
+ p.getSpecificationVersion());
+ System.out.println("Implementation Vendor = " +
+ p.getImplementationVendor());
+ System.out.println("Implementation Version = " +
+ p.getImplementationVersion());
+ }
+ else if(devPhase)
+ System.out.println(", Java 3D " + p.getImplementationVersion() + ".");
+ }
+ }
+ catch (ClassNotFoundException e) {
+ System.out.println("Unable to load " + pkgName);
+ }
+
+ // System.out.println();
+ }
+
+
+ static {
+ // initialize the debug flag
+ debug = false;
+ }
+
+}
+
diff --git a/src/classes/share/javax/media/j3d/J3dI18N.java b/src/classes/share/javax/media/j3d/J3dI18N.java
new file mode 100644
index 0000000..1d62767
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dI18N.java
@@ -0,0 +1,31 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.io.*;
+import java.util.*;
+
+
+class J3dI18N {
+ static String getString(String key) {
+ String s;
+ try {
+ s = (String) ResourceBundle.getBundle("javax.media.j3d.ExceptionStrings").getString(key);
+ }
+ catch (MissingResourceException e) {
+ System.err.println("J3dI18N: Error looking up: " + key);
+ s = key;
+ }
+ return s;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/J3dMessage.java b/src/classes/share/javax/media/j3d/J3dMessage.java
new file mode 100644
index 0000000..586c23a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dMessage.java
@@ -0,0 +1,165 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The J3dMessage is the super class of all messages in Java 3D. It implements
+ * all of the common data members needed.
+ */
+
+class J3dMessage extends Object {
+ /**
+ * The various message types.
+ */
+ static final int INVALID_TYPE = -1;
+ static final int INSERT_NODES = 0;
+ static final int REMOVE_NODES = 1;
+ static final int RUN = 2;
+ static final int TRANSFORM_CHANGED = 3;
+ static final int UPDATE_VIEW = 4;
+ static final int STOP_THREAD = 5;
+ static final int COLORINGATTRIBUTES_CHANGED = 6;
+ static final int LINEATTRIBUTES_CHANGED = 7;
+ static final int POINTATTRIBUTES_CHANGED = 8;
+ static final int POLYGONATTRIBUTES_CHANGED = 9;
+ static final int RENDERINGATTRIBUTES_CHANGED = 10;
+ static final int TEXTUREATTRIBUTES_CHANGED = 11;
+ static final int TRANSPARENCYATTRIBUTES_CHANGED = 12;
+ static final int MATERIAL_CHANGED = 13;
+ static final int TEXCOORDGENERATION_CHANGED = 14;
+ static final int TEXTURE_CHANGED = 15;
+ static final int MORPH_CHANGED = 16;
+ static final int GEOMETRY_CHANGED = 17;
+ static final int APPEARANCE_CHANGED = 18;
+ static final int LIGHT_CHANGED = 19;
+ static final int BACKGROUND_CHANGED = 20;
+ static final int CLIP_CHANGED = 21;
+ static final int FOG_CHANGED = 22;
+ static final int BOUNDINGLEAF_CHANGED = 23;
+ static final int SHAPE3D_CHANGED = 24;
+ static final int TEXT3D_TRANSFORM_CHANGED = 25;
+ static final int TEXT3D_DATA_CHANGED = 26;
+ static final int SWITCH_CHANGED = 27;
+ static final int COND_MET = 28;
+ static final int BEHAVIOR_ENABLE = 29;
+ static final int BEHAVIOR_DISABLE = 30;
+ static final int INSERT_RENDERATOMS = 31;
+ static final int ORDERED_GROUP_INSERTED = 32;
+ static final int ORDERED_GROUP_REMOVED = 33;
+ static final int COLLISION_BOUND_CHANGED = 34;
+ static final int REGION_BOUND_CHANGED = 35;
+ static final int MODELCLIP_CHANGED = 36;
+ static final int BOUNDS_AUTO_COMPUTE_CHANGED = 37;
+ static final int SOUND_ATTRIB_CHANGED = 38;
+ static final int AURALATTRIBUTES_CHANGED = 39;
+ static final int SOUNDSCAPE_CHANGED = 40;
+ static final int ALTERNATEAPPEARANCE_CHANGED = 41;
+ static final int RENDER_OFFSCREEN = 42;
+ static final int RENDER_RETAINED = 43;
+ static final int RENDER_IMMEDIATE = 44;
+ static final int SOUND_STATE_CHANGED = 45;
+ static final int ORIENTEDSHAPE3D_CHANGED = 46;
+ static final int TEXTURE_UNIT_STATE_CHANGED = 47;
+ static final int UPDATE_VIEWPLATFORM = 48;
+ static final int BEHAVIOR_ACTIVATE = 49;
+ static final int GEOMETRYARRAY_CHANGED = 50;
+ static final int MEDIA_CONTAINER_CHANGED = 51;
+ static final int RESIZE_CANVAS = 52;
+ static final int TOGGLE_CANVAS = 53;
+ static final int IMAGE_COMPONENT_CHANGED = 54;
+ static final int SCHEDULING_INTERVAL_CHANGED = 55;
+ static final int VIEWSPECIFICGROUP_CHANGED = 56;
+ static final int VIEWSPECIFICGROUP_INIT = 57;
+ static final int VIEWSPECIFICGROUP_CLEAR = 58;
+ static final int ORDERED_GROUP_TABLE_CHANGED = 59;
+ static final int BEHAVIOR_REEVALUATE = 60;
+
+ /**
+ * This is the time snapshot at which this change occured
+ */
+ long time = -1;
+
+ /**
+ * This is the number of references to this message
+ */
+ private int refcount = 0;
+
+ /**
+ * This is a bitmask of the types of threads that need to be run
+ * once this message is consumed.
+ */
+ int threads = 0;
+
+ /**
+ * The universe that this message originated from
+ */
+ VirtualUniverse universe;
+
+ /**
+ * This holds the type of this message
+ */
+ int type = -1;
+
+ /**
+ * This holds the target view of this message, null means all views
+ */
+ View view = null;
+
+
+ /**
+ * The arguements for a message, 5 for now
+ */
+ static final int MAX_ARGS = 6;
+
+ Object[] args = new Object[MAX_ARGS];
+
+ /**
+ * This constructor does nothing
+ */
+ J3dMessage() {
+ }
+
+ final synchronized void clear() {
+ // System.out.println("J3dMessage : " + this );
+ view = null;
+ universe = null;
+ args[0] = null;
+ args[1] = null;
+ args[2] = null;
+ args[3] = null;
+ args[4] = null;
+ args[5] = null;
+ }
+
+ /**
+ * This increments the reference count for this message
+ */
+ final synchronized void incRefcount() {
+ refcount++;
+ }
+
+ /**
+ * This decrements the reference count for this message. If it goes
+ * to 0, the message is put on the MasterControl freelist.
+ */
+ final synchronized void decRefcount() {
+ if (--refcount == 0) {
+ clear();
+ FreeListManager.freeObject(FreeListManager.MESSAGE, this);
+ }
+ }
+
+ final synchronized int getRefcount() {
+ return refcount;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/J3dNodeTable.java b/src/classes/share/javax/media/j3d/J3dNodeTable.java
new file mode 100644
index 0000000..ee38669
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dNodeTable.java
@@ -0,0 +1,290 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.io.DataOutputStream;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import javax.vecmath.Color3f;
+import javax.vecmath.Tuple3f;
+
+/**
+ * The J3dNodeTable object provides utilities for the save/load
+ * methods in the Java3d nodes. Specifically, it holds an enumerated
+ * list of the Java3D node types and their respective Class names.
+ * It keeps these lists in a Hashtable and an array and allows
+ * other classes to get an enumerated value associated with an Object
+ * type or an instance of an Object associated with an enumerated value.
+ *
+ */
+class J3dNodeTable {
+
+ // nodeTable holds the enumerated value/Classname pairs. This is
+ // used to look up enumerated values given a Class name.
+ Hashtable nodeTable = new Hashtable();
+
+ // nodeArray holds the value/Classname pairs also, but allows lookups
+ // in the other direction (given a value, what's the Class name?)
+ String nodeArray[];
+
+ // Following is a list of the current scene graph objects of java3D.
+ // In order to make later node insertion easier, and to add some logic to
+ // this arbitrary list, the groups of nodes are grouped in terms of
+ // similar functionality of node types.
+
+ // Maximum number of nodes
+ static final int MAX_NUM_NODES = 200;
+
+ // Nothing occupies slot 0 - thus we can return 0 from this class'
+ // methods to denote failure.
+ static final int NOTHING = 0;
+
+ // 1 - 9: Groups
+ static final int GROUP = 1;
+ static final int TRANSFORM_GROUP = 2;
+ static final int SWITCH_GROUP = 3;
+ static final int ORDERED_GROUP = 4;
+ static final int BRANCH_GROUP = 5;
+ static final int ENDGROUP = 9; // denotes done with group
+
+ // 10 - 19: Shape3D (in a class by itself)
+ static final int SHAPE3D = 10;
+
+ // 20 - 49: Appearance and all of its attributes
+ static final int APPEARANCE = 20;
+ static final int MATERIAL = 21;
+ static final int TEXTURE = 22;
+ static final int TEX_COORD_GENERATION = 23;
+ static final int TEXTURE_ATTRIBUTES = 24;
+ static final int COLORING_ATTRIBUTES = 25;
+ static final int TRANSPARENCY_ATTRIBUTES = 26;
+ static final int RENDERING_ATTRIBUTES = 27;
+ static final int POLYGON_ATTRIBUTES = 28;
+ static final int LINE_ATTRIBUTES = 29;
+ static final int POINT_ATTRIBUTES = 30;
+ static final int TEXTURE_2D = 31;
+ static final int TEXTURE_3D = 32;
+ static final int IMAGE_COMPONENT = 33;
+ static final int IMAGE_COMPONENT_2D = 34;
+ static final int IMAGE_COMPONENT_3D = 35;
+ static final int ENDAPPEARANCE = 49;
+
+ // 100 - 149: All Geometry types
+ static final int GEOMETRY = 100;
+ static final int COMPRESSED_GEOMETRY = 101;
+ static final int GEOMETRY_ARRAY = 102;
+ static final int GEOMETRY_STRIP_ARRAY = 103;
+ static final int INDEXED_GEOMETRY_ARRAY = 104;
+ static final int INDEXED_GEOMETRY_STRIP_ARRAY = 105;
+ static final int INDEXED_LINE_ARRAY = 106;
+ static final int INDEXED_LINE_STRIP_ARRAY = 107;
+ static final int INDEXED_POINT_ARRAY = 108;
+ static final int INDEXED_QUAD_ARRAY = 109;
+ static final int INDEXED_TRIANGLE_ARRAY = 110;
+ static final int INDEXED_TRIANGLE_FAN_ARRAY = 111;
+ static final int INDEXED_TRIANGLE_STRIP_ARRAY = 112;
+ static final int LINE_ARRAY = 113;
+ static final int LINE_STRIP_ARRAY = 114;
+ static final int POINT_ARRAY = 115;
+ static final int QUAD_ARRAY = 116;
+ static final int TRIANGLE_ARRAY = 117;
+ static final int TRIANGLE_FAN_ARRAY = 118;
+ static final int TRIANGLE_STRIP_ARRAY = 119;
+ static final int BACKGROUND_SOUND = 120;
+ static final int POINT_SOUND = 121;
+ static final int CONE_SOUND = 122;
+ static final int MEDIA_CONTAINER = 123;
+
+ // 150 - 169: Behaviors
+ static final int ROTATION_INTERPOLATOR = 150;
+ static final int ROTPOSSCALEPATH_INTERPOLATOR = 151;
+ static final int ROTATIONPATH_INTERPOLATOR = 152;
+ static final int POSITIONPATH_INTERPOLATOR = 153;
+ static final int ROTPOSPATH_INTERPOLATOR = 154;
+ static final int POSITION_INTERPOLATOR = 155;
+ static final int SWITCHVALUE_INTERPOLATOR = 156;
+ static final int COLOR_INTERPOLATOR = 157;
+ static final int SCALE_INTERPOLATOR = 158;
+ // Problem: these next two are non j3d-core items
+ static final int SOUND_PLAYER = 159;
+ static final int SOUND_FADER = 160;
+
+ // 170 - 189: Various utility nodes
+ static final int BOUNDS = 170;
+ static final int BOUNDING_SPHERE = 171;
+ static final int BOUNDING_BOX = 172;
+ static final int BOUNDING_POLYTOPE = 173;
+ static final int TRANSFORM3D = 180;
+ static final int BACKGROUND = 181;
+
+ // 190 - 199: Lights
+ static final int LIGHT = 190;
+ static final int POINT_LIGHT = 191;
+ static final int SPOT_LIGHT = 192;
+ static final int DIRECTIONAL_LIGHT = 193;
+ static final int AMBIENT_LIGHT = 194;
+
+
+ /**
+ * Constructs this Object, which initializes the array and Hashtable
+ */
+ J3dNodeTable() {
+ nodeArray = new String[MAX_NUM_NODES];
+ //Initialize all table entries to null
+ for (int i = 0; i < MAX_NUM_NODES; ++i)
+ nodeArray[i] = null;
+ // Setup slots of nodeArray where there should be valid values
+ nodeArray[GROUP] = "Group";
+ nodeArray[TRANSFORM_GROUP] = "TransformGroup";
+ nodeArray[SWITCH_GROUP] = "Switch";
+ nodeArray[ORDERED_GROUP] = "OrderedGroup";
+ nodeArray[BRANCH_GROUP] = "BranchGroup";
+
+ nodeArray[SHAPE3D] = "Shape3D";
+
+ nodeArray[APPEARANCE] = "Appearance";
+ nodeArray[MATERIAL] = "Material";
+ nodeArray[TEXTURE] = "Texture";
+ nodeArray[TEXTURE_2D] = "Texture2D";
+ nodeArray[TEXTURE_3D] = "Texture3D";
+ nodeArray[IMAGE_COMPONENT] = "ImageComponent";
+ nodeArray[IMAGE_COMPONENT_2D] = "ImageComponent2D";
+ nodeArray[IMAGE_COMPONENT_3D] = "ImageComponent3D";
+ nodeArray[TRANSPARENCY_ATTRIBUTES] = "TransparencyAttributes";
+
+ nodeArray[GEOMETRY] = "Geometry";
+ nodeArray[COMPRESSED_GEOMETRY] = "CompressedGeometry";
+ nodeArray[GEOMETRY_ARRAY] = "GeometryArray";
+ nodeArray[GEOMETRY_STRIP_ARRAY] = "GeometryStripArray";
+ nodeArray[INDEXED_GEOMETRY_ARRAY] = "IndexedGeometryArray";
+ nodeArray[INDEXED_GEOMETRY_STRIP_ARRAY] = "IndexedGeometryStripArray";
+ nodeArray[INDEXED_LINE_ARRAY] = "IndexedLineArray";
+ nodeArray[INDEXED_LINE_STRIP_ARRAY] = "IndexedLineStripArray";
+ nodeArray[INDEXED_POINT_ARRAY] = "IndexedPointArray";
+ nodeArray[INDEXED_QUAD_ARRAY] = "IndexedQuadArray";
+ nodeArray[INDEXED_TRIANGLE_ARRAY] = "IndexedTriangleArray";
+ nodeArray[INDEXED_TRIANGLE_FAN_ARRAY] = "IndexedTriangleFanArray";
+ nodeArray[INDEXED_TRIANGLE_STRIP_ARRAY] = "indexedTriangleStripArray";
+ nodeArray[LINE_ARRAY] = "LineArray";
+ nodeArray[LINE_STRIP_ARRAY] = "LineStripArray";
+ nodeArray[POINT_ARRAY] = "PointArray";
+ nodeArray[QUAD_ARRAY] = "QuadArray";
+ nodeArray[TRIANGLE_ARRAY] = "TriangleArray";
+ nodeArray[TRIANGLE_FAN_ARRAY] = "TriangleFanArray";
+ nodeArray[TRIANGLE_STRIP_ARRAY] = "TriangleStripArray";
+ nodeArray[BACKGROUND_SOUND] = "BackgroundSound";
+ nodeArray[POINT_SOUND] = "PointSound";
+ nodeArray[CONE_SOUND] = "ConeSound";
+ nodeArray[MEDIA_CONTAINER] = "MediaContainer";
+
+ nodeArray[ROTATION_INTERPOLATOR] = "RotationInterpolator";
+ nodeArray[ROTPOSSCALEPATH_INTERPOLATOR] = "RotPosScalePathInterpolator";
+ nodeArray[ROTATIONPATH_INTERPOLATOR] = "RotationPathInterpolator";
+ nodeArray[POSITIONPATH_INTERPOLATOR] = "PositionPathInterpolator";
+ nodeArray[ROTPOSPATH_INTERPOLATOR] = "RotPosPathInterpolator";
+ nodeArray[POSITION_INTERPOLATOR] = "PositionInterpolator";
+ nodeArray[SWITCHVALUE_INTERPOLATOR] = "SwitchValueInterpolator";
+ nodeArray[COLOR_INTERPOLATOR] = "ColorInterpolator";
+ nodeArray[SCALE_INTERPOLATOR] = "ScaleInterpolator";
+ nodeArray[SOUND_PLAYER] = "SoundPlayer";
+ nodeArray[SOUND_FADER] = "SoundFader";
+
+ nodeArray[BOUNDS] = "Bounds";
+ nodeArray[BOUNDING_SPHERE] = "BoundingSphere";
+ nodeArray[BOUNDING_BOX] = "BoundingBox";
+ nodeArray[BOUNDING_POLYTOPE] = "BoundingPolytope";
+ nodeArray[TRANSFORM3D] = "Transform3D";
+ nodeArray[BACKGROUND] = "Background";
+
+ nodeArray[LIGHT] = "Light";
+ nodeArray[POINT_LIGHT] = "PointLight";
+ nodeArray[SPOT_LIGHT] = "SpotLight";
+ nodeArray[DIRECTIONAL_LIGHT] = "DirectionalLight";
+ nodeArray[AMBIENT_LIGHT] = "AmbientLight";
+
+ for (int i = 0; i < MAX_NUM_NODES; ++i) {
+ if (nodeArray[i] != null)
+ nodeTable.put(nodeArray[i], new Integer(i));
+ }
+
+ }
+
+ /**
+ * Returns the enumerated value associated with an Object. This
+ * method retrieves the base class name (with no package name and
+ * with no "Retained" portion (if it's part of the Object's name);
+ * we're just looking for the base Java3d node type here.
+ */
+ int getNodeValue(Object object) {
+ Integer i;
+ String fullName = object.getClass().getName();
+ int firstIndex;
+ int lastIndex ;
+ if ((firstIndex = fullName.lastIndexOf(".")) == -1)
+ firstIndex = 0;
+ else
+ firstIndex++;
+ if ((lastIndex = fullName.lastIndexOf("Retained")) == -1)
+ lastIndex = fullName.length();
+ String nodeName = fullName.substring(firstIndex, lastIndex);
+ if ((i = (Integer)nodeTable.get(nodeName))
+ != null) {
+ return i.intValue();
+ }
+ else {
+ // This conditional exists because if a group node is
+ // actually a subclass of some standard Java3d Group type
+ // (e.g., VrmlParser), then we'll just save it and reload
+ // it as a Group.
+ if (object instanceof TransformGroup ||
+ object instanceof TransformGroupRetained)
+ return TRANSFORM_GROUP;
+ else if (object instanceof BranchGroup ||
+ object instanceof BranchGroupRetained)
+ return BRANCH_GROUP;
+ else if (object instanceof Switch ||
+ object instanceof SwitchRetained)
+ return SWITCH_GROUP;
+ else if (object instanceof Group ||
+ object instanceof GroupRetained)
+ return GROUP;
+ else if (object instanceof Shape3D)
+ return SHAPE3D; // For Text2D object in particular
+ else {
+ System.err.println("Warning: Don't know how to save object of type " + object);
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * Returns new instance of an object with the Class name
+ * associated with the given enumerated value.
+ */
+ Object getObject(int nodeValue) {
+ try {
+ if (nodeArray[nodeValue] != null) {
+ String nodeName = "javax.media.j3d." + nodeArray[nodeValue];
+ return Class.forName(nodeName).newInstance();
+ }
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Exception creating object for nodeValue " +
+ nodeValue + "\n nodeName = javax.media.j3d." +
+ nodeArray[nodeValue]);
+ }
+ return null;
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/J3dQueryProps.java b/src/classes/share/javax/media/j3d/J3dQueryProps.java
new file mode 100644
index 0000000..9b3a4c3
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dQueryProps.java
@@ -0,0 +1,110 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+
+/**
+ * Properties object for query operations. It is a read-only Map backed
+ * up by a Hashtable.
+ */
+class J3dQueryProps extends AbstractMap {
+ private Hashtable table;
+ private Set entrySet = null;
+
+
+ /**
+ * Constructs a new J3dQueryProps object using the specified
+ * array of keys and the specified values. The arrays must be
+ * the same size.
+ */
+ J3dQueryProps(String[] keys, Object[] values) {
+ table = new Hashtable();
+ for (int i = 0; i < keys.length; i++) {
+ table.put(keys[i], values[i]);
+ }
+ }
+
+ /**
+ * Gets value corresponding to specified key
+ */
+ public Object get(Object key) {
+ return table.get(key);
+ }
+
+ /**
+ * Returns true if the specified key is contained in this Map
+ */
+ public boolean containsKey(Object key) {
+ return table.containsKey(key);
+ }
+
+ /**
+ * Returns true if the specified value is contained in this Map
+ */
+ public boolean containsValue(Object value) {
+ return table.containsValue(value);
+ }
+
+ /**
+ * Returns a new Set object for the entries of this map
+ */
+ public Set entrySet() {
+ if (entrySet == null)
+ entrySet = new EntrySet();
+
+ return entrySet;
+ }
+
+
+ /**
+ * Entry set class
+ */
+ private class EntrySet extends AbstractSet {
+ private EntrySet() {
+ }
+
+ public int size() {
+ return table.size();
+ }
+
+ public Iterator iterator() {
+ return new MapIterator();
+ }
+ }
+
+
+ /**
+ * Entry set class
+ */
+ private class MapIterator implements Iterator {
+ private Iterator i;
+
+ private MapIterator() {
+ i = table.entrySet().iterator();
+ }
+
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ public Object next() {
+ return i.next();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/J3dStructure.java b/src/classes/share/javax/media/j3d/J3dStructure.java
new file mode 100644
index 0000000..7c4faab
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dStructure.java
@@ -0,0 +1,153 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * The J3dStructure is the super class of all structures in Java 3D.
+ * A structure is a object that organizes a collection of objects.
+ */
+
+abstract class J3dStructure extends Object {
+ /**
+ * This is the list of messages to be processed by this structure
+ */
+ UnorderList messageList = new UnorderList(5, J3dMessage.class);
+
+ /**
+ * This is the update Thread for this structure
+ */
+
+ StructureUpdateThread updateThread = null;
+
+ /**
+ * This is the type of update thread
+ */
+ int threadType = -1;
+
+ /**
+ * The universe of this structure
+ */
+ VirtualUniverse universe = null;
+
+ /**
+ * The thread data for the update thread
+ */
+ J3dThreadData threadData = new J3dThreadData();
+
+ /**
+ * number of messages for this snapshot of time
+ */
+ int nMessage = 0;
+ J3dMessage[] msgList = new J3dMessage[5];
+
+ /**
+ * This constructor does nothing
+ */
+ J3dStructure(VirtualUniverse u, int type) {
+ universe = u;
+ threadType = type;
+ threadData.threadType = type;
+ }
+
+ /**
+ * This returns the thread data for this thread.
+ */
+ final J3dThreadData getUpdateThreadData() {
+ return (threadData);
+ }
+
+ /**
+ * This adds a message to the list of messages for this structure
+ */
+ final void addMessage(J3dMessage message) {
+
+ if (threadData != null) {
+ threadData.lastUpdateTime = message.time;
+ } else {
+ // this force message to consume when initialized
+ message.time = -1;
+ }
+ message.incRefcount();
+ messageList.add(message);
+ }
+
+
+ /**
+ * This returns whether or not there are any pending messages
+ */
+ final J3dMessage[] getMessages(long referenceTime) {
+ int sz, n = 0;
+
+ synchronized (messageList) {
+ if ((sz = messageList.size()) > 0) {
+ J3dMessage mess[] = (J3dMessage []) messageList.toArray(false);
+ for (n = 0; n < sz; n++) {
+ if (mess[n].time > referenceTime) {
+ break;
+ }
+ }
+ if (n > 0) {
+ if (msgList.length < n) {
+ msgList = new J3dMessage[n];
+ }
+ messageList.shift(msgList, n);
+ }
+ }
+ }
+
+ nMessage = n;
+ return msgList;
+ }
+
+ final void clearMessages() {
+ synchronized (messageList) {
+ int nmessage = messageList.size();
+ if (nmessage > 0) {
+ J3dMessage mess[] = (J3dMessage []) messageList.toArray(false);
+ for (int i = nmessage-1; i >=0; i--) {
+ mess[i].decRefcount();
+ }
+ messageList.clear();
+ }
+ nMessage = 0;
+ msgList = new J3dMessage[5];
+ }
+
+ }
+
+ int getNumMessage() {
+ return nMessage;
+ }
+
+ /**
+ * This gets overriden by the structure
+ */
+ abstract void processMessages(long referenceTime);
+
+ /**
+ * This is used by MasterControl to process any unused message
+ * for final cleanup. DON'T decrememt message count in
+ * the method, as it is done by MasterControl.
+ */
+ abstract void removeNodes(J3dMessage m);
+
+ /**
+ * Release resource associate with this structure before GC
+ * We need to clear all those IndexedUnorderSet/WakeupIndexedList
+ * so that the listIdx associate with IndexedObject reset to -1.
+ */
+ abstract void cleanup();
+}
diff --git a/src/classes/share/javax/media/j3d/J3dThread.java b/src/classes/share/javax/media/j3d/J3dThread.java
new file mode 100644
index 0000000..4e94e5e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dThread.java
@@ -0,0 +1,316 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The J3dThread is the super class of all slave threads in Java 3D. It implements
+ * all of the common flow control constructs.
+ */
+
+abstract class J3dThread extends Thread {
+ /**
+ * These are the thread types that a message may affect
+ */
+ static final int BEHAVIOR_SCHEDULER = 0x01;
+ static final int SOUND_SCHEDULER = 0x02;
+ static final int INPUT_DEVICE_SCHEDULER = 0x04;
+ static final int RENDER_THREAD = 0x10;
+// static final int COLLISION_THREAD = 0x20;
+ static final int UPDATE_GEOMETRY = 0x40;
+ static final int UPDATE_RENDER = 0x80;
+ static final int UPDATE_BEHAVIOR = 0x100;
+ static final int UPDATE_SOUND = 0x200;
+ static final int UPDATE_RENDERING_ATTRIBUTES = 0x400;
+ static final int UPDATE_RENDERING_ENVIRONMENT = 0x1000;
+ static final int UPDATE_TRANSFORM = 0x2000;
+
+ /**
+ * The classification types.
+ */
+ static final int WORK_THREAD = 0x01;
+ static final int UPDATE_THREAD = 0x02;
+
+ /**
+ * This runMonitor action puts the thread into an initial wait state
+ */
+ static final int WAIT = 0;
+
+ /**
+ * This runMonitor action notifies MasterControl that this thread
+ * has completed and wait.
+ */
+ static final int NOTIFY_AND_WAIT = 1;
+
+ /**
+ * This is used by Canvas3D Renderer to notify user thread
+ * that swap is completed.
+ */
+ static final int NOTIFY = 2;
+
+ /**
+ * This runMonitor action tells the thread to run N number of
+ * iterations.
+ */
+ static final int RUN = 2;
+
+ /**
+ * This runMonitor action tells the thread to stop running
+ */
+ static final int STOP = 3;
+
+ /**
+ * This indicates that this thread has been activated by MC
+ */
+ boolean active = false;
+
+ /**
+ * This indicates that this thread is alive and running
+ */
+ private boolean running = true;
+
+ /**
+ * The thread data for this thread
+ */
+ private J3dThreadData[] data = null;
+
+ /**
+ * This indicates that this thread is ready
+ */
+ private volatile boolean started = false;
+
+ /**
+ * The time values passed into this thread
+ */
+ long referenceTime;
+
+ /**
+ * Use to assign threadOpts WAIT_ALL_THREADS
+ */
+ long lastWaitTimestamp = 0;
+
+ /**
+ * The type of this thread. It is one of the above constants.
+ */
+ int type;
+
+ /**
+ * The classification of this thread. It is one of the above constants.
+ */
+ int classification = WORK_THREAD;
+
+ /**
+ * The arguments passed in for this thread
+ */
+ Object[] args = null;
+
+ /**
+ * Flag to indicate that user initiate a thread stop
+ */
+ volatile boolean userStop = false;
+
+ /**
+ * Flag to indicate that this thread is waiting to be notify
+ */
+ volatile boolean waiting = false;
+
+ /**
+ * Some variables used to name threads correctly
+ */
+ private static int numInstances = 0;
+ private int instanceNum = -1;
+
+ private 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.
+ */
+ abstract void doWork(long referenceTime);
+
+ /**
+ * This constructor simply assigns the given id.
+ */
+ J3dThread(ThreadGroup t) {
+ super(t, "");
+ }
+
+ /**
+ * This returns the thread data for this thread.
+ */
+ synchronized J3dThreadData getThreadData(View v, Canvas3D c) {
+ J3dThreadData threadData;
+ int i, j;
+ J3dThreadData[] newData;
+
+ if (type != RENDER_THREAD) { // Regular Thread
+ if (data == null) {
+ data = new J3dThreadData[1];
+ data[0] = new J3dThreadData();
+ data[0].thread = this;
+ data[0].threadType = type;
+ data[0].view = null;
+ data[0].canvas = null;
+ }
+ threadData = data[0];
+ } else { // Render thread
+
+ // Note: each renderer has multiple thread data mappings
+ // for its render and swap threads
+
+ if (data == null) {
+ data = new J3dThreadData[1];
+ data[0] = new J3dThreadData();
+ data[0].thread = this;
+ data[0].threadType = type;
+ data[0].view = v;
+ data[0].canvas = c;
+ data[0].threadArgs = new Object[4];
+ threadData = data[0];
+ } else {
+ for (i=0; i<data.length; i++) {
+ if (data[i].view == v && data[i].canvas == c) {
+ break;
+ }
+ }
+ if (i==data.length) {
+ newData = new J3dThreadData[data.length+1];
+ for (j=0; j<data.length; j++) {
+ newData[j] = data[j];
+ }
+ data = newData;
+ data[j] = new J3dThreadData();
+ data[j].thread = this;
+ data[j].threadType = type;
+ data[j].view = v;
+ data[j].canvas = c;
+ data[j].threadArgs = new Object[4];
+ threadData = data[j];
+ } else {
+ threadData = data[i];
+ Object args[] = (Object []) threadData.threadArgs;
+ args[0] = null;
+ args[1] = null;
+ args[2] = null;
+ args[3] = null;
+ }
+ }
+
+ }
+
+ return (threadData);
+ }
+
+ /**
+ * This initializes this thread. Once this method returns, the thread is
+ * ready to do work.
+ */
+ void initialize() {
+ this.start();
+ while (!started) {
+ MasterControl.threadYield();
+ }
+ }
+
+ /**
+ * This causes the threads run method to exit.
+ */
+ void finish() {
+ while (!waiting) {
+ MasterControl.threadYield();
+ }
+ runMonitor(STOP, 0,null);
+
+ }
+
+ /**
+ * This thread controls the syncing of all the canvases attached to
+ * this view.
+ */
+ public void run() {
+ runMonitor(WAIT, 0, null);
+ while (running) {
+ doWork(referenceTime);
+ runMonitor(NOTIFY_AND_WAIT, 0, null);
+ }
+ // resource clean up
+ shutdown();
+ }
+
+ synchronized void runMonitor(int action, long referenceTime,
+ Object[] args) {
+ switch (action) {
+ case WAIT:
+ try {
+ started = true;
+ waiting = true;
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ waiting = false;
+ break;
+ case NOTIFY_AND_WAIT:
+ VirtualUniverse.mc.runMonitor(MasterControl.THREAD_DONE, null,
+ null, null, this);
+ try {
+ waiting = true;
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ waiting = false;
+ break;
+ case RUN:
+ this.referenceTime = referenceTime;
+ this.args = args;
+ notify();
+ break;
+ case STOP:
+ running = false;
+ notify();
+ break;
+ }
+ }
+
+ void cleanupView() {
+ // renderer will reconstruct threadData next time
+ // in getThreadData
+ data = null;
+ }
+
+ // default resource clean up method
+ void shutdown() {
+ }
+
+ void cleanup() {
+ active = false;
+ running = true;
+ data = null;
+ started = true;
+ lastWaitTimestamp = 0;
+ classification = WORK_THREAD;
+ args = null;
+ userStop = false;
+ referenceTime = 0;
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/J3dThreadData.java b/src/classes/share/javax/media/j3d/J3dThreadData.java
new file mode 100644
index 0000000..59f03a6
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dThreadData.java
@@ -0,0 +1,92 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The J3dThreadData is the data wrapper for threads in Java 3D.
+ */
+
+class J3dThreadData extends Object {
+ /**
+ * Thread run options
+ */
+ static final int WAIT_ALL_THREADS = 0x01;
+ static final int CONT_THREAD = 0x02;
+ static final int WAIT_THIS_THREAD = 0x04;
+ static final int START_TIMER = 0x08;
+ static final int STOP_TIMER = 0x10;
+ static final int LAST_STOP_TIMER = 0x20;
+ //static final int LOCK_RENDERBIN = 0x20;
+ //static final int RELEASE_RENDERBIN = 0x40;
+
+ /**
+ * The thread for this data
+ */
+ J3dThread thread = null;
+
+ /**
+ * The last time that a message was sent to this thread.
+ */
+ long lastUpdateTime = -1;
+
+ /**
+ * The last time that this thread was run
+ */
+ long lastRunTime = -1;
+
+ /**
+ * The thread type
+ */
+ int threadType = 0;
+
+ /**
+ * The run options for this thread.
+ */
+ int threadOpts = 0;
+
+ /**
+ * The arguments to be passed to this thread
+ */
+ Object threadArgs = null;
+
+ /**
+ * This indicates whether or not this thread needs to run.
+ */
+ boolean needsRun = false;
+
+ /**
+ * The following data is only used by the Render Thread
+ */
+
+ /**
+ * The type of the thread invocation. RENDER or SWAP
+ */
+ int type = 0;
+
+ /**
+ * The view that this Render invocation belongs to.
+ */
+ View view = null;
+
+ /**
+ * The Canvas3D that this Render invocation belongs to.
+ * It is null for the SWAP invocation.
+ */
+ Canvas3D canvas = null;
+
+ /**
+ * This constructor does nothing
+ */
+ J3dThreadData() {
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/LOD.java b/src/classes/share/javax/media/j3d/LOD.java
new file mode 100644
index 0000000..e3d5305
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LOD.java
@@ -0,0 +1,220 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * An LOD leaf node is an abstract behavior class that operates on
+ * a list of Switch group nodes to select one of the children of the
+ * Switch nodes.
+ * The LOD class is extended to implement various selection criteria.
+ */
+
+public abstract class LOD extends Behavior {
+
+ /**
+ * Wakeup condition for all LOD nodes
+ */
+ WakeupOnElapsedFrames wakeupFrame = new WakeupOnElapsedFrames(0, true);
+
+
+ /**
+ * The LOD Node's vector of switch nodes.
+ */
+ Vector switches = new Vector(5);
+
+ /**
+ * Constructs and initializes an LOD node.
+ */
+ public LOD() {
+ }
+
+ /**
+ * Appends the specified switch node to this LOD's list of switches.
+ * @param switchNode the switch node to add to this LOD's list of switches
+ */
+ public void addSwitch(Switch switchNode) {
+ switches.addElement(switchNode);
+ }
+
+ /**
+ * Replaces the specified switch node with the switch node provided.
+ * @param switchNode the new switch node
+ * @param index which switch node to replace
+ */
+ public void setSwitch(Switch switchNode, int index) {
+ Switch sw = getSwitch(index);
+ switches.setElementAt(switchNode, index);
+ }
+
+ /**
+ * Inserts the specified switch node at specified index.
+ * @param switchNode the new switch node
+ * @param index position to insert new switch node at
+ */
+ public void insertSwitch(Switch switchNode, int index) {
+ switches.insertElementAt(switchNode, index);
+ }
+
+ /**
+ * Removes the switch node at specified index.
+ * @param index which switch node to remove
+ */
+ public void removeSwitch(int index) {
+ Switch sw = getSwitch(index);
+ switches.removeElementAt(index);
+ }
+
+ /**
+ * Returns the switch node specified by the index.
+ * @param index which switch node to return
+ * @return the switch node at location index
+ */
+ public Switch getSwitch(int index) {
+ return (Switch) switches.elementAt(index);
+ }
+
+ /**
+ * Returns the enumeration object of all switches.
+ * @return the enumeration object of all switches
+ */
+ public Enumeration getAllSwitches() {
+ return switches.elements();
+ }
+
+ /**
+ * Returns a count of this LOD's switches.
+ * @return the number of switches controlled by this LOD
+ */
+ public int numSwitches() {
+ return switches.size();
+ }
+
+
+ /**
+ * Retrieves the index of the specified switch node in
+ * this LOD node's list of switches.
+ *
+ * @param switchNode the switch node to be looked up.
+ * @return the index of the specified switch node;
+ * returns -1 if the object is not in the list.
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfSwitch(Switch switchNode) {
+ return switches.indexOf(switchNode);
+ }
+
+
+ /**
+ * Removes the specified switch node from this LOD node's
+ * list of switches.
+ * If the specified object is not in the list, the list is not modified.
+ *
+ * @param switchNode the switch node to be removed.
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeSwitch(Switch switchNode) {
+ int index = switches.indexOf(switchNode);
+ if (index >= 0)
+ removeSwitch(index);
+ }
+
+
+ /**
+ * Removes all switch nodes from this LOD node.
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllSwitches() {
+ int numSwitches = switches.size();
+
+ // Remove in reverse order to ensure valid indices
+ for (int index = numSwitches - 1; index >= 0; index--) {
+ removeSwitch(index);
+ }
+ }
+
+
+ /**
+ * Copies all LOD 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ LOD lod = (LOD) originalNode;
+
+ int numSwitch = lod.numSwitches();
+ for (int i = 0; i < numSwitch; i++) {
+ addSwitch(lod.getSwitch(i));
+ }
+ }
+
+ /**
+ * Callback used to allow a node to check if any nodes referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any node references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding Node in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * node is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ int numSwitch = numSwitches();
+
+ for (int i = 0; i < numSwitch; i++) {
+ Switch curSwitch = getSwitch(i);
+ if (curSwitch != null) {
+ setSwitch((Switch)
+ referenceTable.getNewObjectReference(curSwitch), i);
+ }
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Leaf.java b/src/classes/share/javax/media/j3d/Leaf.java
new file mode 100644
index 0000000..755c51c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Leaf.java
@@ -0,0 +1,34 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The Leaf node is an abstract class for all scene graph nodes that
+ * have no children.
+ * Leaf nodes specify lights, geometry, and sounds. They specify special
+ * linking and instancing capabilities for sharing scene graphs and
+ * provide a view platform for positioning and orienting a view in the
+ * virtual world.
+ * <p>
+ * NOTE: Applications should <i>not</i> extend this class directly.
+ */
+
+public abstract class Leaf extends Node {
+
+ /**
+ * Construct and initialize the Leaf object.
+ */
+ public Leaf(){
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/LeafRetained.java b/src/classes/share/javax/media/j3d/LeafRetained.java
new file mode 100644
index 0000000..42f00ef
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LeafRetained.java
@@ -0,0 +1,48 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.Hashtable;
+
+import java.util.ArrayList;
+
+/**
+ * LeafRetained node.
+ */
+abstract class LeafRetained extends NodeRetained {
+
+ SwitchState switchState = null;
+
+ // temporary variable used during bounds computation, since
+ // multiple mirror shapes could be pointing to the same shape3D
+ boolean boundsDirty = false;
+
+ // Appicable only to the mirror object
+ void updateBoundingLeaf() {
+
+ }
+ protected Object clone(boolean forceDuplicate) {
+ return super.clone();
+ }
+
+ void updateMirrorObject(Object[] args) {
+ }
+
+ void updateTransformChange() {
+ }
+
+ void updateBounds() {
+ }
+
+ void getMirrorObjects(ArrayList l, HashKey k) {
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Light.java b/src/classes/share/javax/media/j3d/Light.java
new file mode 100644
index 0000000..be5dee5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Light.java
@@ -0,0 +1,696 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+import java.util.Enumeration;
+
+/**
+ * The Light leaf node is an abstract class that defines a set of
+ * parameters common to all
+ * types of light. These parameters include the light color, an enable
+ * flag, and a region of influence in which this Light node is active.
+ * A Light node also contains a list of Group nodes that specifies the
+ * hierarchical scope of this Light. If the scope list is empty,
+ * the Light node has universe scope: all nodes within the region of
+ * influence are affected by this Light node. If the scope list is
+ * non-empty, only those Leaf nodes under the Group nodes in the
+ * scope list are affected by this Light node (subject to the
+ * influencing bounds).
+ * <p>
+ * The light in a scene may come from several light sources that can
+ * be individually defined. Some of the light in a scene may
+ * come from a specific direction, known as a directional light,
+ * from a specific position, known as a point light, or
+ * from no particular direction or source as with ambient light.
+ * <p>
+ * Java 3D supports an arbitrary number of lights. However, the number
+ * of lights that can be active within the region of influence is
+ * implementation-dependent and cannot be defined here.
+ * <p>
+ * <b>Light Color</b>
+ * <p>
+ * The Java 3D lighting model approximates the way light works in
+ * the real world. Light is defined in terms of the red, green, and
+ * blue components that combine to create the light color. The
+ * three color components represent the amount of light emitted
+ * by the source.
+ * <p>
+ * Each of the three colors is represented by a
+ * floating point value that ranges from 0.0 to 1.0. A combination
+ * of the three colors such as (1.0, 1.0, 1.0), representing
+ * the red, green, and blue color values respectively, creates a white
+ * light with maximum brightness. A combination such as (0.0, 0.0,
+ * 0.0) creates no light (black). Values between the minimum and
+ * maximum values of the range produce corresponding brightness
+ * and colors. For example, a combination of (0.5, 0.5, 0.5)
+ * produces a 50% grey light. A combination of (1.0, 1.0, 0.0),
+ * red and green but no blue, produces a yellow light.
+ * <p>
+ * If a scene has multiple lights and all lights illuminate an object,
+ * the effect of the light on the object is the sum of the
+ * lights. For example, in a scene with two lights, if the first
+ * light emits (R<sub>1</sub>, G<sub>1</sub>, B<sub>1</sub>) and
+ * the second light emits (R<sub>2</sub>, G<sub>2</sub>,
+ * B<sub>2</sub>), the components are added together giving
+ * (R<sub>1</sub>+R<sub>2</sub>, G<sub>1</sub>+G<sub>2</sub>,
+ * B<sub>1</sub>+B<sub>2</sub>).
+ * If the sums of any of the color values is greater than 1.0,
+ * brighter than the maximum brightness permitted, the color value is
+ * clamped to 1.0.
+ * <p>
+ * <b>Material Colors</b>
+ * <p>
+ * In the Java 3D lighting model, the light sources have an effect
+ * on the scene only when there are object surfaces to absorb or
+ * reflect the light. Java 3D approximates an object's color
+ * by calculating the percentage of red, green, and blue light
+ * the object reflects. An object with a surface color of pure green
+ * absorbs all of the red and blue light that strikes it and
+ * reflects all of the green light. Viewing the object in a
+ * white light, the green color is reflected and you see a green
+ * object. However, if the green object is viewed in a red light,
+ * all of the red light is absorbed and the object appears black.
+ * <p>
+ * The surface of each object in the scene has
+ * certain material properties that define how light affects its
+ * appearance. The object might reflect light in various ways,
+ * depending on the object's surface type. The object
+ * might even emit its own light. The Java 3D lighting model specifies
+ * these material properties as five independent components: emitted
+ * color, ambient color, diffuse color, specular color, and shininess.
+ * All of these properties are computed independently, then added
+ * together to define how the surface of the object appears under
+ * light (an exception is Ambient light, which does not contribute
+ * to specular reflection). The material properties are defined
+ * in the Material class.
+ * <p>
+ * <b>Influencing Bounds</b>
+ * <p>
+ * Since a scene may be quite large, as large as the universe for
+ * example, it is often reasonable to limit the influence of lighting
+ * to a region that is within viewing range. There is no reason
+ * to waste all that computing power on illuminating objects that
+ * are too far away to be viewed. In Java 3D, the influencing bounds
+ * is defined by a Bounds object or a BoundingLeaf object. It should
+ * be noted that a BoundingLeaf object overrides a Bounds object,
+ * should both objects be set.
+ * <p>
+ * A Bounds object represents a convex, closed volume. Bounds
+ * defines three different types of containing
+ * volumes: an axis-aligned-box volume, a spherical volume, and a
+ * bounding polytope. A BoundingLeaf object also specifies a region
+ * of influence, but allows an application to specify a bounding
+ * region in one coordinate system (the local coordinate system of
+ * the BoundingLeaf node) other than the local coordinate
+ * system of the node that references the bounds (the Light).
+ * <p>
+ * <b>Limiting the Scope</b>
+ * <p>
+ * In addition to limiting the lighting calculations to a given
+ * region of a scene, lighting can also be limited to groups of
+ * nodes, defined by a Group object. This is known as "scoping."
+ * All nodes attached to a Group node define a <i>list of scopes</i>.
+ * Methods in the Light class permit the setting, addition, insertion,
+ * removal, and enumeration of nodes in the list of scopes.
+ * <p>
+ * <b>Two-sided Lighting of Polygons</b>
+ * <p>
+ * Java 3D performs lighting calculations for all polygons, whether
+ * they are front-facing or back-facing. Since most polygon objects
+ * are constructed with the front face in mind, the back-facing
+ * portion may not be correctly illuminated. For example, a sphere
+ * with part of the face cut away so you can see its inside.
+ * You might want to have the inside surface lit as well as the
+ * outside surface and you mught also want to define a different
+ * Material description to reduce shininess, specular color, etc.
+ * <p>
+ * For more information, see the "Face culling" and "Back-facing
+ * normal flip" descriptions in the PolygonAttributes class
+ * description.
+ * <p>
+ * <b>Turning on the Lights</b>
+ * <p>
+ * Lighting needs to be explicitly enabled with the setEnable method
+ * or with the lightOn parameter in the constructor
+ * before any of the child light sources have any effect on illuminating
+ * the scene. The child lights may also be enabled or disabled individually.
+ * <p>
+ * If lighting is not enabled, the current color of an
+ * object in the scene is simply mapped onto the object, and none of
+ * the lighting equations regarding Material properties, such as ambient
+ * color, diffuse color, specular color, and shininess, are performed.
+ * However, an object's emitted color, if specified and enabled, will
+ * still affect that object's appearance.
+ * <p>
+ * To disable lighting, call setEnable with <code>false</code> as
+ * the argument.
+ *
+ * @see Material
+ * @see Bounds
+ * @see BoundingLeaf
+ * @see Group
+ * @see PolygonAttributes
+ */
+
+public abstract class Light extends Leaf {
+ /**
+ * Specifies that this Light allows read access to its current state
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_STATE_READ = CapabilityBits.LIGHT_ALLOW_STATE_READ;
+
+ /**
+ * Specifies that this Light allows write access to its current state
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_STATE_WRITE = CapabilityBits.LIGHT_ALLOW_STATE_WRITE;
+
+ /**
+ * Specifies that this Light allows read access to its color
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_COLOR_READ = CapabilityBits.LIGHT_ALLOW_COLOR_READ;
+
+ /**
+ * Specifies that this Light allows write access to its color
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_COLOR_WRITE = CapabilityBits.LIGHT_ALLOW_COLOR_WRITE;
+
+ /**
+ * Specifies that this Light allows read access to its
+ * influencing bounds and bounds leaf information.
+ */
+ public static final int
+ ALLOW_INFLUENCING_BOUNDS_READ = CapabilityBits.LIGHT_ALLOW_INFLUENCING_BOUNDS_READ;
+
+ /**
+ * Specifies that this Light allows write access to its
+ * influencing bounds and bounds leaf information.
+ */
+ public static final int
+ ALLOW_INFLUENCING_BOUNDS_WRITE = CapabilityBits.LIGHT_ALLOW_INFLUENCING_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this Light allows read access to its scope
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_SCOPE_READ = CapabilityBits.LIGHT_ALLOW_SCOPE_READ;
+
+ /**
+ * Specifies that this Light allows write access to its scope
+ * information at runtime.
+ */
+ public static final int
+ ALLOW_SCOPE_WRITE = CapabilityBits.LIGHT_ALLOW_SCOPE_WRITE;
+
+ /**
+ * Constructs a Light node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * enable flag : true<br>
+ * color : white (1,1,1)<br>
+ * scope : empty (universe scope)<br>
+ * influencing bounds : null<br>
+ * influencing bounding leaf : null<br>
+ * </ul>
+ */
+ public Light() {
+ }
+
+ /**
+ * Constructs and initializes a Light node using the specified color.
+ * @param color the color of the light source
+ */
+ public Light(Color3f color) {
+ ((LightRetained)this.retained).initColor(color);
+ }
+
+ /**
+ * Constructs and initializes a Light node using the specified enable
+ * flag and color.
+ * @param lightOn flag indicating whether this light is on or off
+ * @param color the color of the light source
+ */
+ public Light(boolean lightOn, Color3f color) {
+ ((LightRetained)this.retained).initEnable(lightOn);
+ ((LightRetained)this.retained).initColor(color);
+ }
+
+ /**
+ * Turns the light on or off.
+ * @param state true or false to set light on or off
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_STATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light0"));
+
+ if (isLive())
+ ((LightRetained)this.retained).setEnable(state);
+ else
+ ((LightRetained)this.retained).initEnable(state);
+ }
+
+ /**
+ * Retrieves this Light's current state (on/off).
+ * @return this node's current state (on/off)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_STATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light1"));
+
+ return ((LightRetained)this.retained).getEnable();
+ }
+
+ /**
+ * Sets the Light's current color.
+ * @param color the value of this node's new color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light2"));
+
+ if (isLive())
+ ((LightRetained)this.retained).setColor(color);
+ else
+ ((LightRetained)this.retained).initColor(color);
+ }
+
+ /**
+ * Gets this Light's current color and places it in the parameter specified.
+ * @param color the vector that will receive this node's color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light3"));
+
+ ((LightRetained)this.retained).getColor(color);
+ }
+
+ /**
+ * Replaces the node at the specified index in this Light node's
+ * list of scopes with the specified Group node.
+ * By default, Light nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be stored at the specified index.
+ * @param index the index of the Group node to be replaced.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void setScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light4"));
+
+ if (isLive())
+ ((LightRetained)this.retained).setScope(scope, index);
+ else
+ ((LightRetained)this.retained).initScope(scope, index);
+ }
+
+
+ /**
+ * Retrieves the Group node at the specified index from this Light node's
+ * list of scopes.
+ * @param index the index of the Group node to be returned.
+ * @return the Group node at the specified index.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Group getScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light5"));
+
+ return ((LightRetained)this.retained).getScope(index);
+ }
+
+
+ /**
+ * Inserts the specified Group node into this Light node's
+ * list of scopes at the specified index.
+ * By default, Light nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be inserted at the specified index.
+ * @param index the index at which the Group node is inserted.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void insertScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light6"));
+
+ if (isLive())
+ ((LightRetained)this.retained).insertScope(scope, index);
+ else
+ ((LightRetained)this.retained).initInsertScope(scope, index);
+ }
+
+
+ /**
+ * Removes the node at the specified index from this Light node's
+ * list of scopes. If this operation causes the list of scopes to
+ * become empty, then this Light will have universe scope: all nodes
+ * within the region of influence will be affected by this Light node.
+ * @param index the index of the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the group node at the
+ * specified index is part of a compiled scene graph
+ */
+ public void removeScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light7"));
+
+ if (isLive())
+ ((LightRetained)this.retained).removeScope(index);
+ else
+ ((LightRetained)this.retained).initRemoveScope(index);
+ }
+
+
+ /**
+ * Returns an enumeration of this Light node's list of scopes.
+ * @return an Enumeration object containing all nodes in this Light node's
+ * list of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Enumeration getAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light8"));
+
+ return (Enumeration) ((LightRetained)this.retained).getAllScopes();
+ }
+
+
+ /**
+ * Appends the specified Group node to this Light node's list of scopes.
+ * By default, Light nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be appended.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void addScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light9"));
+
+ if (isLive())
+ ((LightRetained)this.retained).addScope(scope);
+ else
+ ((LightRetained)this.retained).initAddScope(scope);
+ }
+
+
+ /**
+ * Returns the number of nodes in this Light node's list of scopes.
+ * If this number is 0, then the list of scopes is empty and this
+ * Light node has universe scope: all nodes within the region of
+ * influence are affected by this Light node.
+ * @return the number of nodes in this Light node's list of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int numScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light8"));
+
+ return ((LightRetained)this.retained).numScopes();
+ }
+
+
+ /**
+ * Retrieves the index of the specified Group node in this
+ * Light node's list of scopes.
+ *
+ * @param scope the Group node to be looked up.
+ * @return the index of the specified Group node;
+ * returns -1 if the object is not in the list.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light8"));
+
+ return ((LightRetained)this.retained).indexOfScope(scope);
+ }
+
+
+ /**
+ * Removes the specified Group node from this Light
+ * node's list of scopes. If the specified object is not in the
+ * list, the list is not modified. If this operation causes the
+ * list of scopes to become empty, then this Light
+ * will have universe scope: all nodes within the region of
+ * influence will be affected by this Light node.
+ *
+ * @param scope the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light7"));
+
+ if (isLive())
+ ((LightRetained)this.retained).removeScope(scope);
+ else
+ ((LightRetained)this.retained).initRemoveScope(scope);
+ }
+
+
+ /**
+ * Removes all Group nodes from this Light node's
+ * list of scopes. The Light node will then have
+ * universe scope: all nodes within the region of influence will
+ * be affected by this Light node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if any group node in this
+ * node's list of scopes is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light7"));
+
+ if (isLive())
+ ((LightRetained)this.retained).removeAllScopes();
+ else
+ ((LightRetained)this.retained).initRemoveAllScopes();
+ }
+
+
+ /**
+ * Sets the Light's influencing region to the specified bounds.
+ * This is used when the influencing bounding leaf is set to null.
+ * @param region the bounds that contains the Light's new influencing
+ * region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light11"));
+
+ if (isLive())
+ ((LightRetained)this.retained).setInfluencingBounds(region);
+ else
+ ((LightRetained)this.retained).initInfluencingBounds(region);
+ }
+
+ /**
+ * Retrieves the Light node's influencing bounds.
+ * @return this Light's influencing bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getInfluencingBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light12"));
+
+ return ((LightRetained)this.retained).getInfluencingBounds();
+ }
+
+ /**
+ * Sets the Light's influencing region to the specified bounding leaf.
+ * When set to a value other than null, this overrides the influencing
+ * bounds object.
+ * @param region the bounding leaf node used to specify the Light
+ * node's new influencing region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light11"));
+
+ if (isLive())
+ ((LightRetained)this.retained).setInfluencingBoundingLeaf(region);
+ else
+ ((LightRetained)this.retained).initInfluencingBoundingLeaf(region);
+ }
+
+ /**
+ * Retrieves the Light node's influencing bounding leaf.
+ * @return this Light's influencing bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getInfluencingBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Light12"));
+
+ return ((LightRetained)this.retained).getInfluencingBoundingLeaf();
+ }
+
+
+
+ /**
+ * Copies all Light 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ LightRetained attr = (LightRetained) originalNode.retained;
+ LightRetained rt = (LightRetained) retained;
+
+ Color3f c = new Color3f();
+ attr.getColor(c);
+ rt.initColor(c);
+ rt.initInfluencingBounds(attr.getInfluencingBounds());
+
+ Enumeration elm = attr.getAllScopes();
+ while (elm.hasMoreElements()) {
+ // this reference will set correctly in updateNodeReferences() callback
+ rt.initAddScope((Group) elm.nextElement());
+ }
+
+ // this reference will set correctly in updateNodeReferences() callback
+ rt.initInfluencingBoundingLeaf(attr.getInfluencingBoundingLeaf());
+
+ rt.initEnable(attr.getEnable());
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+
+
+ LightRetained rt = (LightRetained) retained;
+ BoundingLeaf bl = rt.getInfluencingBoundingLeaf();
+
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.initInfluencingBoundingLeaf((BoundingLeaf)o);
+ }
+
+ int num = rt.numScopes();
+ for (int i=0; i < num; i++) {
+ rt.initScope((Group) referenceTable.
+ getNewObjectReference(rt.getScope(i)), i);
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/LightBin.java b/src/classes/share/javax/media/j3d/LightBin.java
new file mode 100644
index 0000000..28aeb08
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LightBin.java
@@ -0,0 +1,445 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+/**
+ * The LightBin manages a collection of EnvironmentSet objects.
+ * The number of objects managed depends upon the number of Lights
+ * in each EnvironmentSet and the number of lights supported by
+ * the underlying rendering layer.
+ */
+
+class LightBin extends Object implements ObjectUpdate {
+
+ /**
+ * The maximum number of lights in a LightBin
+ */
+ int maxLights = -1;
+
+ /**
+ * The Array of Light references in this LightBin.
+ * This array is always maxLights in length.
+ */
+ LightRetained[] lights = null;
+
+ /**
+ * An Array of reference counts for shared lights in
+ * among EnvirionmentSets
+ */
+ int[] lightsRef = null;
+
+ /**
+ * The number of empty light slots in this LightBin
+ */
+ int numEmptySlots = -1;
+
+ /**
+ * The RenderBin for this object
+ */
+ RenderBin renderBin = null;
+
+ /**
+ * The references to the next and previous LightBins in the
+ * list.
+ */
+ LightBin next = null;
+ LightBin prev = null;
+
+ /**
+ * The list of EnvironmentSets in this LightBin.
+ */
+ EnvironmentSet environmentSetList = null;
+
+ /**
+ * List of envSet to be added for the next iteration
+ */
+ ArrayList insertEnvSet = new ArrayList();
+
+
+
+ /**
+ * cache of the canvasDirty
+ */
+ int canvasDirty = 0;
+
+ /**
+ * lightDirty Mask cache , used to
+ * mark the lightdirty bits for next frame
+ */
+ int lightDirtyMaskCache = 0;
+
+
+ /**
+ * lightDirty Mask used during rendering
+ */
+ int lightDirtyMask = 0;
+
+ /**
+ * List of pointLts in this lightbin
+ * Need to reload these lights when vworld scale changes
+ */
+ ArrayList pointLts = new ArrayList();
+ int[] pointLtsSlotIndex;
+
+ // OrderedGroup info
+ OrderedCollection orderedCollection = null;
+
+ boolean onUpdateList = false;
+
+ // background node that contains geometry
+ BackgroundRetained geometryBackground = null;
+
+
+
+ LightBin(int maxLights, RenderBin rb, boolean isOpaque) {
+ this.maxLights = maxLights;
+ this.numEmptySlots = maxLights;
+ lights = new LightRetained[maxLights];
+ lightsRef = new int[maxLights];
+ renderBin = rb;
+ }
+
+ void reset(boolean inOpaque) {
+ prev = null;
+ next = null;
+ orderedCollection = null;
+ environmentSetList = null;
+ onUpdateList = false;
+ geometryBackground = null;
+ // No need to reset the lights and lightRef
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ for (int i=0; i<maxLights; i++) {
+ J3dDebug.doAssert(lights[i] == null, "lights[i] == null");
+ J3dDebug.doAssert(lightsRef[i] == 0, "lightsRef[i] == 0");
+ }
+ }
+ }
+
+ void setOrderedInfo(OrderedCollection oc) {
+ orderedCollection = oc;
+ }
+
+ /**
+ * Checks to see if an EnvironmentSet will fit into
+ * this LightBin. It takes into account shared lights.
+ */
+ boolean willEnvironmentSetFit(EnvironmentSet e) {
+ int i, j, numEsLights, slotsNeeded;
+ LightRetained light;
+
+ numEsLights = e.lights.size();
+ slotsNeeded = numEsLights;
+ for (i=0; i<numEsLights; i++) {
+ light = (LightRetained) e.lights.get(i);
+ if (light instanceof AmbientLightRetained) {
+ continue;
+ }
+ for (j=0; j<maxLights; j++) {
+ if (lights[j] == light) {
+ slotsNeeded--;
+ break;
+ }
+ }
+ }
+ if (slotsNeeded > numEmptySlots) {
+ return (false);
+ } else {
+ return (true);
+ }
+ }
+
+ /**
+ * Adds the new EnvironmentSet to this LightBin.
+ */
+ void addEnvironmentSet(EnvironmentSet e, RenderBin rb) {
+ int i, j, numEsLights;
+ LightRetained light;
+
+ numEsLights = e.lights.size();
+ for (i=0; i<numEsLights; i++) {
+ light = (LightRetained) e.lights.get(i);
+ if (light instanceof AmbientLightRetained) {
+ continue;
+ }
+ for (j=0; j<maxLights; j++) {
+ if (lights[j] == light) {
+ if (light.lightOn) {
+ e.enableMask |= 1<<j;
+ }
+ lightsRef[j]++;
+ // Keep a reference to the position of the light
+ // in the light bin that this light in the envSet
+ // refers
+ e.ltPos[i] = j;
+ break;
+ }
+ }
+ if (j==maxLights) {
+ // Find an empty slot
+ for (j=0; j<maxLights; j++) {
+ if (lights[j] == null) {
+ lights[j] = light;
+ lightsRef[j] = 1;
+ if (light instanceof PointLightRetained) {
+ pointLts.add(light);
+
+ // save the destinated light slot for point
+ // so that point light can be updated without
+ // referencing the lights list
+ int pointLtsSlotIndexLen = 0;
+ if (pointLtsSlotIndex != null)
+ pointLtsSlotIndexLen = pointLtsSlotIndex.length;
+ if (pointLtsSlotIndexLen < pointLts.size()) {
+
+ int[] newIndexList =
+ new int[pointLtsSlotIndexLen + 8];
+ for (int x = 0; x < pointLtsSlotIndexLen; x++) {
+ newIndexList[x] = pointLtsSlotIndex[x];
+ }
+ pointLtsSlotIndex = newIndexList;
+ }
+ pointLtsSlotIndex[pointLts.size() - 1] = j;
+ }
+ if (light.lightOn) {
+ e.enableMask |= 1<<j;
+ }
+ // Keep a reference to the position of the light
+ // in the light bin that this light in the envSet
+ // refers
+ e.ltPos[i] = j;
+ numEmptySlots--;
+ break;
+ }
+ }
+ }
+ }
+ e.lightBin = this;
+ e.enableMaskCache = e.enableMask;
+ insertEnvSet.add(e);
+ if (!onUpdateList) {
+ rb.objUpdateList.add(this);
+ onUpdateList = true;
+ }
+
+ }
+
+ public void updateObject() {
+ int i, j;
+ EnvironmentSet e ;
+
+
+ // Handle insertion
+ if (insertEnvSet.size() > 0) {
+ e = (EnvironmentSet)insertEnvSet.get(0);
+ if (environmentSetList == null) {
+ environmentSetList = e;
+ }
+ else {
+ e.next = environmentSetList;
+ environmentSetList.prev = e;
+ environmentSetList = e;
+ }
+ for (i = 1; i < insertEnvSet.size(); i++) {
+ e = (EnvironmentSet)insertEnvSet.get(i);
+ e.next = environmentSetList;
+ environmentSetList.prev = e;
+ environmentSetList = e;
+ }
+ }
+
+
+ insertEnvSet.clear();
+ if (canvasDirty != 0) {
+
+ Canvas3D canvases[] = renderBin.view.getCanvases();
+ for (i = 0; i < canvases.length; i++) {
+ canvases[i].canvasDirty |= canvasDirty;
+ }
+ lightDirtyMask = lightDirtyMaskCache;
+ canvasDirty = 0;
+ lightDirtyMaskCache = 0;
+ }
+ onUpdateList = false;
+ }
+
+
+
+ /**
+ * Removes the given EnvironmentSet from this LightBin.
+ */
+ void removeEnvironmentSet(EnvironmentSet e) {
+ int i, j, numEsLights;
+ LightRetained light;
+
+ e.lightBin = null;
+ // If envSet being remove is contained in envSet, then
+ // remove the envSet from the addList
+ if (insertEnvSet.contains(e)) {
+ insertEnvSet.remove(insertEnvSet.indexOf(e));
+ }
+ else {
+ numEsLights = e.lights.size();
+ for (i=0; i<numEsLights; i++) {
+ light = (LightRetained) e.lights.get(i);
+ for (j=0; j<maxLights; j++) {
+ if (lights[j] == light) {
+ lightsRef[j]--;
+ if (lightsRef[j] == 0) {
+ if (light instanceof PointLightRetained)
+ pointLts.remove(pointLts.indexOf(light));
+ lights[j] = null;
+ // If the lightBin is dirty unset the mask
+ lightDirtyMaskCache &= ~(1 << j);
+ // since the canvas may already be updated,
+ lightDirtyMask &= ~(1 << j);
+ numEmptySlots++;
+ }
+ break;
+ }
+ }
+ }
+
+ if (e.prev == null) { // At the head of the list
+ environmentSetList = e.next;
+ if (e.next != null) {
+ e.next.prev = null;
+ }
+ } else { // In the middle or at the end.
+ e.prev.next = e.next;
+ if (e.next != null) {
+ e.next.prev = e.prev;
+ }
+ }
+
+ // Mark all canvases that uses this environment set as
+ Canvas3D canvases[] = renderBin.view.getCanvases();
+ for (i = 0; i < canvases.length; i++) {
+ // Mark the environmentSet cached by all the canvases as null
+ // to force to reEvaluate when it comes back from the freelist
+ // During envset::render(), we only check for the pointers not
+ // being the same, so we need to take care of the env set
+ // gotten from the freelist from one frame to another
+ canvases[i].environmentSet = null;
+ }
+
+ }
+ e.prev = null;
+ e.next = null;
+ renderBin.envSetFreelist.add(e);
+
+ if (environmentSetList == null && insertEnvSet.size() == 0) {
+ renderBin.removeLightBin(this);
+ geometryBackground = null;
+ }
+
+ }
+
+ /**
+ * Renders this LightBin
+ */
+ void render(Canvas3D cv) {
+ EnvironmentSet e;
+
+ // include this LightBin to the to-be-updated list in Canvas
+ cv.setStateToUpdate(Canvas3D.LIGHTBIN_BIT, this);
+
+ e = environmentSetList;
+ while (e != null) {
+ e.render(cv);
+ e = e.next;
+ }
+ }
+
+ void updateAttributes(Canvas3D cv) {
+ int i;
+ double scale;
+
+ int frameCount = VirtualUniverse.mc.frameCount;
+
+ // within frames
+ if (cv.lightBin != this) {
+
+ if (geometryBackground == null) {
+ scale = cv.canvasViewCache.getVworldToCoexistenceScale();
+ cv.setModelViewMatrix(cv.ctx, cv.vpcToEc.mat,
+ renderBin.vworldToVpc);
+ } else {
+ scale = cv.canvasViewCache.getInfVworldToCoexistenceScale();
+ cv.setModelViewMatrix(cv.ctx, cv.vpcToEc.mat,
+ renderBin.infVworldToVpc);
+ }
+
+
+ for (i=0; i<maxLights; i++) {
+ if (lights[i] != null) {
+ if (cv.lights[i] != lights[i] ||
+ cv.frameCount[i] != frameCount) {
+ cv.lights[i] = lights[i];
+ cv.frameCount[i] = frameCount;
+ lights[i].update(cv.ctx, i,scale);
+ }
+ }
+ }
+ cv.lightBin = this;
+ cv.canvasDirty &= ~Canvas3D.LIGHTBIN_DIRTY;
+ // invalidate canvas cached enableMask
+ cv.enableMask = -1;
+ }
+ // across frames
+ else if ((cv.canvasDirty & Canvas3D.LIGHTBIN_DIRTY) != 0) {
+ // Just update the dirty lights
+ if (geometryBackground == null) {
+ scale = cv.canvasViewCache.getVworldToCoexistenceScale();
+ cv.setModelViewMatrix(cv.ctx, cv.vpcToEc.mat,
+ renderBin.vworldToVpc);
+ } else {
+ scale = cv.canvasViewCache.getInfVworldToCoexistenceScale();
+ cv.setModelViewMatrix(cv.ctx, cv.vpcToEc.mat,
+ renderBin.infVworldToVpc);
+ }
+ i = 0;
+ int mask = lightDirtyMask;
+ while (mask != 0) {
+ if ((mask & 1) != 0) {
+ lights[i].update(cv.ctx, i, scale);
+ cv.lights[i] = lights[i];
+ cv.frameCount[i] = frameCount;
+ }
+ mask >>= 1;
+ i++;
+ }
+
+ cv.canvasDirty &= ~Canvas3D.LIGHTBIN_DIRTY;
+ }
+ else if ((pointLts.size() > 0) && ((cv.canvasDirty & Canvas3D.VWORLD_SCALE_DIRTY) != 0 )) {
+ if (geometryBackground == null) {
+ scale = cv.canvasViewCache.getVworldToCoexistenceScale();
+ cv.setModelViewMatrix(cv.ctx, cv.vpcToEc.mat,
+ renderBin.vworldToVpc);
+ } else {
+ scale = cv.canvasViewCache.getInfVworldToCoexistenceScale();
+ cv.setModelViewMatrix(cv.ctx, cv.vpcToEc.mat,
+ renderBin.infVworldToVpc);
+ }
+ for (i = 0; i < pointLts.size(); i++) {
+ LightRetained lt = (LightRetained) pointLts.get(i);
+ lt.update(cv.ctx, pointLtsSlotIndex[i], scale);
+ cv.lights[pointLtsSlotIndex[i]] = lt;
+ cv.frameCount[pointLtsSlotIndex[i]] = frameCount;
+ }
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/LightRetained.java b/src/classes/share/javax/media/j3d/LightRetained.java
new file mode 100644
index 0000000..0132e9d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LightRetained.java
@@ -0,0 +1,1062 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.ArrayList;
+
+/**
+ * LightRetained is an abstract class that contains instance variable common to
+ * all lights.
+ */
+
+abstract class LightRetained extends LeafRetained {
+
+ // Statics used when something in the light changes
+ static final int ENABLE_CHANGED = 0x0001;
+ static final int SCOPE_CHANGED = 0x0002;
+ static final int BOUNDS_CHANGED = 0x0004;
+ static final int COLOR_CHANGED = 0x0008;
+ static final int BOUNDINGLEAF_CHANGED = 0x0010;
+ static final int INIT_MIRROR = 0x0020;
+ static final int CLEAR_MIRROR = 0x0040;
+ static final int LAST_DEFINED_BIT = 0x0040;
+
+ // Indicates whether the light is turned on.
+ boolean lightOn = true;
+
+ // The color of the light (white by default).
+ Color3f color = new Color3f(1.0f, 1.0f, 1.0f);
+
+ // This node which specifies the hierarchical scope of the
+ // light. A null reference means that this light has universal
+ // scope.
+ Vector scopes = new Vector();
+
+ /**
+ * The Boundary object defining the lights's region of influence.
+ */
+ Bounds regionOfInfluence = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * The transformed value of the applicationRegion.
+ */
+ Bounds region = null;
+
+ /**
+ * This bitmask is set when something changes in the light
+ */
+ int lightDirty = 0xffff;
+
+ // This is a copy of the sgLight's dirty bits
+ int sgLightDirty = 0xffff;
+
+ // The type of light
+ int lightType = -1;
+
+ // This is true when this light is needed in the current light set
+ boolean isNeeded = false;
+
+ // This is true when this light is referenced in an immediate mode context
+ boolean inImmCtx = false;
+
+ // A back reference to the scene graph light, when this is a mirror light
+ LightRetained sgLight = null;
+
+ // A HashKey for lights in a shared group
+ HashKey key = null;
+
+ // An array of mirror lights, one for each instance of this light in a
+ // shared group. Entry 0 is the only one valid if we are not in a shared
+ // group.
+ LightRetained[] mirrorLights = new LightRetained[1];
+
+ // The number of valid lights in mirrorLights
+ int numMirrorLights = 0;
+
+ // Indicated whether the light is a scoped light
+ boolean isScoped = false;
+
+ // The object that contains the dynamic HashKey - a string type object
+ // Used in scoping
+ HashKey tempKey = new HashKey(250);
+
+ /**
+ * A list of all the EnvironmentSets that reference this light.
+ * Note that multiple RenderBin update thread may access
+ * this shared environmentSets simultaneously.
+ * So we use UnorderList when sync. all the operations.
+ */
+ UnorderList environmentSets = new UnorderList(1, EnvironmentSet.class);
+
+ // Is true, if the mirror light is viewScoped
+ boolean isViewScoped = false;
+
+
+ /**
+ * Temporary list of newly added mirror lights, during any setlive
+ */
+ ArrayList newlyAddedMirrorLights = new ArrayList();
+
+ // Target threads to be notified when light changes
+ static final int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+ /**
+ * Initialize the light on or off.
+ * @param state true or false to enable or disable the light
+ */
+ void initEnable(boolean state) {
+ this.lightOn = state;
+ }
+
+ /**
+ * Turns the light on or off and send a message
+ * @param state true or false to enable or disable the light
+ */
+ void setEnable(boolean state) {
+ initEnable(state);
+ sendMessage(ENABLE_CHANGED,
+ (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+
+ /**
+ * Returns the state of the light (on/off).
+ * @return true if the light is on, false if the light is off.
+ */
+ boolean getEnable() {
+ return this.lightOn;
+ }
+
+ /**
+ * Initialize the color of this light node.
+ * @param color the value of this new light color
+ */
+ void initColor(Color3f color) {
+ this.color.set(color);
+ }
+
+ /**
+ * Sets the color of this light node and send a message
+ * @param color the value of this new light color
+ */
+ void setColor(Color3f color) {
+ initColor(color);
+ sendMessage(COLOR_CHANGED, new Color3f(color));
+ }
+
+ /**
+ * Retrieves the color of this light.
+ * @param color the vector that will receive the color of this light
+ */
+ void getColor(Color3f color) {
+ color.set(this.color);
+ }
+
+ /**
+ * Initializes the specified scope with the scope provided.
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void initScope(Group scope, int index) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ scopes.setElementAt(group, index);
+
+ }
+
+
+ /**
+ * Replaces the specified scope with the scope provided and
+ * send a message
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void setScope(Group scope, int index) {
+ ArrayList addScopeList = new ArrayList();
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group;
+ Object[] scopeInfo = new Object[3];
+
+ group = (GroupRetained) scopes.get(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights, removeScopeList, tempKey);
+
+
+ group = (GroupRetained)scope.retained;
+ tempKey.reset();
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ group.addAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights,addScopeList, tempKey);
+
+
+ initScope(scope, index);
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ /**
+ * Inserts the specified scope at specified index.
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void initInsertScope(Group scope, int index) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ scopes.insertElementAt(group, index);
+ group.setLightScope();
+ }
+
+ /**
+ * Inserts the specified scope at specified index.
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void insertScope(Group scope, int index) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scope.retained;
+
+ tempKey.reset();
+ group.addAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights,addScopeList, tempKey);
+
+ initInsertScope(scope, index);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+
+ /**
+ * Removes the scope at specified index.
+ * @param index which scope to remove
+ */
+ void initRemoveScope(int index) {
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+ scopes.removeElementAt(index);
+ group.removeLightScope();
+ }
+
+
+ /**
+ * Removes the scope at specified index.
+ * @param index which scope to remove
+ */
+ void removeScope(int index) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList removeScopeList = new ArrayList();
+
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights, removeScopeList, tempKey);
+ initRemoveScope(index); scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+
+ /**
+ * Removes the specified scope
+ * @param scope to be removed
+ */
+ void removeScope(Group scope) {
+ int ind = indexOfScope(scope);
+ if(ind >= 0)
+ removeScope(ind);
+ }
+
+ void initRemoveScope(Group scope) {
+ int ind = indexOfScope(scope);
+ if(ind >= 0)
+ initRemoveScope(ind);
+ }
+
+ /**
+ * Removes all the scopes from this Light's list of scopes
+ */
+ void removeAllScopes() {
+ int n = scopes.size();
+ Object[] scopeInfo = new Object[3];
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group;
+
+ for(int index = n-1; index >= 0; index--) {
+ group = (GroupRetained)scopes.elementAt(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights, removeScopeList, tempKey);
+ initRemoveScope(index);
+ }
+ scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ void initRemoveAllScopes() {
+ int n = scopes.size();
+ for(int i = n-1; i >= 0; i--)
+ initRemoveScope(i);
+ }
+
+ /**
+ * Returns the scope specified by the index.
+ * @param index of the scope to be returned
+ * @return the scope at location index
+ */
+ Group getScope(int index) {
+ return (Group)(((GroupRetained)(scopes.elementAt(index))).source);
+ }
+
+ /**
+ * Returns an enumeration object of the scope
+ * @return an enumeration object of the scope
+ */
+ Enumeration getAllScopes() {
+ Enumeration elm = scopes.elements();
+ Vector v = new Vector(scopes.size());
+ while (elm.hasMoreElements()) {
+ v.add( ((GroupRetained) elm.nextElement()).source);
+ }
+ return v.elements();
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes.
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void initAddScope(Group scope) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ scopes.addElement(group);
+ group.setLightScope();
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes.
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void addScope(Group scope) {
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scope.retained;
+
+ initAddScope(scope);
+ tempKey.reset();
+ group.addAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo);
+ }
+
+ /**
+ * Returns a count of this nodes' scopes.
+ * @return the number of scopes descendant from this node
+ */
+ int numScopes() {
+ return scopes.size();
+ }
+
+ /**
+ * Returns the index of the specified scope
+ * @return index of the scope in this Light's list of scopes
+ */
+ int indexOfScope(Group scope) {
+ if(scope != null)
+ return scopes.indexOf((GroupRetained)scope.retained);
+ else
+ return scopes.indexOf(null);
+ }
+
+ /**
+ * Initializes the Light's region of influence.
+ * @param region a region that contains the Light's new region of influence
+ */
+ void initInfluencingBounds(Bounds region) {
+ if (region != null) {
+ regionOfInfluence = (Bounds) region.clone();
+ if (staticTransform != null) {
+ regionOfInfluence.transform(staticTransform.transform);
+ }
+ } else {
+ regionOfInfluence = null;
+ }
+ }
+
+
+ /**
+ * Set the Light's region of influence and send a message
+ * @param region a region that contains the Light's new region of influence
+ */
+ void setInfluencingBounds(Bounds region) {
+ initInfluencingBounds(region);
+ sendMessage(BOUNDS_CHANGED,
+ (region != null ? region.clone() : null));
+ }
+
+ /**
+ * Get the Light's region of influence
+ * @return this Light's region of influence information
+ */
+ Bounds getInfluencingBounds() {
+ Bounds b = null;
+
+ if (regionOfInfluence != null) {
+ b = (Bounds) regionOfInfluence.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Initializes the Light's region of influence to the specified Leaf node.
+ */
+ void initInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ } else {
+ boundingLeaf = null;
+ }
+ }
+
+ /**
+ * Set the Light's region of influence to the specified Leaf node.
+ */
+ void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ int i, numLgts;
+
+ numLgts = numMirrorLights;
+ if (numMirrorLights == 0)
+ numLgts = 1;
+
+ if (boundingLeaf != null) {
+ // Remove the mirror lights as users of the original bounding leaf
+ for (i = 0; i < numLgts; i++) {
+ boundingLeaf.mirrorBoundingLeaf.removeUser(mirrorLights[i]);
+ }
+ }
+
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ // Add all mirror lights as user of this bounding leaf
+ for (i = 0; i < numLgts; i++) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorLights[i]);
+ }
+ } else {
+ boundingLeaf = null;
+ }
+
+ sendMessage(BOUNDINGLEAF_CHANGED,
+ (boundingLeaf != null ?
+ boundingLeaf.mirrorBoundingLeaf : null));
+ }
+
+ /**
+ * Get the Light's region of influence.
+ */
+ BoundingLeaf getInfluencingBoundingLeaf() {
+ return (boundingLeaf != null ?
+ (BoundingLeaf)boundingLeaf.source : null);
+ }
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+
+ // Called on the parent Light object and loops over the mirror object
+ void initMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ Object[] scopeInfo = (Object[])((Object[])args[4])[5];
+ ArrayList gAtomList = (ArrayList)scopeInfo[1];
+ Boolean scoped = (Boolean)scopeInfo[0];
+ BoundingLeafRetained bl=(BoundingLeafRetained)((Object[])args[4])[0];
+ Bounds bnds = (Bounds)((Object[])args[4])[1];
+ int numLgts = ((Integer)args[2]).intValue();
+ LightRetained[] mLgts = (LightRetained[]) args[3];
+ int k;
+
+ for ( k = 0; k < numLgts; k++) {
+ for (int i = 0; i < gAtomList.size(); i++) {
+ shape = ((GeometryAtom)gAtomList.get(i)).source;
+ shape.addLight(mLgts[k]);
+ }
+ mLgts[k].isScoped = scoped.booleanValue();
+ }
+
+ for (k = 0; k < numLgts; k++) {
+ mLgts[k].inBackgroundGroup = ((Boolean)((Object[])args[4])[2]).booleanValue();
+ mLgts[k].geometryBackground = (BackgroundRetained)((Object[])args[4])[3];
+
+
+ if (bl != null) {
+ mLgts[k].boundingLeaf = bl.mirrorBoundingLeaf;
+ mLgts[k].region = mLgts[k].boundingLeaf.transformedRegion;
+ } else {
+ mLgts[k].boundingLeaf = null;
+ mLgts[k].region = null;
+ }
+
+ if (bnds != null) {
+ mLgts[k].regionOfInfluence = bnds;
+ if (mLgts[k].region == null) {
+ mLgts[k].region = (Bounds)regionOfInfluence.clone();
+ mLgts[k].region.transform(regionOfInfluence, getLastLocalToVworld());
+ }
+ }
+ else {
+ mLgts[k].regionOfInfluence = null;
+ }
+ mLgts[k].lightOn = ((Boolean)((Object[])args[4])[4]).booleanValue();
+
+ }
+ // if its a ambient light,then do a immediate update of color
+ if (this instanceof AmbientLightRetained) {
+ Color3f clr = (Color3f) ((Object[])args[4])[6];
+ for (int i = 0; i < numLgts; i++) {
+ mLgts[i].color.set(clr);
+ }
+ }
+
+ }
+
+ /**
+ * This method is implemented by each light for rendering
+ * context updates. This default one does nothing.
+ */
+ abstract void update(long ctx, int lightSlot, double scale);
+
+
+ // This routine is called when rendering Env structure
+ // get a message, this routine updates values in the mirror object
+ // that are not used by the renderer
+ void updateImmediateMirrorObject(Object[] objs) {
+ Transform3D trans = null;
+ int component = ((Integer)objs[1]).intValue();
+ int numLgts = ((Integer)objs[2]).intValue();
+ LightRetained[] mLgts = (LightRetained[]) objs[3];
+
+ // Color changed called immediately only for ambient lights
+ if ((component & COLOR_CHANGED) != 0) {
+ for (int i = 0; i < numLgts; i++) {
+ mLgts[i].color.set(((Color3f)objs[4]));
+ }
+ }
+ else if ((component & ENABLE_CHANGED) != 0) {
+ for (int i = 0; i < numLgts; i++)
+ mLgts[i].lightOn = ((Boolean)objs[4]).booleanValue();
+ }
+ else if ((component & BOUNDS_CHANGED) != 0) {
+ for (int i = 0; i < numLgts; i++) {
+ mLgts[i].regionOfInfluence = (Bounds) objs[4];
+ if (mLgts[i].boundingLeaf == null) {
+ if (objs[4] != null) {
+ mLgts[i].region =
+ ((Bounds)mLgts[i].regionOfInfluence).copy(mLgts[i].region);
+ mLgts[i].region.transform(mLgts[i].regionOfInfluence,
+ mLgts[i].getCurrentLocalToVworld());
+ }
+ else {
+ mLgts[i].region = null;
+ }
+ }
+ }
+ }
+ else if ((component & BOUNDINGLEAF_CHANGED) != 0) {
+ for (int i = 0; i < numLgts; i++) {
+ mLgts[i].boundingLeaf=((BoundingLeafRetained)objs[4]);
+ if (objs[4] != null) {
+ mLgts[i].region = (Bounds)mLgts[i].boundingLeaf.transformedRegion;
+ }
+ else { // evaluate regionOfInfluence if not null
+ if (mLgts[i].regionOfInfluence != null) {
+ mLgts[i].region =
+ ((Bounds)mLgts[i].regionOfInfluence).copy(mLgts[i].region);
+ mLgts[i].region.transform(mLgts[i].regionOfInfluence,
+ mLgts[i].getCurrentLocalToVworld());
+ }
+ else {
+ mLgts[i].region = null;
+ }
+ }
+ }
+ }
+ else if ((component & SCOPE_CHANGED) != 0) {
+ int nscopes, j, i;
+ GroupRetained group;
+ Vector currentScopes;
+ Object[] scopeList = (Object[])objs[4];
+ ArrayList addList = (ArrayList)scopeList[0];
+ ArrayList removeList = (ArrayList)scopeList[1];
+ boolean isScoped = ((Boolean)scopeList[2]).booleanValue();
+
+ if (addList != null) {
+ for (i = 0; i < numLgts; i++) {
+ mLgts[i].isScoped = isScoped;
+ for (j = 0; j < addList.size(); j++) {
+ Shape3DRetained obj = ((GeometryAtom)addList.get(j)).source;
+ obj.addLight(mLgts[i]);
+ }
+ }
+ }
+
+ if (removeList != null) {
+ for (i = 0; i < numLgts; i++) {
+ mLgts[i].isScoped = isScoped;
+ for (j = 0; j < removeList.size(); j++) {
+ Shape3DRetained obj = ((GeometryAtom)removeList.get(j)).source;
+ ((Shape3DRetained)obj).removeLight(mLgts[i]);
+ }
+ }
+ }
+ }
+
+
+ }
+
+
+
+ // The update Object function called during RenderingEnv objUpdate
+ // Note : if you add any more fields here , you need to update
+ // updateLight() in RenderingEnvironmentStructure
+ void updateMirrorObject(Object[] objs) {
+
+ Transform3D trans = null;
+ int component = ((Integer)objs[1]).intValue();
+ int numLgts = ((Integer)objs[2]).intValue();
+ LightRetained[] mLgts = (LightRetained[]) objs[3];
+
+ if ((component & COLOR_CHANGED) != 0) {
+ for (int i = 0; i < numLgts; i++) {
+ mLgts[i].color.set(((Color3f)objs[4]));
+ }
+ }
+
+ if ((component & INIT_MIRROR) != 0) {
+ for (int i = 0; i < numLgts; i++) {
+ Color3f clr = (Color3f) ((Object[])objs[4])[6];
+ mLgts[i].color.set(clr);
+ }
+ }
+ }
+
+ /** Note: This routine will only be called on
+ * the mirror object - will update the object's
+ * cached region and transformed region
+ */
+
+ void updateBoundingLeaf() {
+ // This is necessary, if for example, the region
+ // changes from sphere to box.
+ if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) {
+ region = boundingLeaf.transformedRegion;
+ } else { // evaluate regionOfInfluence if not null
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence, getCurrentLocalToVworld());
+ } else {
+ region = null;
+ }
+ }
+ }
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ if (!inSharedGroup) {
+ leafList.add(mirrorLights[0]);
+ }
+ else {
+ for (int i=0; i<numMirrorLights; i++) {
+ if (mirrorLights[i].key.equals(key)) {
+ leafList.add(mirrorLights[i]);
+ break;
+ }
+ }
+
+ }
+ }
+ /**
+ * This gets the mirror light for this light given the key.
+ */
+ LightRetained getMirrorLight(HashKey key) {
+ int i;
+ LightRetained[] newLights;
+
+ if (inSharedGroup) {
+ for (i=0; i<numMirrorLights; i++) {
+ if (mirrorLights[i].key.equals(key)) {
+ return(mirrorLights[i]);
+ }
+ }
+ if (numMirrorLights == mirrorLights.length) {
+ newLights = new LightRetained[numMirrorLights*2];
+ for (i=0; i<numMirrorLights; i++) {
+ newLights[i] = mirrorLights[i];
+ }
+ mirrorLights = newLights;
+ }
+ // mirrorLights[numMirrorLights] = (LightRetained)
+ // this.clone(true);
+ mirrorLights[numMirrorLights] = (LightRetained) this.clone();
+ // If the bounding leaf is not null , add this
+ // mirror object as a user
+ if (boundingLeaf != null) {
+ mirrorLights[numMirrorLights].boundingLeaf = this.boundingLeaf.mirrorBoundingLeaf;
+ if (mirrorLights[numMirrorLights].boundingLeaf != null)
+ mirrorLights[numMirrorLights].boundingLeaf.addUser(mirrorLights[numMirrorLights]);
+ }
+ // mirrorLights[numMirrorLights].key = new HashKey(key);
+ mirrorLights[numMirrorLights].key = key;
+ mirrorLights[numMirrorLights].sgLight = this;
+ return(mirrorLights[numMirrorLights++]);
+ } else {
+ if (mirrorLights[0] == null) {
+ //mirrorLights[0] = (LightRetained) this.clone(true);
+ mirrorLights[0] = (LightRetained) this.clone();
+ // If the bounding leaf is not null , add this
+ // mirror object as a user
+ if (boundingLeaf != null) {
+ mirrorLights[0].boundingLeaf = this.boundingLeaf.mirrorBoundingLeaf;
+ if (mirrorLights[0].boundingLeaf != null)
+ mirrorLights[0].boundingLeaf.addUser(mirrorLights[0]);
+ }
+ mirrorLights[0].sgLight = this;
+ }
+ return(mirrorLights[0]);
+ }
+ }
+
+ void setLive(SetLiveState s) {
+ LightRetained ml;
+ int i, j;
+
+ newlyAddedMirrorLights.clear();
+ if (inImmCtx) {
+ throw new IllegalSharingException(J3dI18N.getString("LightRetained0"));
+ }
+ super.doSetLive(s);
+
+ if (s.inSharedGroup) {
+ for (i=0; i<s.keys.length; i++) {
+ ml = this.getMirrorLight(s.keys[i]);
+ ml.localToVworld = new Transform3D[1][];
+ ml.localToVworldIndex = new int[1][];
+
+ j = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(j < 0) {
+ System.out.println("LightRetained : Can't find hashKey");
+ }
+
+ ml.localToVworld[0] = localToVworld[j];
+ ml.localToVworldIndex[0] = localToVworldIndex[j];
+ // If its view Scoped, then add this list
+ // to be sent to Rendering Env
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ml);
+ s.scopedNodesViewList.add(s.viewLists.get(i));
+ } else {
+ s.nodeList.add(ml);
+ }
+
+ newlyAddedMirrorLights.add(ml);
+ if (boundingLeaf != null) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(ml);
+ }
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ml, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(ml, Targets.ENV_TARGETS);
+ }
+ ml.switchState = (SwitchState)s.switchStates.get(j);
+
+ }
+ } else {
+ ml = this.getMirrorLight(null);
+ ml.localToVworld = new Transform3D[1][];
+ ml.localToVworldIndex = new int[1][];
+ ml.localToVworld[0] = this.localToVworld[0];
+ ml.localToVworldIndex[0] = this.localToVworldIndex[0];
+ // Initialization of the mirror object
+ // If its view Scoped, then add this list
+ // to be sent to Rendering Env
+ // System.out.println("lightSetLive, s.viewList = "+s.viewLists);
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ml);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(ml);
+ }
+ newlyAddedMirrorLights.add(ml);
+ if (boundingLeaf != null) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(ml);
+ }
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ml, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(ml, Targets.ENV_TARGETS);
+ }
+ ml.switchState = (SwitchState)s.switchStates.get(0);
+ }
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+ super.markAsLive();
+
+ }
+
+ J3dMessage initMessage(int num) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.LIGHT_CHANGED;
+ createMessage.args[0] = this;
+ // a snapshot of all attributes that needs to be initialized
+ // in the mirror object
+ createMessage.args[1]= new Integer(INIT_MIRROR);
+
+ LightRetained[] mlts = new LightRetained[newlyAddedMirrorLights.size()];
+ for (int i = 0; i < mlts.length; i++) {
+ mlts[i] = (LightRetained)newlyAddedMirrorLights.get(i);
+ }
+ createMessage.args[2] = new Integer(mlts.length);
+ createMessage.args[3] = mlts;
+
+ Object[] obj = new Object[num];
+ obj[0] = boundingLeaf;
+ obj[1] = (regionOfInfluence != null?regionOfInfluence.clone():null);
+ obj[2] = (inBackgroundGroup? Boolean.TRUE:Boolean.FALSE);
+ obj[3] = geometryBackground;
+ obj[4] = (lightOn? Boolean.TRUE:Boolean.FALSE);
+
+ ArrayList addScopeList = new ArrayList();
+ for (int i = 0; i < scopes.size(); i++) {
+ GroupRetained group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.addAllNodesForScopedLight(mlts.length, mlts, addScopeList, tempKey);
+ }
+ Object[] scopeInfo = new Object[2];
+ scopeInfo[0] = ((scopes.size() > 0) ? Boolean.TRUE:Boolean.FALSE);
+ scopeInfo[1] = addScopeList;
+ obj[5] = scopeInfo;
+ Color3f clr = new Color3f(color);
+ obj[6] = clr;
+ createMessage.args[4] = obj;
+ return createMessage;
+
+ }
+
+ // The default set of clearLive actions
+ void clearLive(SetLiveState s) {
+ LightRetained ml;
+ newlyAddedMirrorLights.clear();
+ super.clearLive(s);
+
+ if (inSharedGroup) {
+ for (int i=0; i<s.keys.length; i++) {
+ ml = this.getMirrorLight(s.keys[i]);
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ml, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ newlyAddedMirrorLights.add(ml);
+ // Remove this mirror light as users of the bounding leaf
+ if (ml.boundingLeaf != null) {
+ ml.boundingLeaf.removeUser(ml);
+ ml.boundingLeaf = null;
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(ml, Targets.ENV_TARGETS);
+ }
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ml);
+ s.scopedNodesViewList.add(s.viewLists.get(i));
+ } else {
+ s.nodeList.add(ml);
+ }
+ }
+ } else {
+ ml = this.getMirrorLight(null);
+
+ // Remove this mirror light as users of the bounding leaf
+ if (ml.boundingLeaf != null) {
+ ml.boundingLeaf.removeUser(ml);
+ ml.boundingLeaf = null;
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(ml, Targets.ENV_TARGETS);
+ }
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ml);
+ //System.out.println("s.viewList is " + s.viewLists);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(ml);
+ }
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ml, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+
+ newlyAddedMirrorLights.add(ml);
+ }
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+
+
+
+ if (scopes.size() > 0) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ LightRetained[] mlts = new LightRetained[newlyAddedMirrorLights.size()];
+ for (int i = 0; i < mlts.length; i++) {
+ mlts[i] = (LightRetained)newlyAddedMirrorLights.get(i);
+ }
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.LIGHT_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(CLEAR_MIRROR);
+ ArrayList removeScopeList = new ArrayList();
+ for (int i = 0; i < scopes.size(); i++) {
+ GroupRetained group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.removeAllNodesForScopedLight(mlts.length, mlts, removeScopeList, tempKey);
+ }
+ createMessage.args[2] = removeScopeList;
+ createMessage.args[3] = new Integer(mlts.length);
+ createMessage.args[4] = mlts;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ void clearMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ ArrayList shapeList = (ArrayList)args[2];
+ ArrayList removeScopeList = new ArrayList();
+ LightRetained[] mLgts = (LightRetained[]) args[4];
+ int numLgts = ((Integer)args[3]).intValue();
+
+ for (int k = 0; k < numLgts; k++) {
+ for (int i = 0; i < shapeList.size(); i++) {
+ shape = ((GeometryAtom)shapeList.get(i)).source;
+ shape.removeLight(mLgts[k]);
+ }
+ mLgts[k].isScoped = false;
+
+ }
+
+ }
+
+
+
+ /**
+ * Clones only the retained side, internal use only
+ */
+ protected Object clone() {
+ LightRetained lr = (LightRetained)super.clone();
+ lr.color = new Color3f(color);
+ lr.scopes = (Vector) scopes.clone();
+ lr.initInfluencingBoundingLeaf(getInfluencingBoundingLeaf());
+ lr.region = null;
+ lr.lightDirty = 0xffff;
+ lr.sgLightDirty = 0xffff;
+ lr.universe = null;
+ lr.isNeeded = false;
+ lr.inImmCtx = false;
+ lr.sgLight = null;
+ lr.key = null;
+ lr.mirrorLights = new LightRetained[1];
+ lr.numMirrorLights = 0;
+ lr.environmentSets = new UnorderList(1, EnvironmentSet.class);
+ return lr;
+ }
+
+
+ // Called during RenderingEnv object update
+ void updateTransformChange() {
+ }
+
+ // Called on mirror object and updated when message is received
+ void updateImmediateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (boundingLeaf == null) {
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence,
+ getCurrentLocalToVworld());
+ }
+
+ }
+ }
+
+ void sendMessage(int attrMask, Object attr) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.LIGHT_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorLights);
+ else
+ createMessage.args[2] = new Integer(1);
+
+ createMessage.args[3] = mirrorLights.clone();
+ createMessage.args[4] = attr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (regionOfInfluence != null) {
+ regionOfInfluence.transform(xform.transform);
+ }
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/LightSet.java b/src/classes/share/javax/media/j3d/LightSet.java
new file mode 100644
index 0000000..df59b1a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LightSet.java
@@ -0,0 +1,91 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Vector;
+
+class LightSet extends Object {
+ /**
+ * The Lights that make up this set
+ */
+ LightRetained[] lights = null;
+
+ // The number of lights in this lightset, may be less than lights.length
+ int nlights = 0;
+
+ // A reference to the next LightSet
+ LightSet next = null;
+
+ // A reference to the previous LightSet
+ LightSet prev = null;
+
+ // A flag that indicates that lighting is on
+ boolean lightingOn = true;
+
+ // A flag that indicates that this light set has changed.
+ boolean isDirty = true;
+
+ /**
+ * Constructs a new LightSet
+ */
+ LightSet(RenderBin rb, RenderAtom ra, LightRetained[] lights,
+ int nlights, boolean lightOn) {
+ this.reset(rb, ra, lights, nlights, lightOn);
+ }
+
+ void reset(RenderBin rb, RenderAtom ra, LightRetained[] lights,
+ int nlights, boolean lightOn) {
+ int i;
+
+ this.isDirty = true;
+ this.lightingOn = lightOn;
+ if (this.lights == null || this.lights.length < nlights) {
+ this.lights = new LightRetained[nlights];
+ }
+
+ for (i=0; i<nlights; i++) {
+ this.lights[i] = lights[i];
+ }
+
+ this.nlights = nlights;
+
+ //lists = new RenderList(ro);
+ //lists.prims[ro.geometry.geoType-1] = ro;
+ }
+
+ boolean equals(RenderBin rb, LightRetained[] lights, int nlights,
+ boolean lightOn) {
+ int i, j;
+ LightRetained light;
+
+ if (this.nlights != nlights)
+ return(false);
+
+ if (this.lightingOn != lightOn)
+ return(false);
+
+ for (i=0; i<nlights; i++) {
+ for (j=0; j<this.nlights; j++) {
+ if (this.lights[j] == lights[i]) {
+ break;
+ }
+ }
+ if (j==this.nlights) {
+ return(false);
+ }
+ }
+ return(true);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/LineArray.java b/src/classes/share/javax/media/j3d/LineArray.java
new file mode 100644
index 0000000..125083a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LineArray.java
@@ -0,0 +1,161 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The LineArray object draws the array of vertices as individual
+ * line segments. Each pair of vertices defines a line to be drawn.
+ */
+
+public class LineArray extends GeometryArray {
+
+ /**
+ * packaged scope default constructor.
+ */
+ LineArray() {
+ }
+
+ /**
+ * Constructs an empty LineArray object with the specified
+ * number of vertices, and vertex format.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @exception IllegalArgumentException if vertexCount is less than 2
+ * or vertexCount is <i>not</i> a multiple of 2
+ */
+ public LineArray(int vertexCount, int vertexFormat) {
+ super(vertexCount,vertexFormat);
+
+ if (vertexCount < 2 || ((vertexCount%2) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("LineArray0"));
+ }
+
+ /**
+ * Constructs an empty LineArray object with the specified
+ * number of vertices, and vertex format, number of texture coordinate
+ * sets, and texture coordinate mapping array.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 2
+ * or vertexCount is <i>not</i> a multiple of 2
+ *
+ * @since Java 3D 1.2
+ */
+ public LineArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap);
+
+ if (vertexCount < 2 || ((vertexCount%2) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("LineArray0"));
+ }
+
+ /**
+ * Creates the retained mode LineArrayRetained object that this
+ * LineArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new LineArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ LineArrayRetained rt = (LineArrayRetained) retained;
+
+
+ int texSetCount = rt.getTexCoordSetCount();
+ LineArray l;
+ if (texSetCount == 0) {
+ l = new LineArray(rt.getVertexCount(),
+ rt.getVertexFormat());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ l = new LineArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap);
+
+ }
+
+ l.duplicateNodeComponent(this);
+ return l;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/LineArrayRetained.java b/src/classes/share/javax/media/j3d/LineArrayRetained.java
new file mode 100644
index 0000000..6391b4f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LineArrayRetained.java
@@ -0,0 +1,367 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The LineArray object draws the array of vertices as individual
+ * line segments. Each pair of vertices defines a line to be drawn.
+ */
+
+class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
+
+ LineArrayRetained() {
+ this.geoType = GEO_TYPE_LINE_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[2];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
+ pickRay.direction, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ Vector3d dir =
+ new Vector3d(pickSegment.end.x - pickSegment.start.x,
+ pickSegment.end.y - pickSegment.start.y,
+ pickSegment.end.z - pickSegment.start.z);
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectLineAndRay(pnts[0], pnts[1],
+ pickSegment.start,
+ dir, sdist, iPnt) &&
+ (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("LineArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+
+ }
+
+
+ boolean intersect(Point3d[] pnts) {
+ Point3d[] points = new Point3d[2];
+ double dist[] = new double[1];
+ Vector3d dir;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ case 4: // Quad
+ while (i<validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ if (intersectSegment(pnts, points[0], points[1], dist,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ dir = new Vector3d();
+ while (i<validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectLineAndRay(pnts[0], pnts[1], points[0],
+ dir, dist, null) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ dir = new Vector3d();
+ while (i<validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectPntAndRay(pnts[0], points[0], dir, dist) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+ Point3d[] pnts = new Point3d[2];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ Point3d[] pnts = new Point3d[2];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, null,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ null, null)) {
+ return true;
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+
+ return false;
+ }
+ // From Graphics Gems IV (pg5) and Graphics Gems II, Pg170
+ void computeCentroid() {
+ Point3d pnt0 = new Point3d();
+ Point3d pnt1 = new Point3d();
+ double length;
+ double totallength = 0;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ centroid.x = 0;
+ centroid.y = 0;
+ centroid.z = 0;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt0);
+ getVertexData(i++, pnt1);
+ length = pnt0.distance(pnt1);
+ centroid.x += (pnt0.x + pnt1.x) * length;
+ centroid.y += (pnt0.y + pnt1.y) * length;
+ centroid.z += (pnt0.z + pnt1.z) * length;
+ totallength += length;
+ }
+
+ if (totallength != 0.0) {
+ length = 1.0/(2.0 * totallength);
+ centroid.x *= length;
+ centroid.y *= length;
+ centroid.z *= length;
+ }
+ }
+
+ int getClassType() {
+ return LINE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/LineAttributes.java b/src/classes/share/javax/media/j3d/LineAttributes.java
new file mode 100644
index 0000000..7d9caab
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LineAttributes.java
@@ -0,0 +1,472 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The LineAttributes object defines all rendering state that can be set
+ * as a component object of a Shape3D node.
+ * The line attributes that can be defined are:<P>
+ * <UL><LI>Pattern - specifies the pattern used to draw the line:<p>
+ * <ul>
+ * <li>PATTERN_SOLID - draws a solid line with no pattern. This is
+ * the default.</li>
+ * <p>
+ * <li>PATTERN_DASH - draws dashed lines. Ideally, these will be drawn with
+ * a repeating pattern of 8 pixels on and 8 pixels off.</li>
+ * <p>
+ * <li>PATTERN_DOT - draws dotted lines. Ideally, these will be drawn with
+ * a repeating pattern of 1 pixel on and 7 pixels off.</li>
+ * <p>
+ * <li>PATTERN_DASH_DOT - draws dashed-dotted lines. Ideally, these will be
+ * drawn with a repeating pattern of 7 pixels on, 4 pixels off, 1 pixel on,
+ * and 4 pixels off.</li>
+ * <p>
+ * <li>PATTERN_USER_DEFINED - draws lines with a user-defined line pattern.
+ * See "User-defined Line Patterns," below.</li><p>
+ * </ul>
+ * <p>
+ * <LI>Antialiasing (enabled or disabled). By default, antialiasing
+ * is disabled.</LI>
+ * <p>
+ * <p>
+ * If antialiasing is enabled, the lines are considered transparent
+ * for rendering purposes. They are rendered with all the other transparent
+ * objects and adhere to the other transparency settings such as the
+ * View transparency sorting policy and the View depth buffer freeze
+ * transparent enable.
+ * </p>
+ * <LI>Width (in pixels). The default is a line width of one pixel.
+ * </LI></UL><p>
+ *
+ * <b>User-defined Line Patterns</b>
+ * <p>
+ * A user-defined line pattern is specified with a pattern mask and
+ * an optional scale factor.
+ * <p>
+ * The Pattern Mask<p>
+ *
+ * The pattern is specified
+ * using a 16-bit mask that specifies on and off segments. Bit 0 in
+ * the pattern mask corresponds to the first pixel of the line or line
+ * strip primitive. A value of 1 for a bit in the pattern mask indicates
+ * that the corresponding pixel is drawn, while a value of 0
+ * indicates that the corresponding pixel is not drawn. After all 16 bits
+ * in the pattern are used, the pattern is repeated.
+ * <p>
+ * For example, a mask of 0x00ff defines a dashed line with a repeating
+ * pattern of 8 pixels on followed by 8 pixels off. A value of 0x0101
+ * defines a a dotted line with a repeating pattern of 1 pixel on and 7
+ * pixels off.
+ * <p>
+ * The pattern continues around individual line segments of a line strip
+ * primitive. It is restarted at the beginning of each new line strip.
+ * For line array primitives, the pattern is restarted at the beginning
+ * of each line.
+ * <p>
+ * The Scale Factor
+ * <p>
+ * The pattern is multiplied by the scale factor such that each bit in
+ * the pattern mask corresponds to that many consecutive pixels.
+ * For example, a scale factor of 3 applied to a pattern mask of 0x001f
+ * would produce a repeating pattern of 15 pixels on followed by 33
+ * pixels off. The valid range for this attribute is [1,15]. Values
+ * outside this range are clamped.<p>
+ *
+ * @see Appearance
+ * @see View
+ */
+public class LineAttributes extends NodeComponent {
+
+ /**
+ * Specifies that this LineAttributes object allows reading its
+ * line width information.
+ */
+ public static final int
+ ALLOW_WIDTH_READ = CapabilityBits.LINE_ATTRIBUTES_ALLOW_WIDTH_READ;
+
+ /**
+ * Specifies that this LineAttributes object allows writing its
+ * line width information.
+ */
+ public static final int
+ ALLOW_WIDTH_WRITE = CapabilityBits.LINE_ATTRIBUTES_ALLOW_WIDTH_WRITE;
+
+ /**
+ * Specifies that this LineAttributes object allows reading its
+ * line pattern information.
+ */
+ public static final int
+ ALLOW_PATTERN_READ = CapabilityBits.LINE_ATTRIBUTES_ALLOW_PATTERN_READ;
+
+ /**
+ * Specifies that this LineAttributes object allows writing its
+ * line pattern information.
+ */
+ public static final int
+ ALLOW_PATTERN_WRITE = CapabilityBits.LINE_ATTRIBUTES_ALLOW_PATTERN_WRITE;
+
+ /**
+ * Specifies that this LineAttributes object allows reading its
+ * line antialiasing flag.
+ */
+ public static final int
+ ALLOW_ANTIALIASING_READ = CapabilityBits.LINE_ATTRIBUTES_ALLOW_ANTIALIASING_READ;
+
+ /**
+ * Specifies that this LineAttributes object allows writing its
+ * line antialiasing flag.
+ */
+ public static final int
+ ALLOW_ANTIALIASING_WRITE = CapabilityBits.LINE_ATTRIBUTES_ALLOW_ANTIALIASING_WRITE;
+
+
+ /**
+ * Draw solid lines with no pattern.
+ * @see #setLinePattern
+ */
+ public static final int PATTERN_SOLID = 0;
+
+ /**
+ * Draw dashed lines. Ideally, these will be drawn with
+ * a repeating pattern of 8 pixels on and 8 pixels off.
+ * @see #setLinePattern
+ */
+ public static final int PATTERN_DASH = 1;
+
+ /**
+ * Draw dotted lines. Ideally, these will be drawn with
+ * a repeating pattern of 1 pixel on and 7 pixels off.
+ * @see #setLinePattern
+ */
+ public static final int PATTERN_DOT = 2;
+
+ /**
+ * Draw dashed-dotted lines. Ideally, these will be drawn with
+ * a repeating pattern of 7 pixels on, 4 pixels off, 1 pixel on,
+ * and 4 pixels off.
+ * @see #setLinePattern
+ */
+ public static final int PATTERN_DASH_DOT = 3;
+
+ /**
+ * Draw lines with a user-defined line pattern. The line pattern
+ * is specified with a pattern mask and scale factor.
+ * @see #setLinePattern
+ * @see #setPatternMask
+ * @see #setPatternScaleFactor
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int PATTERN_USER_DEFINED = 4;
+
+
+ /**
+ * Constructs a LineAttributes object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * line width : 1<br>
+ * line pattern : PATTERN_SOLID<br>
+ * pattern mask : 0xffff<br>
+ * pattern scale factor : 1<br>
+ * line antialiasing : false<br>
+ * </ul>
+ */
+ public LineAttributes(){
+ }
+
+ /**
+ * Constructs a LineAttributes object with specified values.
+ * @param lineWidth the width of lines in pixels
+ * @param linePattern the line pattern, one of PATTERN_SOLID,
+ * PATTERN_DASH, PATTERN_DOT, or PATTERN_DASH_DOT
+ * @param lineAntialiasing flag to set line antialising ON or OFF
+ */
+ public LineAttributes(float lineWidth, int linePattern,
+ boolean lineAntialiasing){
+
+ if (linePattern < PATTERN_SOLID || linePattern > PATTERN_DASH_DOT)
+ throw new IllegalArgumentException(J3dI18N.getString("LineAttributes0"));
+
+ ((LineAttributesRetained)this.retained).initLineWidth(lineWidth);
+ ((LineAttributesRetained)this.retained).initLinePattern(linePattern);
+ ((LineAttributesRetained)this.retained).initLineAntialiasingEnable(lineAntialiasing);
+ }
+
+ /**
+ * Sets the line width for this LineAttributes component object.
+ * @param lineWidth the width, in pixels, of line primitives
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setLineWidth(float lineWidth) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_WIDTH_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes1"));
+ if (isLive())
+ ((LineAttributesRetained)this.retained).setLineWidth(lineWidth);
+ else
+ ((LineAttributesRetained)this.retained).initLineWidth(lineWidth);
+
+ }
+
+ /**
+ * Gets the line width for this LineAttributes component object.
+ * @return the width, in pixels, of line primitives
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getLineWidth() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_WIDTH_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes2"));
+ return ((LineAttributesRetained)this.retained).getLineWidth();
+ }
+
+ /**
+ * Sets the line pattern for this LineAttributes component object.
+ * @param linePattern the line pattern to be used, one of:
+ * PATTERN_SOLID, PATTERN_DASH, PATTERN_DOT, PATTERN_DASH_DOT, or
+ * PATTERN_USER_DEFINED.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setLinePattern(int linePattern) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PATTERN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes3"));
+
+ if (linePattern < PATTERN_SOLID || linePattern > PATTERN_USER_DEFINED)
+ throw new IllegalArgumentException(J3dI18N.getString("LineAttributes4"));
+
+ if (isLive())
+ ((LineAttributesRetained)this.retained).setLinePattern(linePattern);
+ else
+ ((LineAttributesRetained)this.retained).initLinePattern(linePattern);
+
+
+}
+
+ /**
+ * Gets the line pattern for this LineAttributes component object.
+ * @return the line pattern
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getLinePattern() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PATTERN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes5"));
+
+ return ((LineAttributesRetained)this.retained).getLinePattern();
+ }
+
+
+ /**
+ * Sets the line pattern mask to the specified value. This is
+ * used when the linePattern attribute is set to
+ * PATTERN_USER_DEFINED. In this mode, the pattern is specified
+ * using a 16-bit mask that specifies on and off segments. Bit 0
+ * in the pattern mask corresponds to the first pixel of the line
+ * or line strip primitive. A value of 1 for a bit in the pattern
+ * mask indicates that the corresponding pixel is drawn, while a
+ * value of 0 indicates that the corresponding pixel is not drawn.
+ * After all 16 bits in the pattern are used, the pattern is
+ * repeated. For example, a mask of 0x00ff defines a dashed line
+ * with a repeating pattern of 8 pixels on followed by 8 pixels
+ * off. A value of 0x0101 defines a a dotted line with a
+ * repeating pattern of 1 pixel on and 7 pixels off
+ * <p>
+ * The pattern continues around individual line segments of a line
+ * strip primitive. It is restarted at the beginning of each new
+ * line strip. For line array primitives, the pattern is
+ * restarted at the beginning of each line.
+ * @param mask the new line pattern mask
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see #setPatternScaleFactor
+ *
+ * @since Java 3D 1.2
+ */
+ public void setPatternMask(int mask) {
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_PATTERN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes8"));
+
+ if (isLive())
+ ((LineAttributesRetained)this.retained).setPatternMask(mask);
+ else
+ ((LineAttributesRetained)this.retained).initPatternMask(mask);
+ }
+
+
+ /**
+ * Retrieves the line pattern mask.
+ * @return the line pattern mask
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getPatternMask() {
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_PATTERN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes9"));
+
+ return ((LineAttributesRetained)this.retained).getPatternMask();
+ }
+
+
+ /**
+ * Sets the line pattern scale factor to the specified value.
+ * This is used in conjunction with the patternMask when the
+ * linePattern attribute is set to PATTERN_USER_DEFINED. The
+ * pattern is multiplied by the scale factor such that each bit in
+ * the pattern mask corresponds to that many consecutive pixels.
+ * For example, a scale factor of 3 applied to a pattern mask of
+ * 0x001f would produce a repeating pattern of 15 pixels on
+ * followed by 33 pixels off. The valid range for this attribute
+ * is [1,15]. Values outside this range are clamped.
+ * @param scaleFactor the new line pattern scale factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see #setPatternMask
+ *
+ * @since Java 3D 1.2
+ */
+ public void setPatternScaleFactor(int scaleFactor) {
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_PATTERN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes10"));
+
+ if (isLive())
+ ((LineAttributesRetained)this.retained).setPatternScaleFactor(scaleFactor);
+ else
+ ((LineAttributesRetained)this.retained).initPatternScaleFactor(scaleFactor);
+ }
+
+
+ /**
+ * Retrieves the line pattern scale factor.
+ * @return the line pattern scale factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getPatternScaleFactor() {
+ if (isLiveOrCompiled() &&
+ !this.getCapability(ALLOW_PATTERN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes11"));
+
+ return ((LineAttributesRetained)this.retained).getPatternScaleFactor();
+ }
+
+
+ /**
+ * Enables or disables line antialiasing
+ * for this LineAttributes component object.
+ * <p>
+ * If antialiasing is enabled, the lines are considered transparent
+ * for rendering purposes. They are rendered with all the other
+ * transparent objects and adhere to the other transparency settings
+ * such as the View transparency sorting policy and the View depth buffer
+ * freeze transparent enable.
+ * </p>
+ * @param state true or false to enable or disable line antialiasing
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see View
+ */
+ public void setLineAntialiasingEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANTIALIASING_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes6"));
+ if (isLive())
+ ((LineAttributesRetained)this.retained).setLineAntialiasingEnable(state);
+ else
+ ((LineAttributesRetained)this.retained).initLineAntialiasingEnable(state);
+
+
+ }
+
+ /**
+ * Retrieves the state of the line antialiasing flag.
+ * @return true if line antialiasing is enabled,
+ * false if line antialiasing is disabled
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getLineAntialiasingEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANTIALIASING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("LineAttributes7"));
+
+ return ((LineAttributesRetained)this.retained).getLineAntialiasingEnable();
+ }
+
+ /**
+ * Creates a retained mode LineAttributesRetained object that this
+ * LineAttributes component object will point to.
+ */
+ void createRetained() {
+ this.retained = new LineAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ LineAttributes la = new LineAttributes();
+ la.duplicateNodeComponent(this);
+ return la;
+ }
+
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent,
+ forceDuplicate);
+
+ LineAttributesRetained attr = (LineAttributesRetained)
+ originalNodeComponent.retained;
+ LineAttributesRetained rt = (LineAttributesRetained) retained;
+
+ rt.initLineWidth(attr.getLineWidth());
+ rt.initLinePattern(attr.getLinePattern());
+ rt.initLineAntialiasingEnable(attr.getLineAntialiasingEnable());
+ rt.initPatternMask(attr.getPatternMask());
+ rt.initPatternScaleFactor(attr.getPatternScaleFactor());
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/LineAttributesRetained.java b/src/classes/share/javax/media/j3d/LineAttributesRetained.java
new file mode 100644
index 0000000..d84b449
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LineAttributesRetained.java
@@ -0,0 +1,335 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * The LineAttributesRetained object defines all rendering state that can be set
+ * as a component object of a Shape3D node.
+ */
+class LineAttributesRetained extends NodeComponentRetained {
+ // A list of pre-defined bits to indicate which component
+ // in this LineAttributesRetained object changed.
+ static final int LINE_WIDTH_CHANGED = 0x01;
+ static final int LINE_PATTERN_CHANGED = 0x02;
+ static final int LINE_AA_CHANGED = 0x04;
+ static final int LINE_PATTERN_MASK_CHANGED = 0x08;
+ static final int LINE_PATTERN_SCALEFACTOR_CHANGED = 0x10;
+
+ // Width, in pixels, of line primitives
+ float lineWidth = 1.0f;
+
+ // The line pattern to be used
+ int linePattern = LineAttributes.PATTERN_SOLID;
+
+ // Line antialiasing switch
+ boolean lineAntialiasing = false;
+
+ // user-defined line pattern mask
+ int linePatternMask = 0xffff;
+
+ // line mask pattern scale factor
+ int linePatternScaleFactor = 1;
+
+ /**
+ * Sets the line width for this lineAttributes component object.
+ * @param lineWidth the width, in pixels, of line primitives
+ */
+ final void initLineWidth(float lineWidth) {
+ this.lineWidth = lineWidth;
+ }
+
+ /**
+ * Sets the line width for this lineAttributes component object and sends a
+ * message notifying the interested structures of the change.
+ * @param lineWidth the width, in pixels, of line primitives
+ */
+ final void setLineWidth(float lineWidth) {
+ initLineWidth(lineWidth);
+ sendMessage(LINE_WIDTH_CHANGED, new Float(lineWidth));
+ }
+
+ /**
+ * Gets the line width for this lineAttributes component object.
+ * @return the width, in pixels, of line primitives
+ */
+ final float getLineWidth() {
+ return lineWidth;
+ }
+
+ /**
+ * Sets the line pattern for this lineAttributes component object
+ * @param linePattern the line pattern to be used, one of:
+ * PATTERN_SOLID, PATTERN_DASH, PATTERN_DOT, or PATTERN_DASH_DOT
+ */
+ final void initLinePattern(int linePattern) {
+ this.linePattern = linePattern;
+ }
+ /**
+ * Sets the line pattern for this lineAttributes component object
+ * and sends a message notifying the interested structures of the change.
+ * @param linePattern the line pattern to be used, one of:
+ * PATTERN_SOLID, PATTERN_DASH, PATTERN_DOT, or PATTERN_DASH_DOT
+ */
+ final void setLinePattern(int linePattern) {
+ initLinePattern(linePattern);
+ sendMessage(LINE_PATTERN_CHANGED, new Integer(linePattern));
+ }
+
+ /**
+ * Gets the line pattern for this lineAttributes component object.
+ * @return the line pattern
+ */
+ final int getLinePattern() {
+ return linePattern;
+ }
+
+ /**
+ * Enables or disables line antialiasing
+ * for this lineAttributes component object and sends a
+ * message notifying the interested structures of the change.
+ * @param state true or false to enable or disable line antialiasing
+ */
+ final void initLineAntialiasingEnable(boolean state) {
+ lineAntialiasing = state;
+ }
+ /**
+ * Enables or disables line antialiasing
+ * for this lineAttributes component object and sends a
+ * message notifying the interested structures of the change.
+ * @param state true or false to enable or disable line antialiasing
+ */
+ final void setLineAntialiasingEnable(boolean state) {
+ initLineAntialiasingEnable(state);
+ sendMessage(LINE_AA_CHANGED,
+ (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Retrieves the state of the line antialiasing flag.
+ * @return true if line antialiasing is enabled,
+ * false if line antialiasing is disabled
+ */
+ final boolean getLineAntialiasingEnable() {
+ return lineAntialiasing;
+ }
+
+
+ /**
+ * Sets the pattern mask for this LineAttributes component object.
+ * This is used when the linePattern attribute is set to
+ * PATTERN_USER_DEFINED.
+ * @param mask the line pattern mask to be used.
+ */
+ final void initPatternMask(int mask) {
+ this.linePatternMask = mask;
+ }
+
+ /**
+ * Sets the pattern mask for this LineAttributes component object
+ * and sends a message notifying the interested structures of change.
+ * This is used when the linePattern attribute is set to
+ * PATTERN_USER_DEFINED.
+ * @param mask the line pattern mask to be used.
+ */
+ final void setPatternMask(int mask) {
+ initPatternMask(mask);
+ sendMessage(LINE_PATTERN_MASK_CHANGED, new Integer(mask));
+ }
+
+ /**
+ * Retrieves the pattern mask for this LineAttributes component object.
+ * @return the user-defined pattern mask
+ */
+ final int getPatternMask() {
+ return linePatternMask;
+ }
+
+ /**
+ * Sets the pattern mask scale factor for this LineAttributes
+ * component object. This is used when the linePattern attribute
+ * is set to PATTERN_USER_DEFINED.
+ * @param scaleFactor the scale factor of mask, clamp to [1, 15]
+ */
+ final void initPatternScaleFactor(int scaleFactor) {
+ if (scaleFactor < 1) {
+ scaleFactor = 1;
+ } else if (scaleFactor > 15) {
+ scaleFactor = 15;
+ }
+ this.linePatternScaleFactor = scaleFactor;
+ }
+
+ /**
+ * Sets the pattern mask scale factor for this LineAttributes
+ * component object and sends a message notifying the interested
+ * structures of change. This is used when the linePattern
+ * attribute is set to PATTERN_USER_DEFINED.
+ * @param scaleFactor the scale factor of mask, clamp to [1, 15]
+ */
+ final void setPatternScaleFactor(int scaleFactor) {
+ initPatternScaleFactor(scaleFactor);
+ sendMessage(LINE_PATTERN_SCALEFACTOR_CHANGED, new Integer(scaleFactor));
+ }
+
+ /**
+ * Retrieves the pattern scale factor for this LineAttributes
+ * component object.
+ * @return the pattern mask scale factor
+ */
+ final int getPatternScaleFactor() {
+ return linePatternScaleFactor;
+ }
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ } else {
+ LineAttributesRetained mirrorLa = new LineAttributesRetained();
+ mirrorLa.source = source;
+ mirrorLa.set(this);
+ mirror = mirrorLa;
+ }
+ } else {
+ ((LineAttributesRetained) mirror).set(this);
+ }
+ }
+
+
+ /**
+ * This (native) method updates the native context.
+ */
+ native void updateNative(long ctx,
+ float lineWidth, int linePattern,
+ int linePatternMask,
+ int linePatternScaleFactor,
+ boolean lineAntialiasing);
+
+ /**
+ * This method updates the native context.
+ */
+ void updateNative(long ctx) {
+ updateNative(ctx,
+ lineWidth, linePattern, linePatternMask,
+ linePatternScaleFactor, lineAntialiasing);
+ }
+
+
+ /**
+ * Initializes a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ ((LineAttributesRetained)mirror).set(this);
+ }
+
+ /** Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ LineAttributesRetained mirrorLa = (LineAttributesRetained) mirror;
+
+ if ((component & LINE_WIDTH_CHANGED) != 0) {
+ mirrorLa.lineWidth = ((Float)value).floatValue();
+ }
+ else if ((component & LINE_PATTERN_CHANGED) != 0) {
+ mirrorLa.linePattern = ((Integer)value).intValue();
+ }
+ else if ((component & LINE_AA_CHANGED) != 0) {
+ mirrorLa.lineAntialiasing = ((Boolean)value).booleanValue();
+ }
+ else if ((component & LINE_PATTERN_MASK_CHANGED) != 0) {
+ mirrorLa.linePatternMask = ((Integer)value).intValue();
+ }
+ else if ((component & LINE_PATTERN_SCALEFACTOR_CHANGED) != 0)
+ {
+ mirrorLa.linePatternScaleFactor = ((Integer)value).intValue();
+ }
+ }
+
+
+ boolean equivalent(LineAttributesRetained lr) {
+ return ((lr != null) &&
+ (lineWidth == lr.lineWidth) &&
+ (linePattern == lr.linePattern) &&
+ (lineAntialiasing == lr.lineAntialiasing) &&
+ (linePatternMask == lr.linePatternMask) &&
+ (linePatternScaleFactor == lr.linePatternScaleFactor));
+
+ }
+
+ protected void set(LineAttributesRetained lr) {
+ super.set(lr);
+ lineWidth = lr.lineWidth;
+ linePattern = lr.linePattern;
+ linePatternScaleFactor = lr.linePatternScaleFactor;
+ linePatternMask = lr.linePatternMask;
+ lineAntialiasing = lr.lineAntialiasing;
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.LINEATTRIBUTES_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.LINEATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+
+ }
+ void handleFrequencyChange(int bit) {
+ if (bit == LineAttributes.ALLOW_WIDTH_WRITE ||
+ bit == LineAttributes.ALLOW_PATTERN_WRITE||
+ bit == LineAttributes.ALLOW_ANTIALIASING_WRITE) {
+ setFrequencyChangeMask(bit, 0x1);
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/LineStripArray.java b/src/classes/share/javax/media/j3d/LineStripArray.java
new file mode 100644
index 0000000..8c7d415
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LineStripArray.java
@@ -0,0 +1,177 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The LineStripArray object draws an array of vertices as a set of
+ * connected line strips. An array of per-strip vertex counts specifies
+ * where the separate strips appear in the vertex array.
+ * For every strip in the set, each vertex, beginning with
+ * the second vertex in the array, defines a line segment to be drawn
+ * from the previous vertex to the current vertex.
+ */
+
+public class LineStripArray extends GeometryStripArray {
+
+ /**
+ * Package scoped default constructor used by cloneNodeComponent.
+ */
+ LineStripArray() {
+ }
+
+ /**
+ * Constructs an empty LineStripArray object with the specified
+ * number of vertices, vertex format, and
+ * array of per-strip vertex counts.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 2
+ * or any element in the stripVertexCounts array is less than 2
+ */
+ public LineStripArray(int vertexCount,
+ int vertexFormat,
+ int stripVertexCounts[]) {
+
+ super(vertexCount, vertexFormat, stripVertexCounts);
+
+ if (vertexCount < 2 )
+ throw new IllegalArgumentException(J3dI18N.getString("LineStripArray0"));
+ }
+
+ /**
+ * Constructs an empty LineStripArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and
+ * array of per-strip vertex counts.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 2
+ * or any element in the stripVertexCounts array is less than 2
+ *
+ * @since Java 3D 1.2
+ */
+ public LineStripArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int stripVertexCounts[]) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ stripVertexCounts);
+
+ if (vertexCount < 2 )
+ throw new IllegalArgumentException(J3dI18N.getString("LineStripArray0"));
+ }
+
+ /**
+ * Creates the retained mode LineStripArrayRetained object that this
+ * LineStripArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new LineStripArrayRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ LineStripArrayRetained rt = (LineStripArrayRetained) retained;
+ int stripcounts[] = new int[rt.getNumStrips()];
+ rt.getStripVertexCounts(stripcounts);
+ int texSetCount = rt.getTexCoordSetCount();
+ LineStripArray l;
+ if (texSetCount == 0) {
+ l = new LineStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ stripcounts);
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ l = new LineStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ stripcounts);
+ }
+ l.duplicateNodeComponent(this);
+ return l;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/LineStripArrayRetained.java b/src/classes/share/javax/media/j3d/LineStripArrayRetained.java
new file mode 100644
index 0000000..80f1386
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LineStripArrayRetained.java
@@ -0,0 +1,463 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The LineStripArray object draws an array of vertices as a set of
+ * connected line strips. An array of per-strip vertex counts specifies
+ * where the separate strips appear in the vertex array.
+ * For every strip in the set, each vertex, beginning with
+ * the second vertex in the array, defines a line segment to be drawn
+ * from the previous vertex to the current vertex.
+ */
+
+class LineStripArrayRetained extends GeometryStripArrayRetained {
+
+ LineStripArrayRetained() {
+ this.geoType = GEO_TYPE_LINE_STRIP_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[2];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int j, end;
+ int i = 0;
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while(i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+
+ if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
+ pickRay.direction, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ Vector3d dir =
+ new Vector3d(pickSegment.end.x - pickSegment.start.x,
+ pickSegment.end.y - pickSegment.start.y,
+ pickSegment.end.z - pickSegment.start.z);
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+
+ if (intersectLineAndRay(pnts[0], pnts[1],
+ pickSegment.start,
+ dir, sdist, iPnt) &&
+ (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+ if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("LineStripArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+
+ }
+
+ boolean intersect(Point3d[] pnts) {
+ int j, end;
+ Point3d[] points = new Point3d[2];
+ double dist[] = new double[1];
+ Vector3d dir;
+ int i = 0;
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+
+
+
+ switch (pnts.length) {
+ case 3:
+ case 4: // Triangle, Quad
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ while (j < end) {
+ getVertexData(j++, points[1]);
+ if (intersectSegment(pnts, points[0], points[1],
+ dist, null)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ }
+ }
+ break;
+ case 2: // Line
+ dir = new Vector3d();
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ while (j < end) {
+ getVertexData(j++, points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectLineAndRay(pnts[0], pnts[1],
+ points[0], dir, dist, null) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ }
+ }
+ break;
+ case 1: // Point
+ dir = new Vector3d();
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ while (j < end) {
+ getVertexData(j++, points[1]);
+ dir.x = points[1].x - points[0].x;
+ dir.y = points[1].y - points[0].y;
+ dir.z = points[1].z - points[0].z;
+ if (intersectPntAndRay(pnts[0], points[0], dir,
+ dist) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+ int i = 0;
+ int j, end;
+ Point3d[] pnts = new Point3d[2];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ thisToOtherVworld.transform(pnts[0]);
+ while (j < end) {
+ getVertexData(j++, pnts[1]);
+ thisToOtherVworld.transform(pnts[1]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = 0;
+ int j, offset, end;
+ Point3d[] pnts = new Point3d[2];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+
+
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while ( j < end) {
+ getVertexData(j++, pnts[1]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while ( j < end) {
+ getVertexData(j++, pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, null,
+ null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ while ( j < end) {
+ getVertexData(j++, pnts[1]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+
+ return false;
+ }
+
+ // From Graphics Gems IV (pg5) and Graphics Gems II, Pg170
+ void computeCentroid() {
+ int i = 0;
+ int j;
+ double length;
+ double totallength = 0;
+ int start, end;
+ boolean replaceVertex1;
+ Point3d pnt0 = new Point3d();
+ Point3d pnt1 = new Point3d();
+
+ centroid.x = 0;
+ centroid.y = 0;
+ centroid.z = 0;
+
+
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnt0);
+ replaceVertex1 = true;
+ while (j < end) {
+ if (replaceVertex1) {
+ getVertexData(j++, pnt1);
+ replaceVertex1 = false;
+ } else {
+ getVertexData(j++, pnt0);
+ replaceVertex1 = true;
+ }
+ length = pnt0.distance(pnt1);
+ centroid.x += (pnt0.x + pnt1.x) * length;
+ centroid.y += (pnt0.y + pnt1.y) * length;
+ centroid.z += (pnt0.z + pnt1.z) * length;
+ totallength += length;
+ }
+ }
+ if (totallength != 0.0) {
+ length = 1.0/(2.0 * totallength);
+ centroid.x *= length;
+ centroid.y *= length;
+ centroid.z *= length;
+ }
+ }
+
+ int getClassType() {
+ return LINE_TYPE;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/LinearFog.java b/src/classes/share/javax/media/j3d/LinearFog.java
new file mode 100644
index 0000000..3ccb35a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LinearFog.java
@@ -0,0 +1,232 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+
+/**
+ * The LinearFog leaf node defines fog distance parameters for
+ * linear fog.
+ * LinearFog extends the Fog node by adding a pair of distance values,
+ * in Z, at which the fog should start obscuring the scene and should maximally
+ * obscure the scene.
+ * <P>
+ * The front and back fog distances are defined in the local coordinate system of
+ * the node, but the actual fog equation will ideally take place in eye
+ * coordinates.
+ * <P>
+ * The linear fog blending factor, f, is computed as follows:
+ * <P><UL>
+ * f = backDistance - z / backDistance - frontDistance<P>
+ * where
+ * <ul>z is the distance from the viewpoint.<br>
+ * frontDistance is the distance at which fog starts obscuring objects.<br>
+ * backDistance is the distance at which fog totally obscurs objects.
+ * </ul><P></UL>
+ */
+public class LinearFog extends Fog {
+ /**
+ * Specifies that this LinearFog node allows read access to its distance
+ * information.
+ */
+ public static final int
+ ALLOW_DISTANCE_READ = CapabilityBits.LINEAR_FOG_ALLOW_DISTANCE_READ;
+
+ /**
+ * Specifies that this LinearFog node allows write access to its distance
+ * information.
+ */
+ public static final int
+ ALLOW_DISTANCE_WRITE = CapabilityBits.LINEAR_FOG_ALLOW_DISTANCE_WRITE;
+
+ /**
+ * Constructs a LinearFog node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * front distance : 0.1<br>
+ * back distance : 1.0<br>
+ * </ul>
+ */
+ public LinearFog() {
+ // Just use the defaults
+ }
+
+ /**
+ * Constructs a LinearFog node with the specified fog color.
+ * @param color the fog color
+ */
+ public LinearFog(Color3f color) {
+ super(color);
+ }
+
+ /**
+ * Constructs a LinearFog node with the specified fog color and distances.
+ * @param color the fog color
+ * @param frontDistance the front distance for the fog
+ * @param backDistance the back distance for the fog
+ */
+ public LinearFog(Color3f color, double frontDistance, double backDistance) {
+ super(color);
+ ((LinearFogRetained)this.retained).initFrontDistance(frontDistance);
+ ((LinearFogRetained)this.retained).initBackDistance(backDistance);
+ }
+
+ /**
+ * Constructs a LinearFog node with the specified fog color.
+ * @param r the red component of the fog color
+ * @param g the green component of the fog color
+ * @param b the blue component of the fog color
+ */
+ public LinearFog(float r, float g, float b) {
+ super(r, g, b);
+ }
+
+ /**
+ * Constructs a LinearFog node with the specified fog color and distances.
+ * @param r the red component of the fog color
+ * @param g the green component of the fog color
+ * @param b the blue component of the fog color
+ * @param frontDistance the front distance for the fog
+ * @param backDistance the back distance for the fog
+ */
+ public LinearFog(float r, float g, float b,
+ double frontDistance, double backDistance) {
+ super(r, g, b);
+ ((LinearFogRetained)this.retained).initFrontDistance(frontDistance);
+ ((LinearFogRetained)this.retained).initBackDistance(backDistance);
+ }
+
+ /**
+ * Sets front distance for fog.
+ * @param frontDistance the distance at which fog starts obscuring objects
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setFrontDistance(double frontDistance) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("LinearFog0"));
+
+ if (isLive())
+ ((LinearFogRetained)this.retained).setFrontDistance(frontDistance);
+ else
+ ((LinearFogRetained)this.retained).initFrontDistance(frontDistance);
+
+ }
+
+ /**
+ * Gets front distance for fog.
+ * @return the distance at which fog starts obscuring objects
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public double getFrontDistance() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("LinearFog1"));
+
+ return ((LinearFogRetained)this.retained).getFrontDistance();
+ }
+
+ /**
+ * Sets back distance for fog.
+ * @param backDistance the distance at which fog totally obscurs objects
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setBackDistance(double backDistance) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("LinearFog0"));
+ if (isLive())
+ ((LinearFogRetained)this.retained).setBackDistance(backDistance);
+ else
+ ((LinearFogRetained)this.retained).initBackDistance(backDistance);
+
+ }
+
+ /**
+ * Gets back distance for fog.
+ * @return the distance at which fog totally obscurs objects
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public double getBackDistance() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("LinearFog1"));
+
+ return ((LinearFogRetained)this.retained).getBackDistance();
+ }
+
+ /**
+ * Creates the retained mode LinearFogRetained object that this
+ * LinearFog node will point to.
+ */
+ void createRetained() {
+ this.retained = new LinearFogRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * 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) {
+ LinearFog lf = new LinearFog();
+ lf.duplicateNode(this, forceDuplicate);
+ return lf;
+ }
+
+
+ /**
+ * Copies all LinearFog 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ LinearFogRetained attr = (LinearFogRetained) originalNode.retained;
+ LinearFogRetained rt = (LinearFogRetained) retained;
+
+ rt.initFrontDistance(attr.getFrontDistance());
+ rt.initBackDistance(attr.getBackDistance());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/LinearFogRetained.java b/src/classes/share/javax/media/j3d/LinearFogRetained.java
new file mode 100644
index 0000000..f5b6204
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LinearFogRetained.java
@@ -0,0 +1,197 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+/**
+ * The LinearFog leaf node defines distance parameters for
+ * linear fog.
+ */
+class LinearFogRetained extends FogRetained {
+ /**
+ * Fog front and back distance
+ */
+ double frontDistance = 0.1;
+ double backDistance = 1.0;
+ double localToVworldScale = 1.0;
+ double frontDistanceInEc;
+ double backDistanceInEc;
+ double vworldToCoexistenceScale;
+
+ // dirty bits for LinearFog
+ static final int FRONT_DISTANCE_CHANGED = FogRetained.LAST_DEFINED_BIT << 1;
+ static final int BACK_DISTANCE_CHANGED = FogRetained.LAST_DEFINED_BIT << 2;
+
+ LinearFogRetained() {
+ this.nodeType = NodeRetained.LINEARFOG;
+ }
+
+ /**
+ * Initializes front distance for fog before the object is live
+ */
+ void initFrontDistance(double frontDistance){
+ this.frontDistance = frontDistance;
+ }
+
+ /**
+ * Sets front distance for fog and sends a message
+ */
+ void setFrontDistance(double frontDistance){
+ this.frontDistance = frontDistance;
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.FOG_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(FRONT_DISTANCE_CHANGED);
+ createMessage.args[2] = new Double(frontDistance);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ }
+
+ /**
+ * Gets front distance for fog
+ */
+ double getFrontDistance(){
+ return this.frontDistance;
+ }
+
+ /**
+ * Initializes back distance for fog
+ */
+ void initBackDistance(double backDistance){
+ this.backDistance = backDistance;
+ }
+ /**
+ * Sets back distance for fog
+ */
+ void setBackDistance(double backDistance){
+ this.backDistance = backDistance;
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.FOG_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(BACK_DISTANCE_CHANGED);
+ createMessage.args[2] = new Double(backDistance);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ /**
+ * Gets back distance for fog
+ */
+ double getBackDistance(){
+ return this.backDistance;
+ }
+ /**
+ * This method and its native counterpart update the native context
+ * fog values.
+ */
+ native void update(long ctx, float red, float green, float blue,
+ double fdist, double bdist);
+
+ void update(long ctx, double scale) {
+ validateDistancesInEc(scale);
+ update(ctx, color.x, color.y, color.z, frontDistanceInEc, backDistanceInEc);
+ }
+
+
+
+ void setLive(SetLiveState s) {
+ GroupRetained group;
+
+ super.setLive(s);
+
+ // Initialize the mirror object, this needs to be done, when
+ // renderBin is not accessing any of the fields
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.FOG_CHANGED;
+ createMessage.args[0] = this;
+ // a snapshot of all attributes that needs to be initialized
+ // in the mirror object
+ createMessage.args[1]= new Integer(INIT_MIRROR);
+ ArrayList addScopeList = new ArrayList();
+ for (int i = 0; i < scopes.size(); i++) {
+ group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.addAllNodesForScopedFog(mirrorFog, addScopeList, tempKey);
+ }
+ Object[] scopeInfo = new Object[2];
+ scopeInfo[0] = ((scopes.size() > 0) ? Boolean.TRUE:Boolean.FALSE);
+ scopeInfo[1] = addScopeList;
+ createMessage.args[2] = scopeInfo;
+ Color3f clr = new Color3f(color);
+ createMessage.args[3] = clr;
+
+ Object[] obj = new Object[6];
+ obj[0] = boundingLeaf;
+ obj[1] = (regionOfInfluence != null?regionOfInfluence.clone():null);
+ obj[2] = (inBackgroundGroup? Boolean.TRUE:Boolean.FALSE);
+ obj[3] = geometryBackground;
+ obj[4] = new Double(frontDistance);
+ obj[5] = new Double(backDistance);
+
+ createMessage.args[4] = obj;
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ }
+
+
+ // The update Object function.
+ // Note : if you add any more fields here , you need to update
+ // updateFog() in RenderingEnvironmentStructure
+ synchronized void updateMirrorObject(Object[] objs) {
+
+ int component = ((Integer)objs[1]).intValue();
+ Transform3D trans;
+
+ if ((component & FRONT_DISTANCE_CHANGED) != 0)
+ ((LinearFogRetained)mirrorFog).frontDistance = ((Double)objs[2]).doubleValue();
+ if ((component & BACK_DISTANCE_CHANGED) != 0)
+ ((LinearFogRetained)mirrorFog).backDistance = ((Double)objs[2]).doubleValue();
+ if ((component & INIT_MIRROR) != 0) {
+ ((LinearFogRetained)mirrorFog).frontDistance = ((Double)((Object[])objs[4])[4]).doubleValue();
+ ((LinearFogRetained)mirrorFog).backDistance = ((Double)((Object[])objs[4])[5]).doubleValue();
+
+ }
+ ((LinearFogRetained)mirrorFog).localToVworldScale =
+ getLastLocalToVworld().getDistanceScale();
+
+
+ super.updateMirrorObject(objs);
+ }
+
+ /**
+ * Scale distances from local to eye coordinate
+ */
+
+ void validateDistancesInEc(double vworldToCoexistenceScale) {
+ // vworldToCoexistenceScale can be used here since
+ // CoexistenceToEc has a unit scale
+ double localToEcScale = localToVworldScale * vworldToCoexistenceScale;
+
+ frontDistanceInEc = frontDistance * localToEcScale;
+ backDistanceInEc = backDistance * localToEcScale;
+ }
+
+ // Called on mirror object
+ void updateTransformChange() {
+ super.updateTransformChange();
+ localToVworldScale = sgFog.getLastLocalToVworld().getDistanceScale();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Link.java b/src/classes/share/javax/media/j3d/Link.java
new file mode 100644
index 0000000..089f15f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Link.java
@@ -0,0 +1,140 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A Link leaf node allows an application to reference a shared graph,
+ * rooted by a SharedGroup node, from within a branch graph or another
+ * shared graph.
+ * Any number of Link nodes can refer to the same SharedGroup node.
+ */
+
+public class Link extends Leaf {
+ /**
+ * For Link nodes, specifies that the node allows access to
+ * its object's SharedGroup information.
+ */
+ public static final int
+ ALLOW_SHARED_GROUP_READ = CapabilityBits.LINK_ALLOW_SHARED_GROUP_READ;
+
+ /**
+ * For Link nodes, specifies that the node allows writing
+ * its object's SharedGroup information.
+ */
+ public static final int
+ ALLOW_SHARED_GROUP_WRITE = CapabilityBits.LINK_ALLOW_SHARED_GROUP_WRITE;
+
+ /**
+ * Constructs a Link node object that does not yet point to a
+ * SharedGroup node.
+ */
+ public Link() {
+ }
+
+ /**
+ * Constructs a Link node object that points to the specified
+ * SharedGroup node.
+ * @param sharedGroup the SharedGroup node
+ */
+ public Link(SharedGroup sharedGroup) {
+ ((LinkRetained)this.retained).setSharedGroup(sharedGroup);
+ }
+
+ /**
+ * Creates the retained mode LinkRetained object that this
+ * Link object will point to.
+ */
+ void createRetained() {
+ this.retained = new LinkRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Sets the node's SharedGroup reference.
+ * @param sharedGroup the SharedGroup node to reference
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setSharedGroup(SharedGroup sharedGroup) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SHARED_GROUP_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Link0"));
+ ((LinkRetained)this.retained).setSharedGroup(sharedGroup);
+ }
+
+ /**
+ * Retrieves the node's SharedGroup reference.
+ * @return the SharedGroup node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public SharedGroup getSharedGroup() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SHARED_GROUP_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Link1"));
+ return ((LinkRetained)this.retained).getSharedGroup();
+ }
+
+ /**
+ * Used to create a new instance of the node. This routine is called
+ * by <code>cloneTree</code> to duplicate the current node.
+ * <br>
+ * The cloned Link node will refer to the same
+ * SharedGroup as the original node. The SharedGroup referred to by
+ * this Link node will not be cloned.
+ * @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) {
+ Link l = new Link();
+ l.duplicateNode(this, forceDuplicate);
+ return l;
+ }
+
+ /**
+ * Copies all Link 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+ ((LinkRetained) retained).setSharedGroup(
+ ((LinkRetained) originalNode.retained).getSharedGroup());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/LinkRetained.java b/src/classes/share/javax/media/j3d/LinkRetained.java
new file mode 100644
index 0000000..84410a7
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/LinkRetained.java
@@ -0,0 +1,330 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.*;
+
+/**
+ * A Link leaf node consisting of a reference to a SharedGroup node.
+ */
+
+class LinkRetained extends LeafRetained {
+ /**
+ * The SharedGroup component of the link node.
+ */
+ SharedGroupRetained sharedGroup;
+
+ static String plus = "+";
+
+ // This is used when setLive to check for cycle scene graph
+ boolean visited = false;
+
+ LinkRetained() {
+ this.nodeType = NodeRetained.LINK;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ /**
+ * Sets the SharedGroup reference.
+ * @param sharedGroup the SharedGroup node
+ */
+ void setSharedGroup(SharedGroup sharedGroup) {
+ // Note that it is possible that the sharedGroup pass
+ // in already link to another link and live.
+ HashKey newKeys[] = null;
+ boolean abort = false;
+
+ if (source.isLive()) {
+ // bug 4370407: if sharedGroup is a parent, then don't do anything
+ if (sharedGroup != null) {
+ synchronized(universe.sceneGraphLock) {
+ NodeRetained pa;
+ for (pa = parent; pa != null; pa = pa.parent) {
+ if (pa == (NodeRetained)sharedGroup.retained) {
+ abort = true;
+ throw new SceneGraphCycleException(J3dI18N.getString("LinkRetained1"));
+ }
+ }
+ }
+ if (abort)
+ return;
+ }
+
+ newKeys = getNewKeys(locale.nodeId, localToVworldKeys);
+
+ if (this.sharedGroup != null) {
+ ((GroupRetained) parent).checkClearLive(this.sharedGroup,
+ newKeys, true, null,
+ 0, 0, this);
+ this.sharedGroup.parents.removeElement(this);
+ }
+ }
+
+ if (sharedGroup != null) {
+ this.sharedGroup =
+ (SharedGroupRetained)sharedGroup.retained;
+ } else {
+ this.sharedGroup = null;
+ }
+
+ if (source.isLive() && (sharedGroup != null)) {
+
+ this.sharedGroup.parents.addElement(this);
+ visited = true;
+ try {
+ int ci = ((GroupRetained) parent).indexOfChild((Node)this.sharedGroup.source);
+ ((GroupRetained) parent).checkSetLive(this.sharedGroup, ci,
+ newKeys, true, null,
+ 0, this);
+ } catch (SceneGraphCycleException e) {
+ throw e;
+ } finally {
+ visited = false;
+ }
+ }
+
+ }
+
+ /**
+ * Retrieves the SharedGroup reference.
+ * @return the SharedGroup node
+ */
+ SharedGroup getSharedGroup() {
+ return (sharedGroup != null ?
+ (SharedGroup)this.sharedGroup.source : null);
+ }
+
+ void computeCombineBounds(Bounds bounds) {
+
+ if (boundsAutoCompute) {
+ sharedGroup.computeCombineBounds(bounds);
+ } else {
+ // Should this be lock too ? ( MT safe ? )
+ synchronized(localBounds) {
+ bounds.combine(localBounds);
+ }
+ }
+ }
+
+
+ /**
+ * Gets the bounding object of a node.
+ * @return the node's bounding object
+ */
+ Bounds getBounds() {
+ return (boundsAutoCompute ?
+ (Bounds)sharedGroup.getBounds().clone() :
+ super.getBounds());
+ }
+
+
+ /**
+ * assign a name to this node when it is made live.
+ */
+ void setLive(SetLiveState s) {
+
+ super.doSetLive(s);
+
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("LinkRetained0"));
+ }
+
+ if (nodeId == null) {
+ nodeId = universe.getNodeId();
+ }
+
+ if (sharedGroup != null) {
+ this.sharedGroup.parents.addElement(this);
+ HashKey newKeys[] = getNewKeys(s.locale.nodeId, s.keys);
+ HashKey oldKeys[] = s.keys;
+ s.keys = newKeys;
+ s.inSharedGroup = true;
+ if (visited) {
+ throw new SceneGraphCycleException(J3dI18N.getString("LinkRetained1"));
+ }
+ visited = true;
+ try {
+ this.sharedGroup.setLive(s);
+ } catch (SceneGraphCycleException e) {
+ throw e;
+ } finally {
+ visited = false;
+ }
+
+ s.inSharedGroup = inSharedGroup;
+ s.keys = oldKeys;
+
+ localBounds.setWithLock(this.sharedGroup.localBounds);
+ }
+
+ super.markAsLive();
+ }
+
+ void setNodeData(SetLiveState s) {
+
+ super.setNodeData(s);
+
+ // add this node to parentTransformLink's childTransformLink
+ if (s.childTransformLinks != null) {
+ // do not duplicate shared nodes
+ synchronized(s.childTransformLinks) {
+ if(!inSharedGroup || !s.childTransformLinks.contains(this)) {
+ s.childTransformLinks.add(this);
+ }
+ }
+ }
+
+ // add this node to parentSwitchLink's childSwitchLink
+ if (s.childSwitchLinks != null) {
+ if(!inSharedGroup ||
+ // only add if not already added in sharedGroup
+ !s.childSwitchLinks.contains(this)) {
+ s.childSwitchLinks.add(this);
+ }
+ }
+ }
+
+
+ void recombineAbove() {
+ localBounds.setWithLock(sharedGroup.localBounds);
+ parent.recombineAbove();
+ }
+
+ /**
+ * assign a name to this node when it is made live.
+ */
+ void clearLive(SetLiveState s) {
+
+ if (sharedGroup != null) {
+ HashKey newKeys[] = getNewKeys(s.locale.nodeId, s.keys);
+ super.clearLive(s);
+ HashKey oldKeys[] = s.keys;
+ s.keys = newKeys;
+ s.inSharedGroup = true;
+ this.sharedGroup.parents.removeElement(this);
+ this.sharedGroup.clearLive(s);
+ s.inSharedGroup = inSharedGroup;
+ s.keys = oldKeys;
+ } else {
+ super.clearLive(s);
+ }
+ }
+
+ void removeNodeData(SetLiveState s) {
+ if(refCount <= 0) {
+ // either not in sharedGroup or last instance in sharedGroup
+ // remove this node from parentTransformLink's childTransformLink
+ if (parentTransformLink != null) {
+ ArrayList obj;
+ if (parentTransformLink instanceof TransformGroupRetained) {
+ obj = ((TransformGroupRetained)
+ parentTransformLink).childTransformLinks;
+ } else {
+ obj = ((SharedGroupRetained)
+ parentTransformLink).childTransformLinks;
+ }
+ synchronized(obj) {
+ obj.remove(this);
+ }
+ }
+
+ // remove this node from parentSwitchLink's childSwitchLink
+ if (parentSwitchLink != null) {
+ ArrayList switchLinks;
+ for(int i=0; i<parentSwitchLink.childrenSwitchLinks.size();i++){
+ switchLinks = (ArrayList)
+ parentSwitchLink.childrenSwitchLinks.get(i);
+ if (switchLinks.contains(this)) {
+ switchLinks.remove(this);
+ break;
+ }
+ }
+ }
+ }
+ super.removeNodeData(s);
+ }
+
+
+ void updatePickable(HashKey keys[], boolean pick[]) {
+ super.updatePickable(keys, pick);
+
+ if (sharedGroup != null) {
+ HashKey newKeys[] = getNewKeys(locale.nodeId, keys);
+ sharedGroup.updatePickable(newKeys, pick);
+ }
+ }
+
+ void updateCollidable(HashKey keys[], boolean collide[]) {
+ super.updateCollidable(keys, collide);
+
+ if (sharedGroup != null) {
+ HashKey newKeys[] = getNewKeys(locale.nodeId, keys);
+ sharedGroup.updateCollidable(newKeys, collide);
+ }
+ }
+
+ void setBoundsAutoCompute(boolean autoCompute) {
+ super.setBoundsAutoCompute(autoCompute);
+ if (!autoCompute) {
+ localBounds = getBounds();
+ }
+ }
+
+ void setCompiled() {
+ super.setCompiled();
+ if (sharedGroup != null) {
+ sharedGroup.setCompiled();
+ }
+ }
+
+ void compile(CompileState compState) {
+
+ super.compile(compState);
+
+ // TODO: for now keep the static transform in the parent tg
+ compState.keepTG = true;
+
+ // don't remove this group node
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numLinks++;
+ }
+ }
+
+ HashKey[] getNewKeys(String localeNodeId, HashKey oldKeys[]) {
+ HashKey newKeys[];
+
+ if (!inSharedGroup) {
+ newKeys = new HashKey[1];
+ newKeys[0] = new HashKey(localeNodeId);
+ newKeys[0].append(plus + nodeId);
+ } else {
+ // Need to append this link node id to all keys passed in.
+ newKeys = new HashKey[oldKeys.length];
+ for (int i=oldKeys.length-1; i>=0; i--) {
+ newKeys[i] = new HashKey(oldKeys[i].toString()
+ + plus + nodeId);
+ }
+ }
+ return newKeys;
+ }
+
+ void searchGeometryAtoms(UnorderList list) {
+ if (sharedGroup != null) {
+ sharedGroup.searchGeometryAtoms(list);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Locale.java b/src/classes/share/javax/media/j3d/Locale.java
new file mode 100644
index 0000000..68a1cdc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Locale.java
@@ -0,0 +1,656 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.ArrayList;
+
+/**
+ * A Locale object defines a high-resolution position within a
+ * VirtualUniverse, and serves as a container for a collection of
+ * BranchGroup-rooted subgraphs (branch graphs), at that position.
+ * Objects within a Locale are defined using standard double-precision
+ * coordinates, relative to the origin of the Locale. This origin
+ * defines the Virtual World coordinate system for that Locale.
+ * <p>
+ * A Locale object defines methods to set and get its high-resolution
+ * coordinates, and methods to add, remove, and enumerate the branch
+ * graphs.
+ *
+ * @see VirtualUniverse
+ * @see HiResCoord
+ * @see BranchGroup
+ */
+
+public class Locale extends Object {
+
+ /**
+ * The virtual universe that this Locale object is contained within.
+ */
+ VirtualUniverse universe;
+
+ /**
+ * The high resolution coordinate associated with this Locale object.
+ */
+ HiResCoord hiRes;
+
+ /**
+ * List of BranchGroup objects included in this Locale
+ */
+ Vector branchGroups = new Vector();
+
+ // locale's identifier
+ String nodeId = null;
+
+ /**
+ * Constructs and initializes a new high resolution Locale object
+ * located at (0, 0, 0).
+ * @param universe the virtual universe that will contain this
+ * Locale object
+ */
+ public Locale(VirtualUniverse universe) {
+ this.universe = universe;
+ this.universe.addLocale(this);
+ this.hiRes = new HiResCoord();
+ nodeId = universe.getNodeId();
+ }
+
+ /**
+ * Constructs and initializes a new high resolution Locale object
+ * from the parameters provided.
+ * @param universe the virtual universe that will contain this
+ * Locale object
+ * @param x an eight element array specifying the x position
+ * @param y an eight element array specifying the y position
+ * @param z an eight element array specifying the z position
+ */
+ public Locale(VirtualUniverse universe, int[] x, int[] y, int[] z) {
+ this.universe = universe;
+ this.universe.addLocale(this);
+ this.hiRes = new HiResCoord(x, y, z);
+ nodeId = universe.getNodeId();
+ }
+
+ /**
+ * Constructs and initializes a new high resolution Locale object
+ * at the location specified by the HiResCoord argument.
+ * @param universe the virtual universe that will contain this
+ * Locale object
+ * @param hiRes the HiRes coordinate to use in creating this Locale
+ */
+ public Locale(VirtualUniverse universe, HiResCoord hiRes) {
+ this.universe = universe;
+ this.universe.addLocale(this);
+ this.hiRes = new HiResCoord(hiRes);
+ nodeId = universe.getNodeId();
+ }
+
+ /**
+ * Retrieves the virtual universe within which this Locale object
+ * is contained. A null reference indicates that this
+ * Locale has been removed from its VirtualUniverse.
+ * @return the virtual universe within which this Locale object
+ * is contained.
+ */
+ public VirtualUniverse getVirtualUniverse() {
+ return universe;
+ }
+
+ /**
+ * Sets the HiRes coordinate of this Locale to the location
+ * specified by the parameters provided.
+ * @param x an eight element array specifying the x position
+ * @param y an eight element array specifying the y position
+ * @param z an eight element array specifying the z position
+ */
+ public void setHiRes(int[] x, int[] y, int[] z) {
+ this.hiRes.setHiResCoord(x, y, z);
+ }
+
+ /**
+ * Sets the HiRes coordinate of this Locale
+ * to the location specified by the HiRes argument.
+ * @param hiRes the HiRes coordinate specifying this node's new location
+ */
+ public void setHiRes(HiResCoord hiRes) {
+ this.hiRes.setHiResCoord(hiRes);
+ }
+
+ /**
+ * Returns this node's HiResCoord.
+ * @param hiRes a HiResCoord object that will receive the
+ * HiRes coordinate of this Locale node
+ */
+ public void getHiRes(HiResCoord hiRes) {
+ this.hiRes.getHiResCoord(hiRes);
+ }
+
+ /**
+ * Add a new branch graph rooted at BranchGroup to
+ * the list of branch graphs.
+ * @param branchGroup root of the branch graph to be added
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ * @exception MultipleParentException if the specified BranchGroup node
+ * is already live.
+ */
+ public void addBranchGraph(BranchGroup branchGroup){
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ // if the BranchGroup already has a parent, or has already been
+ // added to a locale, throw MultipleParentException
+ if ((((BranchGroupRetained)branchGroup.retained).parent != null) ||
+ (branchGroup.isLive())) {
+ throw new MultipleParentException(J3dI18N.getString("Locale0"));
+ }
+
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doAddBranchGraph(branchGroup);
+ universe.setLiveState.reset(this);
+ }
+ universe.waitForMC();
+ }
+
+ // The method that does the work once the lock is acquired.
+ void doAddBranchGraph(BranchGroup branchGroup) {
+ BranchGroupRetained bgr = (BranchGroupRetained)branchGroup.retained;
+ J3dMessage createMessage;
+ SetLiveState s = universe.setLiveState;
+
+ // bgr.setLocale(this);
+
+ // addElement needs to precede setLive or else any liveness checks
+ // in the initialize() call of a user behavior (ie, calling collision
+ // or picking constructor with a SceneGraphPath) will fail
+ // when SceneGraphPath.validate() attempts to verify that
+ // the proper Locale is associated with that SceneGraphPath
+ bgr.attachedToLocale = true;
+ branchGroups.addElement(branchGroup);
+ s.reset(this);
+ s.currentTransforms[0] = new Transform3D[2];
+ s.currentTransforms[0][0] = new Transform3D();
+ s.currentTransforms[0][1] = new Transform3D();
+ s.currentTransformsIndex[0] = new int[2];
+ s.currentTransformsIndex[0][0] = 0;
+ s.currentTransformsIndex[0][1] = 0;
+
+ s.localToVworld = s.currentTransforms;
+ s.localToVworldIndex = s.currentTransformsIndex;
+
+ s.branchGroupPaths = new ArrayList();
+ s.branchGroupPaths.add(new BranchGroupRetained[0]);
+
+ s.orderedPaths = new ArrayList(1);
+ s.orderedPaths.add(new OrderedPath());
+
+ s.switchStates = new ArrayList(1);
+ s.switchStates.add(new SwitchState(false));
+
+ bgr.setLive(s);
+
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER| J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.type = J3dMessage.ORDERED_GROUP_INSERTED;
+ createMessage.universe = universe;
+ createMessage.args[0] = s.ogList.toArray();
+ createMessage.args[1] = s.ogChildIdList.toArray();
+ createMessage.args[2] = s.ogOrderedIdList.toArray();
+ createMessage.args[3] = s.ogCIOList.toArray();
+ createMessage.args[4] = s.ogCIOTableList.toArray();
+
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.type = J3dMessage.VIEWSPECIFICGROUP_INIT;
+ createMessage.universe = universe;
+ createMessage.args[0] = s.changedViewGroup;
+ createMessage.args[1] = s.changedViewList;
+ createMessage.args[2] = s.keyList;
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = s.notifyThreads;
+ createMessage.type = J3dMessage.INSERT_NODES;
+ createMessage.universe = universe;
+ createMessage.args[0] = s.nodeList.toArray();
+ createMessage.args[1] = null;
+ createMessage.args[2] = null;
+ if (s.viewScopedNodeList != null) {
+ createMessage.args[3] = s.viewScopedNodeList;
+ createMessage.args[4] = s.scopedNodesViewList;
+ }
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ int sz = s.behaviorNodes.size();
+ for (int i=0; i< sz; i++) {
+ BehaviorRetained b;
+ b = (BehaviorRetained)s.behaviorNodes.get(i);
+ b.executeInitialize();
+ }
+
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_BEHAVIOR;
+ createMessage.type = J3dMessage.BEHAVIOR_ACTIVATE;
+ createMessage.universe = universe;
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ // Free up memory.
+ s.reset(null);
+ }
+
+ /**
+ * Removes a branch graph rooted at BranchGroup from
+ * the list of branch graphs.
+ * @param branchGroup root of the branch graph to be removed
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ * @exception CapabilityNotSetException if the ALLOW_DETACH capability is
+ * not set in the specified BranchGroup node.
+ */
+ public void removeBranchGraph(BranchGroup branchGroup){
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ if (! branchGroup.getCapability(BranchGroup.ALLOW_DETACH)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Locale1"));
+ }
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doRemoveBranchGraph(branchGroup, null, 0);
+ universe.setLiveState.reset(this);
+ }
+ universe.waitForMC();
+ }
+
+
+ // Method to remove all branch graphs from this Locale and remove
+ // this Locale from the VirtualUniverse
+ void removeFromUniverse() {
+ if (branchGroups.size() > 0) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ // Make a copy of the branchGroups list so that we can safely
+ // iterate over it.
+ Object[] bg = branchGroups.toArray();
+ for (int i = 0; i < bg.length; i++) {
+ doRemoveBranchGraph((BranchGroup)bg[i], null, 0);
+ }
+ }
+ // Put after sceneGraphLock to prevent deadlock
+ universe.waitForMC();
+ }
+
+ // free nodeId
+ if (nodeId != null) {
+ universe.nodeIdFreeList.addElement(nodeId);
+ nodeId = null;
+ }
+
+ // Set universe pointer to null, indicating that this Locale
+ // has been removed from its universe
+ universe = null;
+ }
+
+
+ // The method that does the work once the lock is acquired.
+ void doRemoveBranchGraph(BranchGroup branchGroup,
+ J3dMessage messages[], int startIndex) {
+
+ BranchGroupRetained bgr = (BranchGroupRetained)branchGroup.retained;
+ J3dMessage destroyMessage;
+
+ if (!branchGroup.isLive())
+ return;
+ bgr.attachedToLocale = false;
+ branchGroups.removeElement(branchGroup);
+ universe.setLiveState.reset(this);
+ bgr.clearLive(universe.setLiveState);
+ bgr.setParent(null);
+ bgr.setLocale(null);
+
+ if (messages == null) {
+ destroyMessage = VirtualUniverse.mc.getMessage();
+ } else {
+ destroyMessage = messages[startIndex++];
+ }
+ destroyMessage.threads = J3dThread.UPDATE_RENDER| J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ destroyMessage.type = J3dMessage.ORDERED_GROUP_REMOVED;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = universe.setLiveState.ogList.toArray();
+ destroyMessage.args[1] = universe.setLiveState.ogChildIdList.toArray();
+ destroyMessage.args[3] = universe.setLiveState.ogCIOList.toArray();
+ destroyMessage.args[4] = universe.setLiveState.ogCIOTableList.toArray();
+
+ if (messages == null) {
+ VirtualUniverse.mc.processMessage(destroyMessage);
+ destroyMessage = VirtualUniverse.mc.getMessage();
+ } else {
+ destroyMessage = messages[startIndex++];
+ }
+ destroyMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ destroyMessage.type = J3dMessage.VIEWSPECIFICGROUP_CLEAR;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = universe.setLiveState.changedViewGroup;
+ destroyMessage.args[1] = universe.setLiveState.keyList;
+
+ if (messages == null) {
+ VirtualUniverse.mc.processMessage(destroyMessage);
+ destroyMessage = VirtualUniverse.mc.getMessage();
+ } else {
+ destroyMessage = messages[startIndex++];
+ }
+
+ destroyMessage.threads = universe.setLiveState.notifyThreads;
+ destroyMessage.type = J3dMessage.REMOVE_NODES;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = universe.setLiveState.nodeList.toArray();
+ if (universe.setLiveState.viewScopedNodeList != null) {
+ destroyMessage.args[3] = universe.setLiveState.viewScopedNodeList;
+ destroyMessage.args[4] = universe.setLiveState.scopedNodesViewList;
+ }
+ if (messages == null) {
+ VirtualUniverse.mc.processMessage(destroyMessage);
+ } else {
+ destroyMessage = messages[startIndex++];
+ }
+
+ if (universe.isEmpty()) {
+ VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE,
+ universe);
+ }
+ universe.setLiveState.reset(null); // cleanup memory
+ }
+
+ /**
+ * Replaces the branch graph rooted at oldGroup in the list of
+ * branch graphs with the branch graph rooted at
+ * newGroup.
+ * @param oldGroup root of the branch graph to be replaced.
+ * @param newGroup root of the branch graph that will replace the old
+ * branch graph.
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ * @exception CapabilityNotSetException if the ALLOW_DETACH capability is
+ * not set in the old BranchGroup node.
+ * @exception MultipleParentException if the new BranchGroup node
+ * is already live.
+ */
+ public void replaceBranchGraph(BranchGroup oldGroup,
+ BranchGroup newGroup){
+
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ if (! oldGroup.getCapability(BranchGroup.ALLOW_DETACH)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Locale1"));
+ }
+
+ if (((BranchGroupRetained)newGroup.retained).parent != null) {
+ throw new MultipleParentException(J3dI18N.getString("Locale3"));
+ }
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ doReplaceBranchGraph(oldGroup, newGroup);
+ universe.setLiveState.reset(this);
+ }
+ universe.waitForMC();
+ }
+
+ // The method that does the work once the lock is acquired.
+ void doReplaceBranchGraph(BranchGroup oldGroup,
+ BranchGroup newGroup){
+ BranchGroupRetained obgr = (BranchGroupRetained)oldGroup.retained;
+ BranchGroupRetained nbgr = (BranchGroupRetained)newGroup.retained;
+ J3dMessage createMessage;
+ J3dMessage destroyMessage;
+
+
+ branchGroups.removeElement(oldGroup);
+ obgr.attachedToLocale = false;
+ universe.setLiveState.reset(this);
+ obgr.clearLive(universe.setLiveState);
+
+ destroyMessage = VirtualUniverse.mc.getMessage();
+
+ destroyMessage.threads = J3dThread.UPDATE_RENDER| J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ destroyMessage.type = J3dMessage.ORDERED_GROUP_REMOVED;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = universe.setLiveState.ogList.toArray();
+ destroyMessage.args[1] = universe.setLiveState.ogChildIdList.toArray();
+ destroyMessage.args[3] = universe.setLiveState.ogCIOList.toArray();
+ destroyMessage.args[4] = universe.setLiveState.ogCIOTableList.toArray();
+ VirtualUniverse.mc.processMessage(destroyMessage);
+
+ destroyMessage = VirtualUniverse.mc.getMessage();
+ destroyMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ destroyMessage.type = J3dMessage.VIEWSPECIFICGROUP_CLEAR;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = universe.setLiveState.changedViewGroup;
+ destroyMessage.args[1] = universe.setLiveState.keyList;
+ VirtualUniverse.mc.processMessage(destroyMessage);
+
+
+ destroyMessage = VirtualUniverse.mc.getMessage();
+ destroyMessage.threads = universe.setLiveState.notifyThreads;
+ destroyMessage.type = J3dMessage.REMOVE_NODES;
+ destroyMessage.universe = universe;
+ destroyMessage.args[0] = universe.setLiveState.nodeList.toArray();
+ VirtualUniverse.mc.processMessage(destroyMessage);
+
+ branchGroups.addElement(newGroup);
+ nbgr.attachedToLocale = true;
+ universe.setLiveState.reset(this);
+ universe.setLiveState.currentTransforms[0] = new Transform3D[2];
+ universe.setLiveState.currentTransforms[0][0] = new Transform3D();
+ universe.setLiveState.currentTransforms[0][1] = new Transform3D();
+ universe.setLiveState.currentTransformsIndex[0] = new int[2];
+ universe.setLiveState.currentTransformsIndex[0][0] = 0;
+ universe.setLiveState.currentTransformsIndex[0][1] = 0;
+
+ universe.setLiveState.localToVworld =
+ universe.setLiveState.currentTransforms;
+ universe.setLiveState.localToVworldIndex =
+ universe.setLiveState.currentTransformsIndex;
+
+ universe.setLiveState.branchGroupPaths = new ArrayList();
+ universe.setLiveState.branchGroupPaths.add(new BranchGroupRetained[0]);
+
+ universe.setLiveState.orderedPaths = new ArrayList(1);
+ universe.setLiveState.orderedPaths.add(new OrderedPath());
+
+ universe.setLiveState.switchStates = new ArrayList(1);
+ universe.setLiveState.switchStates.add(new SwitchState(false));
+
+ nbgr.setLive(universe.setLiveState);
+
+
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER| J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.type = J3dMessage.ORDERED_GROUP_INSERTED;
+ createMessage.universe = universe;
+ createMessage.args[0] = universe.setLiveState.ogList.toArray();
+ createMessage.args[1] = universe.setLiveState.ogChildIdList.toArray();
+ createMessage.args[2] = universe.setLiveState.ogOrderedIdList.toArray();
+ createMessage.args[3] = universe.setLiveState.ogCIOList.toArray();
+ createMessage.args[4] = universe.setLiveState.ogCIOTableList.toArray();
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ // TODO: make these two into one message
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = universe.setLiveState.notifyThreads;
+ createMessage.type = J3dMessage.INSERT_NODES;
+ createMessage.universe = universe;
+ createMessage.args[0] = universe.setLiveState.nodeList.toArray();
+ createMessage.args[1] = null;
+ createMessage.args[2] = null;
+ if (universe.setLiveState.viewScopedNodeList != null) {
+ createMessage.args[3] = universe.setLiveState.viewScopedNodeList;
+ createMessage.args[4] = universe.setLiveState.scopedNodesViewList;
+ }
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ Object behaviorNodes[] = universe.setLiveState.behaviorNodes.toArray();
+
+ if (universe.isEmpty()) {
+ VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE,
+ universe);
+ }
+
+ for (int i=0; i< behaviorNodes.length; i++) {
+ ((BehaviorRetained) behaviorNodes[i]).executeInitialize();
+ }
+
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_BEHAVIOR;
+ createMessage.type = J3dMessage.BEHAVIOR_ACTIVATE;
+ createMessage.universe = universe;
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // Free up memory.
+ universe.setLiveState.reset(null);
+
+
+ }
+
+ /**
+ * Get number of branch graphs in this Locale.
+ * @return number of branch graphs in this Locale.
+ */
+ public int numBranchGraphs(){
+ return branchGroups.size();
+ }
+
+ /**
+ * Gets an Enumeration object of all branch graphs in this Locale.
+ * @return an Enumeration object of all branch graphs.
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ */
+ public Enumeration getAllBranchGraphs(){
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ return branchGroups.elements();
+ }
+
+ /**
+ * Returns an array referencing all the items that are pickable below this
+ * <code>Locale</code> that intersect with PickShape.
+ * The resultant array is unordered.
+ *
+ * @param pickShape the description of this picking volume or area.
+ *
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ *
+ * @see BranchGroup#pickAll
+ */
+ public SceneGraphPath[] pickAll( PickShape pickShape ) {
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ return Picking.pickAll( this, pickShape );
+ }
+
+
+ /**
+ * Returns a sorted array of references to all the Pickable items
+ * that intersect with the pickShape. Element [0] references the
+ * item closest to <i>origin</i> of PickShape successive array
+ * elements are further from the <i>origin</i>
+ * <br>
+ * NOTE: If pickShape is of type PickBounds, the resulting array
+ * is unordered.
+ *
+ * @param pickShape the description of this picking volume or area.
+ *
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ *
+ * @see BranchGroup#pickAllSorted
+ */
+ public SceneGraphPath[] pickAllSorted( PickShape pickShape ) {
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ return Picking.pickAllSorted( this, pickShape );
+ }
+
+
+ /**
+ * Returns a SceneGraphPath which references the pickable item
+ * which is closest to the origin of <code>pickShape</code>.
+ * <br>
+ * NOTE: If pickShape is of type PickBounds, the return is any
+ * pickable node below this Locale.
+ *
+ * @param pickShape the description of this picking volume or area.
+ *
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ *
+ * @see BranchGroup#pickClosest
+ */
+ public SceneGraphPath pickClosest( PickShape pickShape ) {
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ return Picking.pickClosest( this, pickShape );
+ }
+
+
+ /**
+ * Returns a reference to any item that is Pickable below this
+ * Locale which intersects with <code>pickShape</code>.
+ *
+ * @param pickShape the description of this picking volume or area.
+ *
+ * @exception IllegalStateException if this Locale has been
+ * removed from its VirtualUniverse.
+ *
+ * @see BranchGroup#pickAny
+ */
+ public SceneGraphPath pickAny( PickShape pickShape ) {
+ if (universe == null) {
+ throw new IllegalStateException(J3dI18N.getString("Locale4"));
+ }
+
+ return Picking.pickAny( this, pickShape );
+ }
+
+
+ /**
+ * Cleans up resources associated with this Locale
+ */
+ protected void finalize() {
+ // free nodeId
+ if (universe != null && nodeId != null) {
+ universe.nodeIdFreeList.addElement(nodeId);
+ nodeId = null;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/MRSWLock.java b/src/classes/share/javax/media/j3d/MRSWLock.java
new file mode 100644
index 0000000..8c35cb3
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MRSWLock.java
@@ -0,0 +1,74 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Use this lock to allow multiple reads/single write synchronization.
+ * To prevent deadlock a read/writeLock call must match with a read/writeUnlock call.
+ * Write request has precedence over read request.
+ */
+
+class MRSWLock {
+
+ static boolean debug = false;
+
+ private int readCount;
+ private boolean write;
+ private int writeRequested;
+ private int lockRequested;
+
+ MRSWLock() {
+ readCount = 0;
+ write = false;
+ writeRequested = 0;
+ lockRequested = 0;
+ }
+
+ synchronized final void readLock() {
+ lockRequested++;
+ while((write == true) || (writeRequested > 0)) {
+ try { wait(); } catch(InterruptedException e){}
+ }
+ lockRequested--;
+ readCount++;
+ }
+
+ synchronized final void readUnlock() {
+ if(readCount>0)
+ readCount--;
+ else
+ if(debug) System.out.println("ReadWriteLock.java : Problem! readCount is >= 0.");
+
+ if(lockRequested>0)
+ notifyAll();
+ }
+
+ synchronized final void writeLock() {
+ lockRequested++;
+ writeRequested++;
+ while((readCount>0)||(write == true)) {
+ try { wait(); } catch(InterruptedException e){}
+ }
+ write = true;
+ lockRequested--;
+ writeRequested--;
+ }
+
+ synchronized final void writeUnlock() {
+ write = false;
+
+ if(lockRequested>0)
+ notifyAll();
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/MasterControl.java b/src/classes/share/javax/media/j3d/MasterControl.java
new file mode 100644
index 0000000..5d92ff0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MasterControl.java
@@ -0,0 +1,3702 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * Portions of this code were derived from work done by the Blackdown
+ * group (www.blackdown.org), who did the initial Linux implementation
+ * of the Java 3D API.
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import java.awt.*;
+
+class MasterControl {
+
+ /**
+ * Options for the runMonitor
+ */
+ static final int CHECK_FOR_WORK = 0;
+ static final int SET_WORK = 1;
+ static final int RUN_THREADS = 2;
+ static final int THREAD_DONE = 3;
+ static final int WAIT_FOR_ALL = 4;
+ static final int SET_WORK_FOR_REQUEST_RENDERER = 5;
+ static final int RUN_RENDERER_CLEANUP = 6;
+ static final int SLEEP = 7;
+
+ // The thread states for MC
+ static final int SLEEPING = 0;
+ static final int RUNNING = 1;
+ static final int WAITING_FOR_THREAD = 2;
+ static final int WAITING_FOR_THREADS = 3;
+ static final int WAITING_FOR_CPU = 4;
+ static final int WAITING_FOR_RENDERER_CLEANUP = 5;
+
+ // The Rendering API's that we currently know about
+ static final int RENDER_OPENGL_SOLARIS = 0;
+ static final int RENDER_OPENGL_WIN32 = 1;
+ static final int RENDER_DIRECT3D = 2;
+ static final int RENDER_OPENGL_LINUX = 3;
+
+ // Constants used in renderer thread argument
+ static final Integer REQUESTRENDER = new Integer(Renderer.REQUESTRENDER);
+ static final Integer RENDER = new Integer(Renderer.RENDER);
+ static final Integer SWAP = new Integer(Renderer.SWAP);
+
+ // Constants used for request from user threads
+ static final Integer ACTIVATE_VIEW = new Integer(1);
+ static final Integer DEACTIVATE_VIEW = new Integer(2);
+ static final Integer START_VIEW = new Integer(3);
+ static final Integer STOP_VIEW = new Integer(4);
+ static final Integer REEVALUATE_CANVAS = new Integer(5);
+ static final Integer UNREGISTER_VIEW = new Integer(6);
+ static final Integer PHYSICAL_ENV_CHANGE = new Integer(7);
+ static final Integer INPUTDEVICE_CHANGE = new Integer(8);
+ static final Integer EMPTY_UNIVERSE = new Integer(9);
+ static final Integer START_RENDERER = new Integer(10);
+ static final Integer STOP_RENDERER = new Integer(11);
+ static final Integer RENDER_ONCE = new Integer(12);
+ static final Integer FREE_CONTEXT = new Integer(13);
+ static final Integer FREE_DRAWING_SURFACE = new Integer(14);
+ static final Integer FREE_MESSAGE = new Integer(15);
+ static final Integer RESET_CANVAS = new Integer(16);
+ static final Integer GETBESTCONFIG = new Integer(17);
+ static final Integer ISCONFIGSUPPORT = new Integer(18);
+ static final Integer SET_GRAPHICSCONFIG_FEATURES = new Integer(19);
+ static final Integer SET_QUERYPROPERTIES = new Integer(20);
+ static final Integer SET_VIEW = new Integer(21);
+
+ /**
+ * reference to MasterControl thread
+ */
+ private MasterControlThread mcThread = null;
+
+ /**
+ * The list of views that are currently registered
+ */
+ private UnorderList views = new UnorderList(1, View.class);
+
+ /**
+ * the flag to indicate whether the geometry should be locked or not
+ */
+
+ private boolean lockGeometry = false;
+
+ /**
+ * The number of registered views that are active
+ */
+ private int numActiveViews = 0;
+
+ // A freelist for ImageComponentUpdateInfo
+ private ImageComponentUpdateInfo[] imageUpdateInfoList =
+ new ImageComponentUpdateInfo[2];
+ private int numFreeImageUpdateInfo = 0;
+
+
+ /**
+ * The list of active universes get from View
+ */
+ private UnorderList activeUniverseList = new UnorderList(VirtualUniverse.class);
+
+ /**
+ * The list of universes register from View
+ */
+ private UnorderList regUniverseList = new UnorderList(VirtualUniverse.class);
+
+ /**
+ * A lock used for accessing time structures.
+ */
+ private Object timeLock = new Object();
+
+
+ /**
+ * The current "time" value
+ */
+ private long time = 0;
+
+ /**
+ * Use to assign threadOpts in Renderer thread.
+ */
+ private long waitTimestamp = 0;
+
+ /**
+ * The current list of work threads
+ */
+ private UnorderList stateWorkThreads =
+ new UnorderList(J3dThreadData.class);
+ private UnorderList renderWorkThreads =
+ new UnorderList(J3dThreadData.class);
+ private UnorderList requestRenderWorkThreads =
+ new UnorderList(J3dThreadData.class);
+
+ /**
+ * The current list of work threads
+ */
+ private UnorderList renderThreadData = new UnorderList(J3dThreadData.class);
+
+ /**
+ * The list of input device scheduler thread
+ */
+ private UnorderList inputDeviceThreads =
+ new UnorderList(1, InputDeviceScheduler.class);
+
+ /**
+ * A flag that is true when the thread lists need updating
+ */
+ private boolean threadListsChanged;
+
+
+ /**
+ * Markers for the last transform structure update thread
+ * and the last update thread.
+ */
+ private int lastTransformStructureThread = 0;
+ private int lastStructureUpdateThread = 0;
+
+ /**
+ * The current time snapshots
+ */
+ private long currentTime;
+
+ // Only one Timer thread in the system.
+ TimerThread timerThread;
+
+ /**
+ * This flag indicates that MC is running
+ */
+ volatile boolean running = true;
+
+ /**
+ * This flag indicates that MC has work to do
+ */
+ private boolean workToDo = false;
+
+ /**
+ * This flag indicates that there is work for requestRenderer
+ */
+ private boolean requestRenderWorkToDo = false;
+
+ /**
+ * The number of THREAD_DONE messages pending
+ */
+ private int threadPending = 0;
+ private int renderPending = 0;
+ private int statePending = 0;
+
+ /**
+ * State variables for work lists
+ */
+ private boolean renderWaiting = false;
+ private boolean stateWaiting = false;
+
+ /**
+ * The current state of the MC thread
+ */
+ private int state = SLEEPING;
+
+ // time for sleep in order to met the minimum frame duration
+ private long sleepTime = 0;
+
+
+ /**
+ * The number of cpu's Java 3D may use
+ */
+ private int cpuLimit;
+
+ /**
+ * A list of mirror objects to be updated
+ */
+ private UnorderList mirrorObjects = new UnorderList(ObjectUpdate.class);
+
+ /**
+ * The renderingAttributesStructure for updating node component
+ * objects
+ */
+ private RenderingAttributesStructure renderingAttributesStructure =
+ new RenderingAttributesStructure();
+
+ /**
+ * The default rendering method
+ */
+ private DefaultRenderMethod defaultRenderMethod = null;
+
+ /**
+ * The text3D rendering method
+ */
+ private Text3DRenderMethod text3DRenderMethod = null;
+
+ /**
+ * The vertex array rendering method
+ */
+ private VertexArrayRenderMethod vertexArrayRenderMethod = null;
+
+ /**
+ * The displayList rendering method
+ */
+ private DisplayListRenderMethod displayListRenderMethod = null;
+
+ /**
+ * The compressed geometry rendering method
+ */
+ private CompressedGeometryRenderMethod compressedGeometryRenderMethod = null;
+
+ /**
+ * The oriented shape3D rendering method
+ */
+ private OrientedShape3DRenderMethod orientedShape3DRenderMethod = null;
+
+ /**
+ * This is the start time upon which alpha's and behaviors
+ * are synchronized to.
+ */
+ static long systemStartTime = System.currentTimeMillis();
+
+ // The rendering API we are using
+ private int renderingAPI = RENDER_OPENGL_SOLARIS;
+ static boolean isD3DAPI = false;
+
+ // Are we on a Win32 system
+ static boolean isWin32 = false;
+
+ // The class that describes the low level rendering code
+ private NativeAPIInfo nativeAPIInfo = null;
+
+ // This is a counter for texture id's, valid id starts from 1
+ private int textureIdCount = 0;
+
+ // This is lock for both 2D/3D textureIds;
+ private Object textureIdLock = new Object();
+
+ // This is a time stamp used when context is created
+ private long contextTimeStamp = 0;
+
+ // This is a counter for canvas bit
+ private int canvasBitCount = 0;
+
+ // This is a counter for rendererBit
+ private int rendererCount = 0;
+
+ /*
+ // Flag that indicates whether the JVM is version JDK1.5 or later.
+ // If so, then the jvm15OrBetter flag is set to true, indicating that
+ // 1.5 functionality can be used.
+ // We don't use any JDK 1.5 features yet, so this is a placeholder.
+ static boolean jvm15OrBetter = false;
+ */
+
+ // Flag that indicates whether to shared display context or not
+ boolean isSharedCtx = false;
+ boolean sharedCtxOverride = false;
+
+ // Flag that tells us to use NV_register_combiners
+ boolean useCombiners = false;
+
+ // Flag that indicates whether compile is disabled or not
+ boolean disableCompile = false;
+
+ // Flag that indicates whether or not compaction occurs
+ boolean doCompaction = true;
+
+ // Flag that indicates whether separate specular color is disabled or not
+ boolean disableSeparateSpecularColor = false;
+
+ // Maximum number of texture units
+ int textureUnitMax = 100;
+
+ // Flag that indicates whether DisplayList is used or not
+ boolean isDisplayList = true;
+
+ // If this flag is set, then by-ref geometry will not be
+ // put in display list
+ boolean buildDisplayListIfPossible = false;
+
+
+ // REQUESTCLEANUP messages argument
+ static Integer REMOVEALLCTXS_CLEANUP = new Integer(1);
+ static Integer REMOVECTX_CLEANUP = new Integer(2);
+ static Integer REMOVENOTIFY_CLEANUP = new Integer(3);
+ static Integer RESETCANVAS_CLEANUP = new Integer(4);
+ static Integer FREECONTEXT_CLEANUP = new Integer(5);
+
+ // arguments for renderer resource cleanup run
+ Object rendererCleanupArgs[] = {new Integer(Renderer.REQUESTCLEANUP),
+ null, null};
+
+
+ // Context creation should obtain this lock, so that
+ // first_time and all the extension initilialization
+ // are done in the MT safe manner
+ Object contextCreationLock = new Object();
+
+ // Flag that indicates whether to lock the DSI while rendering
+ boolean doDsiRenderLock = false;
+
+ // Flag that indicates whether J3DGraphics2D uses texturemapping
+ // instead of drawpixel for composite the buffers
+ boolean isJ3dG2dDrawPixel = true;
+
+ // flag that indicates whether BackgroundRetained uses texturemapping
+ // or drawpixel clear the background
+ boolean isBackgroundTexture = true;
+
+ // Flag that indicates whether the framebuffer is sharing the
+ // Z-buffer with both the left and right eyes when in stereo mode.
+ // If this is true, we need to clear the Z-buffer between rendering
+ // to the left and right eyes.
+ boolean sharedStereoZBuffer;
+
+ // True to disable all underlying multisampling API so it uses
+ // the setting in the driver.
+ boolean implicitAntialiasing = false;
+
+ // False to disable compiled vertex array extensions if support
+ boolean isCompliedVertexArray = true;
+
+ // False to disable rescale normal if OGL support
+ boolean isForceNormalized = false;
+
+ // Hashtable that maps a GraphicsDevice to its associated
+ // Screen3D--this is only used for on-screen Canvas3Ds
+ Hashtable deviceScreenMap = new Hashtable();
+
+ // Use to store all requests from user threads.
+ UnorderList requestObjList = new UnorderList();
+ private UnorderList requestTypeList = new UnorderList(Integer.class);
+
+ // Temporary storage to store stop request for requestViewList
+ private UnorderList tempViewList = new UnorderList();
+ private UnorderList renderOnceList = new UnorderList();
+
+ // This flag is true when there is pending request
+ // i.e. false when the above requestxxx Lists are all empty.
+ private boolean pendingRequest = false;
+
+ // Root ThreadGroup for creating Java 3D threads
+ private static ThreadGroup rootThreadGroup;
+
+ // Thread priority for all Java3D threads
+ private static int threadPriority;
+
+ static private Object mcThreadLock = new Object();
+
+ private ArrayList timestampUpdateList = new ArrayList(3);
+
+ private UnorderList freeMessageList = new UnorderList(8);
+
+ long awt;
+ native long getAWT();
+
+ // Method to initialize the native J3D library
+ private native boolean initializeJ3D(boolean disableXinerama);
+
+ // Method to get number of procesor
+ private native int getNumberOfProcessor();
+
+ // Methods to set/get system thread concurrency
+ private native void setThreadConcurrency(int newLevel);
+ private native int getThreadConcurrency();
+
+ // Maximum lights supported by the native API
+ private native int getMaximumLights();
+ int maxLights;
+
+ // This is used for D3D only
+ int resendTexTimestamp = 0;
+
+ // Indicates that the property to disable Xinerama mode was set and
+ // successfully disabled.
+ boolean xineramaDisabled = false;
+
+ /**
+ * Constructs a new MasterControl object. Note that there is
+ * exatly one MasterControl object, created statically by
+ * VirtualUniverse.
+ */
+ MasterControl() {
+
+ // Get AWT handle
+ awt = getAWT();
+
+ // Get native API information
+ nativeAPIInfo = new NativeAPIInfo();
+ renderingAPI = nativeAPIInfo.getRenderingAPI();
+ isD3DAPI = (renderingAPI == RENDER_DIRECT3D);
+ isWin32 = isD3DAPI || (renderingAPI == RENDER_OPENGL_WIN32);
+
+ if(J3dDebug.devPhase) {
+ // Check to see whether debug mode is allowed
+ Boolean j3dDebug =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.debug", "false");
+ return new Boolean(str);
+ }
+ });
+ J3dDebug.debug = j3dDebug.booleanValue();
+
+ // Get graphic library.
+ //System.err.println("In Development Phase : \n");
+
+ if(renderingAPI == RENDER_OPENGL_SOLARIS)
+ System.err.print("Graphics Library : Solaris OpenGL");
+ else if(renderingAPI == RENDER_OPENGL_WIN32)
+ System.err.print("Graphics Library : Win32 OpenGL");
+ else if(renderingAPI == RENDER_DIRECT3D)
+ System.err.println("Graphics Library : Direct3D");
+
+ System.err.println();
+
+ // Get package info.
+ ClassLoader classLoader = getClass().getClassLoader();
+ if (classLoader != null) {
+ // it is null in case of plugin
+ J3dDebug.pkgInfo(classLoader, "javax.media.j3d",
+ "SceneGraphObject");
+ }
+
+ if(J3dDebug.debug) {
+ J3dDebug.pkgInfo(classLoader, "javax.vecmath", "Point3d");
+ J3dDebug.pkgInfo(classLoader, "com.sun.j3d.utils.universe", "SimpleUniverse");
+
+ // Reminder statement.
+ System.err.println("For production release : Set J3dDebug.devPhase to false.\n");
+ System.err.println("MasterControl: J3dDebug.debug = " + J3dDebug.debug);
+ }
+ }
+
+ // Check to see whether shared contexts are allowed
+ if (getRenderingAPI() != RENDER_DIRECT3D) {
+ Boolean j3dSharedCtx =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.sharedctx");
+ if (str == null) {
+ return Boolean.FALSE;
+ } else {
+ sharedCtxOverride = true;
+ return new Boolean(str);
+ }
+ }
+ });
+ isSharedCtx = j3dSharedCtx.booleanValue();
+ if (sharedCtxOverride) {
+ if (isSharedCtx)
+ System.err.println("Java 3D: shared contexts enabled");
+ else
+ System.err.println("Java 3D: shared contexts disabled");
+ }
+ }
+
+ Boolean j3dDisableCompile =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.disablecompile");
+ if (str == null) {
+ return Boolean.FALSE;
+ } else {
+ return Boolean.TRUE;
+ }
+ }
+ });
+ disableCompile = j3dDisableCompile.booleanValue();
+ if (disableCompile) {
+ System.err.println("Java 3D: Compile disabled");
+ }
+
+ Boolean j3dDoCompaction =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.docompaction");
+ if (str == null) {
+ return Boolean.TRUE;
+ } else {
+ return Boolean.FALSE;
+ }
+ }
+ });
+ doCompaction = j3dDoCompaction.booleanValue();
+ if (!doCompaction) {
+ System.err.println("Java 3D: Disabling compaction.");
+ }
+
+ Boolean j3dUseCombiners =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.usecombiners");
+ if (str == null) {
+ return Boolean.FALSE;
+ } else {
+ return Boolean.TRUE;
+ }
+ }
+ });
+ useCombiners = j3dUseCombiners.booleanValue();
+ if (useCombiners) {
+ System.err.println("Java 3D: Using NV_register_combiners if available");
+ }
+
+ Boolean j3dDisableSeparateSpecularColor =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty(
+ "j3d.disableSeparateSpecular");
+ if (str == null) {
+ return Boolean.FALSE;
+ } else {
+ return Boolean.TRUE;
+ }
+ }
+ });
+ disableSeparateSpecularColor =
+ j3dDisableSeparateSpecularColor.booleanValue();
+ if (disableSeparateSpecularColor) {
+ System.err.println(
+ "Java 3D: Separate Specular Color disabled if possible");
+ }
+
+ // Get the maximum number of texture units
+ final int defaultTextureUnitMax = textureUnitMax;
+ Integer textureUnitLimit =
+ (Integer) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return Integer.getInteger("j3d.textureUnitMax",
+ defaultTextureUnitMax);
+ }
+ });
+
+
+ textureUnitMax = textureUnitLimit.intValue();
+ if (textureUnitMax != defaultTextureUnitMax) {
+ System.err.println("Java 3D: maximum number of texture units = " +
+ textureUnitMax);
+ }
+
+ Boolean j3dDisplayList =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.displaylist", "true");
+ return new Boolean(str);
+ }
+ });
+
+ isDisplayList = j3dDisplayList.booleanValue();
+ if (!isDisplayList) {
+ System.err.println("Java 3D: Display List disabled");
+ }
+
+
+ Boolean j3dimplicitAntialiasing =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.implicitAntialiasing", "false");
+ return new Boolean(str);
+ }
+ });
+
+ implicitAntialiasing = j3dimplicitAntialiasing.booleanValue();
+ if (implicitAntialiasing) {
+ System.err.println("Java 3D: Implicit Antialiasing enabled");
+ }
+
+
+ Boolean j3dcompliedVertexArray =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.compliedVertexArray", "true");
+ return new Boolean(str);
+ }
+ });
+
+ isCompliedVertexArray = j3dcompliedVertexArray.booleanValue();
+ if (!isCompliedVertexArray) {
+ System.err.println("Java 3D: Complied vertex array disabled");
+ }
+
+
+
+ Boolean j3dforceNormalized =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.forceNormalized", "false");
+ return new Boolean(str);
+ }
+ });
+
+ isForceNormalized = j3dforceNormalized.booleanValue();
+ if (isForceNormalized) {
+ System.err.println("Java 3D: Force Normalized");
+ }
+
+
+ Boolean j3dOptimizeSpace =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.optimizeForSpace", "true");
+ return new Boolean(str);
+ }
+
+ });
+
+
+ if (!j3dOptimizeSpace.booleanValue()) {
+ System.err.println("Java 3D: Optimize For Space disabled");
+ }
+ // Build Display list for by-ref geometry and infrequently changing geometry
+ // ONLY IF (isDisplayList is true and optimizeForSpace if False)
+ if (isDisplayList && !j3dOptimizeSpace.booleanValue()) {
+ buildDisplayListIfPossible = true;
+ }
+ else {
+ buildDisplayListIfPossible = false;
+ }
+
+ // Check to see whether Renderer can run without DSI lock
+ Boolean j3dRenderLock =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.renderLock", "false");
+ return new Boolean(str);
+ }
+ });
+ doDsiRenderLock = j3dRenderLock.booleanValue();
+ // Don't print this out now that the default is false
+ //if (!doDsiRenderLock) {
+ // System.err.println("Java 3D: render lock disabled");
+ //}
+
+ // Check to see whether J3DGraphics2D uses texturemapping
+ // or drawpixel for composite the buffers
+ Boolean j3dG2DRender =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.g2ddrawpixel", "false");
+ return new Boolean(str);
+ }
+ });
+ isJ3dG2dDrawPixel = j3dG2DRender.booleanValue();
+
+ if(J3dDebug.devPhase) {
+ if (!isJ3dG2dDrawPixel) {
+ System.err.println("Java 3D: render Graphics2D DrawPixel disabled");
+ } else {
+ System.err.println("Java 3D: render Graphics2D DrawPixel enabled");
+ }
+ }
+
+ // Check to see whether BackgroundRetained uses texturemapping
+ // or drawpixel clear the background
+ if (!isD3D()) {
+ Boolean j3dBackgroundTexture =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.backgroundtexture", "true");
+ return new Boolean(str);
+ }
+ });
+ isBackgroundTexture = j3dBackgroundTexture.booleanValue();
+
+ if(J3dDebug.devPhase) {
+ if (!isBackgroundTexture) {
+ System.err.println("Java 3D: background texture is disabled");
+ } else {
+ System.err.println("Java 3D: background texture is enabled");
+ }
+ }
+ } else {
+ // D3D always use background texture and use
+ // canvas.clear() instead of canvas.textureclear() in Renderer
+ isBackgroundTexture = false;
+ }
+
+ // Check to see if stereo mode is sharing the Z-buffer for both
+ // eyes.
+ Boolean j3dSharedStereoZBuffer =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String defaultValue = (isWin32 ? "true" : "false");
+ String str = System.getProperty("j3d.sharedstereozbuffer",
+ defaultValue);
+ return new Boolean(str);
+ }
+ });
+ sharedStereoZBuffer = j3dSharedStereoZBuffer.booleanValue();
+ j3dSharedStereoZBuffer = null;
+
+ // Get the maximum number of concurrent threads (CPUs)
+ final int defaultThreadLimit = getNumberOfProcessor()+1;
+ Integer threadLimit =
+ (Integer) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return Integer.getInteger("j3d.threadLimit",
+ defaultThreadLimit);
+ }
+ });
+
+
+ cpuLimit = threadLimit.intValue();
+ if (cpuLimit < 1)
+ cpuLimit = 1;
+ if (J3dDebug.debug || cpuLimit != defaultThreadLimit) {
+ System.err.println("Java 3D: concurrent threadLimit = " +
+ cpuLimit);
+ }
+
+ // Ensure that there are at least enough system threads to
+ // support all of Java 3D's threads running in parallel
+ int threadConcurrency = getThreadConcurrency();
+ if (J3dDebug.debug) {
+ System.err.println("System threadConcurrency = " +
+ threadConcurrency);
+ }
+ if (threadConcurrency != -1 && threadConcurrency < (cpuLimit + 1)) {
+ threadConcurrency = cpuLimit + 1;
+ if (J3dDebug.debug) {
+ System.err.println("Setting system threadConcurrency to " +
+ threadConcurrency);
+ }
+ setThreadConcurrency(threadConcurrency);
+ }
+
+ // Get the input device scheduler sampling time
+ Integer samplingTime =
+ (Integer) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return Integer.getInteger("j3d.deviceSampleTime", 0);
+ }
+ });
+
+ if (samplingTime.intValue() > 0) {
+ InputDeviceScheduler.samplingTime =
+ samplingTime.intValue();
+ System.err.println("Java 3D: Input device sampling time = "
+ + samplingTime + " ms");
+ }
+
+ // See if Xinerama should be disabled for better performance.
+ Boolean j3dDisableXinerama =
+ (Boolean) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("j3d.disableXinerama",
+ "false");
+ return new Boolean(str);
+ }
+ });
+
+ boolean disableXinerama = j3dDisableXinerama.booleanValue();
+
+ // Initialize the native J3D library
+ if (!initializeJ3D(disableXinerama)) {
+ if (isGreenThreadUsed()) {
+ System.err.print(J3dI18N.getString("MasterControl1"));
+ }
+ throw new RuntimeException(J3dI18N.getString("MasterControl0"));
+ }
+
+ if (xineramaDisabled) {
+ // initializeJ3D() successfully disabled Xinerama.
+ System.err.println("Java 3D: Xinerama disabled");
+ }
+ else if (disableXinerama) {
+ // j3d.disableXinerama is true, but attempt failed.
+ System.err.println("Java 3D: could not disable Xinerama");
+ }
+
+ // Get the maximum Lights
+ maxLights = getMaximumLights();
+
+ // create the freelists
+ FreeListManager.createFreeLists();
+ }
+
+ static public String getProperty(final String s) {
+
+ return (String) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return System.getProperty(s);
+ }
+ });
+ }
+
+ boolean isGreenThreadUsed() {
+
+ String javaVMInfo =
+ (String) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ String str = System.getProperty("java.vm.info");
+ return str;
+ }
+ });
+
+ String greenThreadStr = new String("green threads");
+ if (javaVMInfo.indexOf(greenThreadStr) == -1)
+ return false;
+ else
+ return true;
+ }
+
+
+ /**
+ * Method to load the native libraries needed by Java 3D. This is
+ * called by the static initializer in VirtualUniverse <i>before</i>
+ * the MasterControl object is created.
+ */
+ static void loadLibraries() {
+ // This works around a native load library bug
+ try {
+ java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
+ toolkit = null; // just making sure GC collects this
+ } catch (java.awt.AWTError e) {
+ }
+
+ // Load the JAWT native library
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ System.loadLibrary("jawt");
+ return null;
+ }
+ });
+
+ // Load the native J3D library
+ java.security.AccessController.doPrivileged(new
+ java.security.PrivilegedAction() {
+ public Object run() {
+
+ String osName = System.getProperty("os.name");
+ /* System.out.println(" os.name is " + osName ); */
+ // If it is a Windows OS, we want to support
+ // dynamic native library selection (ogl, d3d)
+ if((osName.length() > 8) &&
+ ((osName.substring(0,7)).equals("Windows"))){
+
+ /*
+ * TODO : Will support a more flexible dynamic
+ * selection scheme via the use of Preferences API.
+ *
+ */
+ String str = System.getProperty("j3d.rend");
+ if ((str == null) || (!str.equals("d3d"))) {
+ /* System.out.println("(1) ogl case : j3d.rend is " + str ); */
+ System.loadLibrary("j3dcore-ogl");
+
+ }
+ else {
+ /* System.out.println("(2) d3d case : j3d.rend is " + str); */
+ System.loadLibrary("j3dcore-d3d");
+ }
+ }
+ else {
+ /* System.out.println("(3) ogl case"); */
+ System.loadLibrary("j3dcore-ogl");
+ }
+ return null;
+ }
+ });
+ }
+
+
+ /**
+ * Invoke from InputDeviceScheduler to create an
+ * InputDeviceBlockingThread.
+ */
+ InputDeviceBlockingThread getInputDeviceBlockingThread(
+ final InputDevice device) {
+
+ return (InputDeviceBlockingThread)
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ synchronized (rootThreadGroup) {
+ Thread thread = new InputDeviceBlockingThread(
+ rootThreadGroup, device);
+ thread.setPriority(threadPriority);
+ return thread;
+ }
+ }
+ }
+ );
+ }
+
+ /**
+ * Set thread priority to all threads under Java3D thread group.
+ */
+ void setThreadPriority(final int pri) {
+ synchronized (rootThreadGroup) {
+ threadPriority = pri;
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ Thread list[] = new
+ Thread[rootThreadGroup.activeCount()];
+ int count = rootThreadGroup.enumerate(list);
+ for (int i=count-1; i >=0; i--) {
+ list[i].setPriority(pri);
+ }
+ return null;
+ }
+ });
+ }
+ }
+
+
+ /**
+ * Return Java3D thread priority
+ */
+ int getThreadPriority() {
+ return threadPriority;
+ }
+
+ /**
+ * This returns the a unused renderer bit
+ */
+ int getRendererBit() {
+ return (1 << rendererCount++);
+ }
+
+ /**
+ * This returns a context creation time stamp
+ * Note: this has to be called under the contextCreationLock
+ */
+ long getContextTimeStamp() {
+ return (++contextTimeStamp);
+ }
+
+
+ /**
+ * This returns the a unused displayListId
+ */
+ Integer getDisplayListId() {
+ return (Integer)
+ FreeListManager.getObject(FreeListManager.DISPLAYLIST);
+ }
+
+ void freeDisplayListId(Integer id) {
+ FreeListManager.freeObject(FreeListManager.DISPLAYLIST, id);
+ }
+
+ /**
+ * This returns the a unused textureId
+ */
+ int getTexture2DId() {
+ // MasterControl has to handle the ID itself. 2D and 3D ideas must
+ // never be the same, so the counter has to be in the MasterControl
+ MemoryFreeList textureIds =
+ FreeListManager.getFreeList(FreeListManager.TEXTURE2D);
+ int id;
+
+ synchronized (textureIdLock) {
+ if (textureIds.size() > 0) {
+ return ((Integer)FreeListManager.
+ getObject(FreeListManager.TEXTURE2D)).intValue();
+ } else {
+ return (++textureIdCount);
+ }
+ }
+ }
+
+ int getTexture3DId() {
+ // MasterControl has to handle the ID itself. 2D and 3D ideas must
+ // never be the same, so the counter has to be in the MasterControl
+ MemoryFreeList textureIds =
+ FreeListManager.getFreeList(FreeListManager.TEXTURE3D);
+ synchronized (textureIdLock) {
+ if (textureIds.size > 0) {
+ return ((Integer)FreeListManager.
+ getObject(FreeListManager.TEXTURE3D)).intValue();
+ }
+ else return (++textureIdCount);
+ }
+ }
+
+ void freeTexture2DId(int id) {
+ FreeListManager.freeObject(FreeListManager.TEXTURE2D, new Integer(id));
+ }
+
+ void freeTexture3DId(int id) {
+ FreeListManager.freeObject(FreeListManager.TEXTURE3D, new Integer(id));
+ }
+
+ int getCanvasBit() {
+ // Master control need to keep count itself
+ MemoryFreeList cbId =
+ FreeListManager.getFreeList(FreeListManager.CANVASBIT);
+ if (cbId.size() > 0) {
+ return ((Integer)FreeListManager.
+ getObject(FreeListManager.CANVASBIT)).intValue();
+ }
+ else {
+ if (canvasBitCount > 31) {
+ throw new InternalError();
+ }
+ return (1 << canvasBitCount++);
+ }
+ }
+
+
+ void freeCanvasBit(int canvasBit) {
+ FreeListManager.freeObject(FreeListManager.CANVASBIT,
+ new Integer(canvasBit));
+ }
+
+ Transform3D getTransform3D(Transform3D val) {
+ Transform3D t;
+ t = (Transform3D)
+ FreeListManager.getObject(FreeListManager.TRANSFORM3D);
+ if (val != null) t.set(val);
+ return t;
+ }
+
+ void addToTransformFreeList(Transform3D t) {
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t);
+ }
+
+
+ ImageComponentUpdateInfo getFreeImageUpdateInfo() {
+ ImageComponentUpdateInfo info;
+
+ synchronized (imageUpdateInfoList) {
+ if (numFreeImageUpdateInfo > 0) {
+ numFreeImageUpdateInfo--;
+ info = (ImageComponentUpdateInfo)
+ imageUpdateInfoList[numFreeImageUpdateInfo];
+ } else {
+ info = new ImageComponentUpdateInfo();
+ }
+ }
+ return (info);
+ }
+
+ void addFreeImageUpdateInfo(ImageComponentUpdateInfo info) {
+ synchronized (imageUpdateInfoList) {
+ if (imageUpdateInfoList.length == numFreeImageUpdateInfo) {
+ ImageComponentUpdateInfo[] newFreeList =
+ new ImageComponentUpdateInfo[numFreeImageUpdateInfo * 2];
+ System.arraycopy(imageUpdateInfoList, 0, newFreeList, 0,
+ numFreeImageUpdateInfo);
+ newFreeList[numFreeImageUpdateInfo++] = info;
+ imageUpdateInfoList = newFreeList;
+ } else {
+ imageUpdateInfoList[numFreeImageUpdateInfo++] = info;
+ }
+ }
+ }
+
+ void addFreeImageUpdateInfo(ArrayList freeList) {
+ ImageComponentUpdateInfo info;
+
+ synchronized (imageUpdateInfoList) {
+ int len = numFreeImageUpdateInfo + freeList.size();
+
+ if (imageUpdateInfoList.length <= len) {
+ ImageComponentUpdateInfo[] newFreeList =
+ new ImageComponentUpdateInfo[len * 2];
+ System.arraycopy(imageUpdateInfoList, 0, newFreeList, 0,
+ numFreeImageUpdateInfo);
+ imageUpdateInfoList = newFreeList;
+ }
+
+ for (int i = 0; i < freeList.size(); i++) {
+ info = (ImageComponentUpdateInfo) freeList.get(i);
+ if (info != null) {
+ imageUpdateInfoList[numFreeImageUpdateInfo++] = info;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Create a Renderer if it is not already done so.
+ * This is used for GraphicsConfigTemplate3D passing
+ * graphics call to RequestRenderer.
+ */
+ Renderer createRenderer(GraphicsConfiguration gc) {
+ final GraphicsDevice gd = gc.getDevice();
+
+ Renderer rdr = (Renderer) Screen3D.deviceRendererMap.get(gd);
+ if (rdr != null) {
+ return rdr;
+ }
+
+
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ Renderer r;
+ synchronized (rootThreadGroup) {
+ r = new Renderer(rootThreadGroup);
+ r.initialize();
+ r.setPriority(threadPriority);
+ Screen3D.deviceRendererMap.put(gd, r);
+ }
+ return null;
+ }
+ });
+
+ threadListsChanged = true;
+
+ return (Renderer) Screen3D.deviceRendererMap.get(gd);
+ }
+
+ /**
+ * Post the request in queue
+ */
+ void postRequest(Integer type, Object obj) {
+
+ synchronized (mcThreadLock) {
+ synchronized (requestObjList) {
+ if (mcThread == null) {
+ if ((type == ACTIVATE_VIEW) ||
+ (type == GETBESTCONFIG) ||
+ (type == SET_VIEW) ||
+ (type == ISCONFIGSUPPORT) ||
+ (type == SET_QUERYPROPERTIES) ||
+ (type == SET_GRAPHICSCONFIG_FEATURES)) {
+ createMasterControlThread();
+ requestObjList.add(obj);
+ requestTypeList.add(type);
+ pendingRequest = true;
+ } else if (type == EMPTY_UNIVERSE) {
+ destroyUniverseThreads((VirtualUniverse) obj);
+ } else if (type == STOP_VIEW) {
+ View v = (View) obj;
+ v.stopViewCount = -1;
+ v.isRunning = false;
+ } else if (type == STOP_RENDERER) {
+ if (obj instanceof Canvas3D) {
+ ((Canvas3D) obj).isRunningStatus = false;
+ } else {
+ ((Renderer) obj).userStop = true;
+ }
+ } else if (type == UNREGISTER_VIEW) {
+ ((View) obj).doneUnregister = true;
+ } else {
+ requestObjList.add(obj);
+ requestTypeList.add(type);
+ pendingRequest = true;
+ }
+ } else {
+ requestObjList.add(obj);
+ requestTypeList.add(type);
+ pendingRequest = true;
+ }
+ }
+ }
+
+ setWork();
+ }
+
+
+
+
+ /**
+ * This procedure is invoked when isRunning is false.
+ * Return true when there is no more pending request so that
+ * Thread can terminate. Otherwise we have to recreate
+ * the MC related threads.
+ */
+ boolean mcThreadDone() {
+ synchronized (mcThreadLock) {
+ synchronized (requestObjList) {
+ if (!pendingRequest) {
+ mcThread = null;
+ if (renderingAttributesStructure.updateThread !=
+ null) {
+ renderingAttributesStructure.updateThread.finish();
+ renderingAttributesStructure.updateThread =
+ null;
+ }
+ renderingAttributesStructure = new RenderingAttributesStructure();
+ if (timerThread != null) {
+ timerThread.finish();
+ timerThread = null;
+ }
+ requestObjList.clear();
+ requestTypeList.clear();
+ return true;
+ }
+ running = true;
+ createMCThreads();
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns the native rendering layer we are using
+ */
+ final int getRenderingAPI() {
+ return renderingAPI;
+ }
+
+ final boolean isD3D() {
+ return isD3DAPI;
+ }
+
+ /**
+ * This method increments and returns the next time value
+ * timeLock must get before this procedure is invoked
+ */
+ final long getTime() {
+ return (time++);
+ }
+
+
+
+ /**
+ * This adds a BHNode to one of the list of BHNodes
+ */
+ void addBHNodeToFreelists(BHNode bH) {
+ bH.parent = null;
+ bH.mark = false;
+
+ if (bH.nodeType == BHNode.BH_TYPE_INTERNAL) {
+ ((BHInternalNode)bH).lChild = null;
+ ((BHInternalNode)bH).rChild = null;
+ FreeListManager.freeObject(FreeListManager.BHINTERNAL, bH);
+ }
+ else if (bH.nodeType == BHNode.BH_TYPE_LEAF) {
+ ((BHLeafNode)(bH)).leafIF = null;
+ FreeListManager.freeObject(FreeListManager.BHLEAF, bH);
+ }
+ }
+
+ /**
+ * This gets a message from the free list. If there isn't any,
+ * it creates one.
+ */
+ BHNode getBHNode(int type) {
+
+ if (type == BHNode.BH_TYPE_LEAF) {
+ return (BHNode) FreeListManager.getObject(FreeListManager.BHLEAF);
+ }
+
+ if (type == BHNode.BH_TYPE_INTERNAL) {
+ return (BHNode)
+ FreeListManager.getObject(FreeListManager.BHINTERNAL);
+ }
+ return null;
+ }
+
+
+ /**
+ * This adds a message to the list of messages
+ */
+ final void addMessageToFreelists(J3dMessage m) {
+ FreeListManager.freeObject(FreeListManager.MESSAGE, m);
+ }
+
+ /**
+ * This gets a message from the free list. If there isn't any,
+ * it creates one.
+ */
+ final J3dMessage getMessage() {
+ return (J3dMessage) FreeListManager.getObject(FreeListManager.MESSAGE);
+ }
+
+
+ /**
+ * This takes a given message and parses it out to the structures and
+ * marks its time value.
+ */
+ void processMessage(J3dMessage message) {
+
+ synchronized (timeLock) {
+ message.time = getTime();
+ sendMessage(message);
+ }
+ setWork();
+ }
+
+ /**
+ * This takes an array of messages and parses them out to the structures and
+ * marks the time value. Make sure, setWork() is done at the very end
+ * to make sure all the messages will be processed in the same frame
+ */
+ void processMessage(J3dMessage[] messages) {
+
+ synchronized (timeLock) {
+ long time = getTime();
+
+ for (int i = 0; i < messages.length; i++) {
+ messages[i].time = time;
+ sendMessage(messages[i]);
+ }
+ }
+ setWork();
+ }
+
+
+ /**
+ * Create and start the MasterControl Thread.
+ */
+ void createMasterControlThread() {
+ running = true;
+ workToDo = false;
+ state = RUNNING;
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ synchronized (rootThreadGroup) {
+ mcThread = new
+ MasterControlThread(rootThreadGroup);
+ mcThread.setPriority(threadPriority);
+ }
+ return null;
+ }
+ });
+ }
+
+ // assuming the timeLock is already acquired
+
+ /**
+ * Send a message to another Java 3D thread.
+ */
+ void sendMessage(J3dMessage message) {
+
+ synchronized (message) {
+ VirtualUniverse u = message.universe;
+ int targetThreads = message.threads;
+
+
+ // System.out.println("============> sendMessage");
+ // dumpmsg(message);
+ if ((targetThreads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) {
+ renderingAttributesStructure.addMessage(message);
+ }
+
+ // GraphicsContext3D send message with universe = null
+ if (u != null) {
+ if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
+ u.geometryStructure.addMessage(message);
+ }
+ if ((targetThreads & J3dThread.UPDATE_TRANSFORM) != 0) {
+ u.transformStructure.addMessage(message);
+ }
+ if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
+ u.behaviorStructure.addMessage(message);
+ }
+ if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
+ u.soundStructure.addMessage(message);
+ }
+ if ((targetThreads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) {
+ u.renderingEnvironmentStructure.addMessage(message);
+ }
+ }
+
+ if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
+ // Note that we don't check for active view
+ if (message.view != null && message.view.soundScheduler != null ) {
+ // This make sure that message won't lost even
+ // though this view not yet register
+ message.view.soundScheduler.addMessage(message);
+ } else {
+ synchronized (views) {
+ View v[] = (View []) views.toArray(false);
+ int i = views.arraySize()-1;
+ if (u == null) {
+ while (i>=0) {
+ v[i--].soundScheduler.addMessage(message);
+ }
+ } else {
+ while (i>=0) {
+ if (v[i].universe == u) {
+ v[i].soundScheduler.addMessage(message);
+ }
+ i--;
+ }
+ }
+ }
+ }
+ }
+
+ if ((targetThreads & J3dThread.UPDATE_RENDER) != 0) {
+ // Note that we don't check for active view
+ if (message.view != null && message.view.renderBin != null) {
+ // This make sure that message won't lost even
+ // though this view not yet register
+ message.view.renderBin.addMessage(message);
+ } else {
+ synchronized (views) {
+ View v[] = (View []) views.toArray(false);
+ int i = i=views.arraySize()-1;
+ if (u == null) {
+ while (i>=0) {
+ v[i--].renderBin.addMessage(message);
+ }
+ }
+ else {
+ while (i>=0) {
+ if (v[i].universe == u) {
+ v[i].renderBin.addMessage(message);
+ }
+ i--;
+ }
+ }
+ }
+ }
+ }
+
+ if (message.getRefcount() == 0) {
+ message.clear();
+ addMessageToFreelists(message);
+ }
+ }
+ }
+
+
+ /**
+ * Send a message to another Java 3D thread.
+ * This variant is only call by TimerThread for Input Device Scheduler
+ * or to redraw all View for RenderThread
+ */
+ void sendRunMessage(int targetThreads) {
+
+ synchronized (timeLock) {
+
+ long time = getTime();
+
+ if ((targetThreads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) {
+ synchronized (inputDeviceThreads) {
+ InputDeviceScheduler ds[] = (InputDeviceScheduler [])
+ inputDeviceThreads.toArray(false);
+ for (int i=inputDeviceThreads.size()-1; i >=0; i--) {
+ if (ds[i].physicalEnv.activeViewRef > 0) {
+ ds[i].getThreadData().lastUpdateTime =
+ time;
+ }
+ }
+
+ // timerThread instance in MC will set to null in
+ // destroyUniverseThreads() so we need to check if
+ // TimerThread kick in to sendRunMessage() after that.
+ // It happens because TimerThread is the only thread run
+ // asychronizously with MasterControl thread.
+
+ if (timerThread != null) {
+ // Notify TimerThread to wakeup this procedure
+ // again next time.
+ timerThread.addInputDeviceSchedCond();
+ }
+ }
+ }
+ if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
+ synchronized (renderThreadData) {
+ J3dThreadData[] threads = (J3dThreadData [])
+ renderThreadData.toArray(false);
+ int i=renderThreadData.arraySize()-1;
+ J3dThreadData thr;
+ while (i>=0) {
+ thr = threads[i--];
+ if ( thr.view.renderBinReady) {
+ thr.lastUpdateTime = time;
+ }
+ }
+ }
+ }
+ }
+ setWork();
+ }
+
+ /**
+ * Send a message to another Java 3D thread.
+ * This variant is only call by TimerThread for Sound Scheduler
+ */
+ void sendRunMessage(long waitTime, View view, int targetThreads) {
+
+ synchronized (timeLock) {
+
+ long time = getTime();
+
+ if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
+ if (view.soundScheduler != null) {
+ view.soundScheduler.threadData.lastUpdateTime = time;
+ }
+ // wakeup this procedure next time
+ // QUESTION: waitTime calculated some milliseconds BEFORE
+ // this methods getTime() called - shouldn't actual
+ // sound Complete time be passed by SoundScheduler
+ // QUESTION: will this wake up only soundScheduler associated
+ // with this view?? (since only it's lastUpdateTime is set)
+ // or all soundSchedulers??
+ timerThread.addSoundSchedCond(time+waitTime);
+ }
+ }
+ setWork();
+ }
+
+ /**
+ * Send a message to another Java 3D thread.
+ * This variant is only called to update Render Thread
+ */
+ void sendRunMessage(View v, int targetThreads) {
+
+ synchronized (timeLock) {
+ long time = getTime();
+
+ if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
+ synchronized (renderThreadData) {
+ J3dThreadData[] threads = (J3dThreadData [])
+ renderThreadData.toArray(false);
+ int i=renderThreadData.arraySize()-1;
+ J3dThreadData thr;
+ while (i>=0) {
+ thr = threads[i--];
+ if (thr.view == v && v.renderBinReady) {
+ thr.lastUpdateTime = time;
+ }
+ }
+ }
+ }
+ }
+ setWork();
+ }
+
+
+ /**
+ * This sends a run message to the given threads.
+ */
+ void sendRunMessage(VirtualUniverse u, int targetThreads) {
+ // We don't sendRunMessage to update structure except Behavior
+
+ synchronized (timeLock) {
+ long time = getTime();
+
+ if ((targetThreads & J3dThread.BEHAVIOR_SCHEDULER) != 0) {
+ if (u.behaviorScheduler != null) {
+ u.behaviorScheduler.getThreadData(null,
+ null).lastUpdateTime = time;
+ }
+ }
+
+ if ((targetThreads & J3dThread.UPDATE_BEHAVIOR) != 0) {
+ u.behaviorStructure.threadData.lastUpdateTime = time;
+ }
+
+ if ((targetThreads & J3dThread.UPDATE_GEOMETRY) != 0) {
+ u.geometryStructure.threadData.lastUpdateTime = time;
+ }
+
+ if ((targetThreads & J3dThread.UPDATE_SOUND) != 0) {
+ u.soundStructure.threadData.lastUpdateTime = time;
+ }
+
+ if ((targetThreads & J3dThread.SOUND_SCHEDULER) != 0) {
+ synchronized (views) {
+ View v[] = (View []) views.toArray(false);
+ for (int i= views.arraySize()-1; i >=0; i--) {
+ if ((v[i].soundScheduler != null) &&
+ (v[i].universe == u)) {
+ v[i].soundScheduler.threadData.lastUpdateTime = time;
+ }
+ }
+ }
+ }
+
+ if ((targetThreads & J3dThread.RENDER_THREAD) != 0) {
+
+ synchronized (renderThreadData) {
+ J3dThreadData[] threads = (J3dThreadData [])
+ renderThreadData.toArray(false);
+ int i=renderThreadData.arraySize()-1;
+ J3dThreadData thr;
+ while (i>=0) {
+ thr = threads[i--];
+ if (thr.view.universe == u && thr.view.renderBinReady) {
+ thr.lastUpdateTime = time;
+ }
+ }
+ }
+ }
+ }
+
+ setWork();
+ }
+
+
+ /**
+ * Return a clone of View, we can't access
+ * individual element of View after getting the size
+ * in separate API call without synchronized views.
+ */
+ UnorderList cloneView() {
+ return (UnorderList) views.clone();
+ }
+
+ /**
+ * Return true if view is already registered with MC
+ */
+ boolean isRegistered(View view) {
+ return views.contains(view);
+ }
+
+ /**
+ * This snapshots the time values to be used for this iteration
+ */
+ private void updateTimeValues() {
+ int i=0;
+ J3dThreadData lastThread=null;
+ J3dThreadData thread=null;
+ long lastTime = currentTime;
+
+ currentTime = getTime();
+
+ J3dThreadData threads[] = (J3dThreadData [])
+ stateWorkThreads.toArray(false);
+ int size = stateWorkThreads.arraySize();
+
+ while (i<lastTransformStructureThread) {
+ thread = threads[i++];
+
+ if ((thread.lastUpdateTime > thread.lastRunTime) &&
+ !thread.thread.userStop) {
+ lastThread = thread;
+ thread.needsRun = true;
+ thread.threadOpts = J3dThreadData.CONT_THREAD;
+ thread.lastRunTime = currentTime;
+ } else {
+ thread.needsRun = false;
+ }
+ }
+
+ if (lastThread != null) {
+ lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
+ lastThread = null;
+ }
+
+ while (i<lastStructureUpdateThread) {
+ thread = threads[i++];
+ if ((thread.lastUpdateTime > thread.lastRunTime) &&
+ !thread.thread.userStop) {
+ lastThread = thread;
+ thread.needsRun = true;
+ thread.threadOpts = J3dThreadData.CONT_THREAD;
+ thread.lastRunTime = currentTime;
+ } else {
+ thread.needsRun = false;
+ }
+ }
+ if (lastThread != null) {
+ lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
+ lastThread = null;
+ }
+
+ while (i<size) {
+ thread = threads[i++];
+ if ((thread.lastUpdateTime > thread.lastRunTime) &&
+ !thread.thread.userStop) {
+ lastThread = thread;
+ thread.needsRun = true;
+ thread.threadOpts = J3dThreadData.CONT_THREAD;
+ thread.lastRunTime = currentTime;
+ } else {
+ thread.needsRun = false;
+ }
+ }
+ if (lastThread != null) {
+ lastThread.threadOpts = J3dThreadData.WAIT_ALL_THREADS;
+ lastThread = null;
+ }
+
+
+ threads = (J3dThreadData []) renderWorkThreads.toArray(false);
+ size = renderWorkThreads.arraySize();
+ View v = null;
+ J3dThreadData lastRunThread = null;
+ waitTimestamp++;
+ sleepTime = Long.MAX_VALUE;
+
+ boolean threadToRun = false;
+ boolean needToSetWork = false;
+
+ for (i=0; i<size; i++) {
+ thread = threads[i];
+ if (thread.canvas == null) { // Only for swap thread
+ ((Object []) thread.threadArgs)[3] = null;
+ }
+ if (thread.view != v) {
+ thread.view.computeCycleTime();
+ if (thread.view.sleepTime < sleepTime) {
+ sleepTime = thread.view.sleepTime;
+ }
+ }
+ if ((thread.lastUpdateTime > thread.lastRunTime) &&
+ !thread.thread.userStop) {
+
+ if (!thread.view.isMinCycleTimeAchieve) {
+ thread.needsRun = false;
+ needToSetWork = true;
+ continue;
+ }
+
+ if (thread.thread.lastWaitTimestamp == waitTimestamp) {
+ // This renderer thread is repeated. We must wait
+ // until all previous renderer threads done before
+ // allowing this thread to continue. Note that
+ // lastRunThread can't be null in this case.
+ waitTimestamp++;
+ if (thread.view != v) {
+ // A new View is start
+ v = thread.view;
+ threadToRun = true;
+ lastRunThread.threadOpts =
+ (J3dThreadData.STOP_TIMER |
+ J3dThreadData.WAIT_ALL_THREADS);
+ ((Object []) lastRunThread.threadArgs)[3] = lastRunThread.view;
+ thread.threadOpts = (J3dThreadData.START_TIMER |
+ J3dThreadData.CONT_THREAD);
+ } else {
+ if ((lastRunThread.threadOpts &
+ J3dThreadData.START_TIMER) != 0) {
+ lastRunThread.threadOpts =
+ (J3dThreadData.START_TIMER |
+ J3dThreadData.WAIT_ALL_THREADS);
+
+ } else {
+ lastRunThread.threadOpts =
+ J3dThreadData.WAIT_ALL_THREADS;
+ }
+ thread.threadOpts = J3dThreadData.CONT_THREAD;
+
+ }
+ } else {
+ if (thread.view != v) {
+ v = thread.view;
+ threadToRun = true;
+ // Although the renderer thread is not
+ // repeated. We still need to wait all
+ // previous renderer threads if new View
+ // start.
+ if (lastRunThread != null) {
+ lastRunThread.threadOpts =
+ (J3dThreadData.STOP_TIMER |
+ J3dThreadData.WAIT_ALL_THREADS);
+ ((Object []) lastRunThread.threadArgs)[3]
+ = lastRunThread.view;
+ }
+ thread.threadOpts = (J3dThreadData.START_TIMER |
+ J3dThreadData.CONT_THREAD);
+ } else {
+ thread.threadOpts = J3dThreadData.CONT_THREAD;
+ }
+ }
+ thread.thread.lastWaitTimestamp = waitTimestamp;
+ thread.needsRun = true;
+ thread.lastRunTime = currentTime;
+ lastRunThread = thread;
+ } else {
+ thread.needsRun = false;
+ }
+ }
+
+
+ if (lastRunThread != null) {
+ lastRunThread.threadOpts =
+ (J3dThreadData.STOP_TIMER |
+ J3dThreadData.WAIT_ALL_THREADS|
+ J3dThreadData.LAST_STOP_TIMER);
+ lockGeometry = true;
+ ((Object []) lastRunThread.threadArgs)[3] = lastRunThread.view;
+ } else {
+ lockGeometry = false;
+ }
+
+
+ if (needToSetWork && !threadToRun) {
+ sleepTime -= (currentTime - lastTime);
+ if (sleepTime > 0) {
+ runMonitor(SLEEP, null, null, null, null);
+ }
+ // Need to invoke MC to do work
+ // next time after sleep
+ setWork();
+ }
+
+ }
+
+ private void createUpdateThread(J3dStructure structure) {
+ final J3dStructure s = structure;
+
+ if (s.updateThread == null) {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ synchronized (rootThreadGroup) {
+ s.updateThread = new StructureUpdateThread(
+ rootThreadGroup, s, s.threadType);
+ s.updateThread.setPriority(threadPriority);
+ }
+ return null;
+ }
+ });
+ s.updateThread.initialize();
+ s.threadData.thread = s.updateThread;
+ // This takes into accout for thread that just destroy and
+ // create again. In this case the threadData may receive
+ // message before the thread actually created. We don't want
+ // the currentTime to overwrite the update time of which
+ // is set by threadData when get message.
+ s.threadData.lastUpdateTime = Math.max(currentTime,
+ s.threadData.lastUpdateTime);
+ }
+ }
+
+ private void emptyMessageList(J3dStructure structure, View v) {
+ if (structure != null) {
+ if (v == null) {
+ if (structure.threadData != null) {
+ structure.threadData.thread = null;
+ }
+
+ if (structure.updateThread != null) {
+ structure.updateThread.structure = null;
+ }
+ structure.updateThread = null;
+ }
+ boolean otherViewExist = false;
+ if ((v != null) && (v.universe != null)) {
+ // Check if there is any other View register with the
+ // same universe
+ for (int i=views.size()-1; i >= 0; i--) {
+ if (((View) views.get(i)).universe == v.universe) {
+ otherViewExist = true;
+ break;
+ }
+ }
+ }
+
+
+ UnorderList mlist = structure.messageList;
+ // Note that message is add at the end of array
+ synchronized (mlist) {
+ int size = mlist.size();
+ if (size > 0) {
+ J3dMessage mess[] = (J3dMessage []) mlist.toArray(false);
+ J3dMessage m;
+ int i = 0;
+
+ Object oldRef= null;
+ while (i < size) {
+ m = mess[i];
+ if ((v == null) || (m.view == v) ||
+ ((m.view == null) && !otherViewExist)) {
+ if (m.type == J3dMessage.INSERT_NODES) {
+ // There is another View register request
+ // immediately following, so no need
+ // to remove message.
+ break;
+ }
+ // Some other thread may still using this
+ // message so we should not directly
+ // add this message to free lists
+ m.decRefcount();
+ mlist.removeOrdered(i);
+ size--;
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void destroyUpdateThread(J3dStructure structure) {
+ // If unregisterView message got before EMPTY_UNIVERSE
+ // message, then updateThread is already set to null.
+ if (structure.updateThread != null) {
+ structure.updateThread.finish();
+ structure.updateThread.structure = null;
+ structure.updateThread = null;
+ }
+ structure.threadData.thread = null;
+ structure.clearMessages();
+ }
+
+ /**
+ * This register a View with MasterControl.
+ * The View has at least one Canvas3D added to a container.
+ */
+ private void registerView(View v) {
+ final VirtualUniverse univ = v.universe;
+
+ if (views.contains(v) && regUniverseList.contains(univ)) {
+ return; // already register
+ }
+
+ if (timerThread == null) {
+ // This handle the case when MC shutdown and restart in
+ // a series of pending request
+ running = true;
+ createMCThreads();
+ }
+ // If viewId is null, assign one ..
+ v.assignViewId();
+
+ // Create thread if not done before
+ createUpdateThread(univ.behaviorStructure);
+ createUpdateThread(univ.geometryStructure);
+ createUpdateThread(univ.soundStructure);
+ createUpdateThread(univ.renderingEnvironmentStructure);
+ createUpdateThread(univ.transformStructure);
+
+ // create Behavior scheduler
+ J3dThreadData threadData = null;
+
+ if (univ.behaviorScheduler == null) {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ synchronized (rootThreadGroup) {
+ univ.behaviorScheduler = new BehaviorScheduler(
+ rootThreadGroup, univ);
+ univ.behaviorScheduler.setPriority(threadPriority);
+ }
+ return null;
+ }
+ });
+ univ.behaviorScheduler.initialize();
+ univ.behaviorScheduler.userStop = v.stopBehavior;
+ threadData = univ.behaviorScheduler.getThreadData(null, null);
+ threadData.thread = univ.behaviorScheduler;
+ threadData.threadType = J3dThread.BEHAVIOR_SCHEDULER;
+ threadData.lastUpdateTime = Math.max(currentTime,
+ threadData.lastUpdateTime);
+ }
+
+ createUpdateThread(v.renderBin);
+ createUpdateThread(v.soundScheduler);
+
+ if (v.physicalEnvironment != null) {
+ v.physicalEnvironment.addUser(v);
+ }
+ // create InputDeviceScheduler
+ evaluatePhysicalEnv(v);
+
+ regUniverseList.addUnique(univ);
+ views.addUnique(v);
+ }
+
+
+
+ /**
+ * This unregister a View with MasterControl.
+ * The View no longer has any Canvas3Ds in a container.
+ */
+ private void unregisterView(View v) {
+
+ if (!views.remove(v)) {
+ v.active = false;
+ v.doneUnregister = true;
+ return; // already unregister
+ }
+
+ if (v.active) {
+ viewDeactivate(v);
+ }
+
+ if(J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
+ "MC: Destroy Sound Scheduler and RenderBin Update thread");
+ }
+
+ v.soundScheduler.updateThread.finish();
+ v.renderBin.updateThread.finish();
+ v.soundScheduler.updateThread = null;
+ v.renderBin.updateThread = null;
+
+ // remove VirtualUniverse related threads if Universe
+ // is empty
+ VirtualUniverse univ = v.universe;
+ int i;
+
+ synchronized (timeLock) {
+ // The reason we need to sync. with timeLock is because we
+ // don't want user thread running sendMessage() to
+ // dispatch it in different structure queue when
+ // part of the structure list is empty at the same time.
+ // This will cause inconsistence in the message reference
+ // count.
+ emptyMessageList(v.soundScheduler, v);
+ emptyMessageList(v.renderBin, v);
+
+ if (univ.isEmpty()) {
+ destroyUniverseThreads(univ);
+ } else {
+ emptyMessageList(univ.behaviorStructure, v);
+ emptyMessageList(univ.geometryStructure, v);
+ emptyMessageList(univ.soundStructure, v);
+ emptyMessageList(univ.renderingEnvironmentStructure, v);
+ emptyMessageList(univ.transformStructure, v);
+ }
+ }
+
+ if (v.physicalEnvironment != null) {
+ v.physicalEnvironment.removeUser(v);
+ }
+
+ // remove all InputDeviceScheduler if this is the last View
+ UnorderList list = new UnorderList(1, PhysicalEnvironment.class);
+ for (Enumeration e = PhysicalEnvironment.physicalEnvMap.keys();
+ e.hasMoreElements(); ) {
+ PhysicalEnvironment phyEnv = (PhysicalEnvironment) e.nextElement();
+ InputDeviceScheduler sched = (InputDeviceScheduler)
+ PhysicalEnvironment.physicalEnvMap.get(phyEnv);
+ for (i=phyEnv.users.size()-1; i>=0; i--) {
+ if (views.contains((View) phyEnv.users.get(i))) {
+ // at least one register view refer to it.
+ break;
+ }
+ }
+ if (i < 0) {
+ if(J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
+ "MC: Destroy InputDeviceScheduler thread "
+ + sched);
+ }
+ sched.finish();
+ phyEnv.inputsched = null;
+ list.add(phyEnv);
+ }
+ }
+ for (i=list.size()-1; i>=0; i--) {
+ PhysicalEnvironment.physicalEnvMap.remove(list.get(i));
+ }
+
+
+ freeContext(v);
+
+ if (views.isEmpty()) {
+ if(J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
+ "MC: Destroy all Renderers");
+ }
+ // remove all Renderers if this is the last View
+ for (Enumeration e = Screen3D.deviceRendererMap.elements();
+ e.hasMoreElements(); ) {
+ Renderer rdr = (Renderer) e.nextElement();
+ Screen3D scr;
+
+ rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
+ runMonitor(RUN_RENDERER_CLEANUP, null, null, null, rdr);
+ scr = rdr.onScreen;
+ if (scr != null) {
+ if (scr.renderer != null) {
+ rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
+ runMonitor(RUN_RENDERER_CLEANUP, null, null,
+ null, scr.renderer);
+ scr.renderer = null;
+ }
+
+ }
+ scr = rdr.offScreen;
+ if (scr != null) {
+ if (scr.renderer != null) {
+ rendererCleanupArgs[2] = REMOVEALLCTXS_CLEANUP;
+ runMonitor(RUN_RENDERER_CLEANUP, null, null,
+ null, scr.renderer);
+ scr.renderer = null;
+ }
+ }
+ rdr.onScreen = null;
+ rdr.offScreen = null;
+
+ }
+
+ // cleanup ThreadData corresponds to the view in renderer
+ for (Enumeration e = Screen3D.deviceRendererMap.elements();
+ e.hasMoreElements(); ) {
+ Renderer rdr = (Renderer) e.nextElement();
+ rdr.cleanup();
+ }
+ // We have to reuse renderer even though MC exit
+ // see bug 4363279
+ // Screen3D.deviceRendererMap.clear();
+
+ } else {
+ // cleanup ThreadData corresponds to the view in renderer
+ for (Enumeration e = Screen3D.deviceRendererMap.elements();
+ e.hasMoreElements(); ) {
+ Renderer rdr = (Renderer) e.nextElement();
+ rdr.cleanupView();
+ }
+ }
+
+
+ freeMessageList.add(univ);
+ freeMessageList.add(v);
+
+ evaluateAllCanvases();
+ stateWorkThreads.clear();
+ renderWorkThreads.clear();
+ requestRenderWorkThreads.clear();
+ threadListsChanged = true;
+
+ // This notify VirtualUniverse waitForMC() thread to continue
+ v.doneUnregister = true;
+ }
+
+
+ /**
+ * This procedure create MC thread that start together with MC.
+ */
+ void createMCThreads() {
+
+ // There is only one renderingAttributesUpdate Thread globally
+ createUpdateThread(renderingAttributesStructure);
+
+ // Create timer thread
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ synchronized (rootThreadGroup) {
+ timerThread = new TimerThread(rootThreadGroup);
+ timerThread.setPriority(threadPriority);
+ }
+ return null;
+ }
+ });
+ timerThread.start();
+ }
+
+ /**
+ * Destroy all VirtualUniverse related threads.
+ * This procedure may call two times when Locale detach in a
+ * live viewPlatform.
+ */
+ private void destroyUniverseThreads(VirtualUniverse univ) {
+
+ if (regUniverseList.contains(univ)) {
+ if (J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
+ "MC: Destroy universe threads " + univ);
+ }
+ destroyUpdateThread(univ.behaviorStructure);
+ destroyUpdateThread(univ.geometryStructure);
+ destroyUpdateThread(univ.soundStructure);
+ destroyUpdateThread(univ.renderingEnvironmentStructure);
+ destroyUpdateThread(univ.transformStructure);
+ univ.behaviorScheduler.finish();
+ univ.behaviorScheduler.free();
+ univ.behaviorScheduler = null;
+ univ.initMCStructure();
+ activeUniverseList.remove(univ);
+ regUniverseList.remove(univ);
+ } else {
+ emptyMessageList(univ.behaviorStructure, null);
+ emptyMessageList(univ.geometryStructure, null);
+ emptyMessageList(univ.soundStructure, null);
+ emptyMessageList(univ.renderingEnvironmentStructure, null);
+ emptyMessageList(univ.transformStructure, null);
+ }
+
+ if (regUniverseList.isEmpty() && views.isEmpty()) {
+ if(J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
+ "MC: Destroy RenderingAttributes Update and Timer threads");
+ }
+ if (renderingAttributesStructure.updateThread != null) {
+ renderingAttributesStructure.updateThread.finish();
+ renderingAttributesStructure.updateThread = null;
+ }
+ renderingAttributesStructure.messageList.clear();
+ renderingAttributesStructure.objList = new ArrayList();
+ renderingAttributesStructure = new RenderingAttributesStructure();
+ if (timerThread != null) {
+ timerThread.finish();
+ timerThread = null;
+ }
+
+ // shouldn't all of these be synchronized ???
+ synchronized (VirtualUniverse.mc.deviceScreenMap) {
+ deviceScreenMap.clear();
+ }
+ FreeListManager.clearList(FreeListManager.MESSAGE);
+ FreeListManager.clearList(FreeListManager.BHLEAF);
+ FreeListManager.clearList(FreeListManager.BHINTERNAL);
+ mirrorObjects.clear();
+ // Note: We should not clear the DISPLAYLIST/TEXTURE
+ // list here because other structure may release them
+ // later
+
+ FreeListManager.clearList(FreeListManager.CANVASBIT);
+ canvasBitCount = 0;
+ renderOnceList.clear();
+ timestampUpdateList.clear();
+
+ FreeListManager.clearList(FreeListManager.TRANSFORM3D);
+ defaultRenderMethod = null;
+ text3DRenderMethod = null;
+ vertexArrayRenderMethod = null;
+ displayListRenderMethod = null;
+ compressedGeometryRenderMethod = null;
+ orientedShape3DRenderMethod = null;
+ // Terminate MC thread
+ running = false;
+ }
+ }
+
+ /**
+ * Note that we have to go through all views instead of
+ * evaluate only the canvas in a single view since each screen
+ * may share by multiple view
+ */
+ private void evaluateAllCanvases() {
+
+ synchronized (renderThreadData) {
+ // synchronized to prevent lost message when
+ // renderThreadData is clear
+
+ // First remove all renderrenderThreadData
+ renderThreadData.clear();
+
+ // Second reset canvasCount to zero
+ View viewArr[] = (View []) views.toArray(false);
+ for (int i=views.size()-1; i>=0; i--) {
+ viewArr[i].getCanvasList(true); // force canvas cache update
+ Screen3D screens[] = viewArr[i].getScreens();
+ for (int j=screens.length-1; j>=0; j--) {
+ screens[j].canvasCount = 0;
+ }
+ }
+
+
+ // Third create render thread and message thread
+ for (int i=views.size()-1; i>=0; i--) {
+ View v = viewArr[i];
+ Canvas3D canvasList[][] = v.getCanvasList(false);
+ if (!v.active) {
+ continue;
+ }
+
+ for (int j=canvasList.length-1; j>=0; j--) {
+ boolean added = false;
+
+ for (int k=canvasList[j].length-1; k>=0; k--) {
+ Canvas3D cv = canvasList[j][k];
+
+ final Screen3D screen = cv.screen;
+
+ if (cv.active) {
+ if (screen.canvasCount++ == 0) {
+ // Create Renderer, one per screen
+ if (screen.renderer == null) {
+ // get the renderer created for the graphics
+ // device of the screen of the canvas
+ // No need to synchronized since only
+ // MC use it.
+ Renderer rdr =
+ (Renderer) screen.deviceRendererMap.get(
+ cv.screen.graphicsDevice);
+ if (rdr == null) {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+
+ synchronized (rootThreadGroup) {
+ screen.renderer
+ = new Renderer(
+ rootThreadGroup);
+ screen.renderer.setPriority(threadPriority);
+ }
+ return null;
+ }
+ });
+ screen.renderer.initialize();
+ screen.deviceRendererMap.put(
+ screen.graphicsDevice, screen.renderer);
+ } else {
+ screen.renderer = rdr;
+ }
+ }
+ }
+ // offScreen canvases will be handled by the
+ // request renderer, so don't add offScreen canvas
+ // the render list
+ if (!cv.offScreen) {
+ screen.renderer.onScreen = screen;
+ } else {
+ screen.renderer.offScreen = screen;
+ continue;
+ }
+
+ if (!added) {
+ // Swap message data thread, one per
+ // screen only. Note that we don't set
+ // lastUpdateTime for this thread so
+ // that it won't run in the first round
+ J3dThreadData renderData =
+ screen.renderer.getThreadData(v, null);
+ renderThreadData.add(renderData);
+
+ // only if renderBin is ready then we
+ // update the lastUpdateTime to make it run
+ if (v.renderBinReady) {
+ renderData.lastUpdateTime =
+ Math.max(currentTime,
+ renderData.lastUpdateTime);
+ }
+ added = true;
+ }
+ // Renderer message data thread
+ J3dThreadData renderData =
+ screen.renderer.getThreadData(v, cv);
+ renderThreadData.add(renderData);
+ if (v.renderBinReady) {
+ renderData.lastUpdateTime =
+ Math.max(currentTime,
+ renderData.lastUpdateTime);
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ threadListsChanged = true;
+ }
+
+ private void evaluatePhysicalEnv(View v) {
+ final PhysicalEnvironment env = v.physicalEnvironment;
+
+ if (env.inputsched == null) {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ synchronized (rootThreadGroup) {
+ env.inputsched = new InputDeviceScheduler(
+ rootThreadGroup,
+ env);
+ env.inputsched.setPriority(threadPriority);
+ }
+ return null;
+ }
+ });
+ env.inputsched.start();
+ PhysicalEnvironment.physicalEnvMap.put(env, env.inputsched);
+ }
+ threadListsChanged = true;
+ }
+
+ final private void addToStateThreads(J3dThreadData threadData) {
+ if (threadData.thread.active) {
+ stateWorkThreads.add(threadData);
+ }
+ }
+
+
+ private void assignNewPrimaryView(VirtualUniverse univ) {
+
+ View currentPrimary = univ.getCurrentView();
+
+ if (currentPrimary != null) {
+ currentPrimary.primaryView = false;
+ }
+
+ View v[] = (View []) views.toArray(false);
+ int nviews = views.size();
+ for (int i=0; i<nviews; i++) {
+ View view = v[i];
+ if (view.active && view.isRunning &&
+ (univ == view.universe)) {
+ view.primaryView = true;
+ univ.setCurrentView(view);
+ return;
+ }
+ }
+ univ.setCurrentView(null);
+ }
+
+
+ /**
+ * This returns the default RenderMethod
+ */
+ RenderMethod getDefaultRenderMethod() {
+ if (defaultRenderMethod == null) {
+ defaultRenderMethod = new DefaultRenderMethod();
+ }
+ return defaultRenderMethod;
+ }
+
+ /**
+ * This returns the text3d RenderMethod
+ */
+ RenderMethod getText3DRenderMethod() {
+ if (text3DRenderMethod == null) {
+ text3DRenderMethod = new Text3DRenderMethod();
+ }
+ return text3DRenderMethod;
+ }
+
+
+ /**
+ * This returns the vertexArray RenderMethod
+ */
+ RenderMethod getVertexArrayRenderMethod() {
+ if (vertexArrayRenderMethod == null) {
+ vertexArrayRenderMethod = new VertexArrayRenderMethod();
+ }
+ return vertexArrayRenderMethod;
+ }
+
+ /**
+ * This returns the displayList RenderMethod
+ */
+ RenderMethod getDisplayListRenderMethod() {
+ if (displayListRenderMethod == null) {
+ displayListRenderMethod = new DisplayListRenderMethod();
+ }
+ return displayListRenderMethod;
+ }
+
+ /**
+ * This returns the compressed geometry RenderMethod
+ */
+ RenderMethod getCompressedGeometryRenderMethod() {
+ if (compressedGeometryRenderMethod == null) {
+ compressedGeometryRenderMethod =
+ new CompressedGeometryRenderMethod();
+ }
+ return compressedGeometryRenderMethod;
+ }
+
+ /**
+ * This returns the oriented shape3d RenderMethod
+ */
+ RenderMethod getOrientedShape3DRenderMethod() {
+ if (orientedShape3DRenderMethod == null) {
+ orientedShape3DRenderMethod = new OrientedShape3DRenderMethod();
+ }
+ return orientedShape3DRenderMethod;
+ }
+
+ /**
+ * This notifies MasterControl that the given view has been activated
+ */
+ private void viewActivate(View v) {
+
+ VirtualUniverse univ = v.universe;
+
+ if (univ == null) {
+ return;
+ }
+
+ if (!views.contains(v) || !regUniverseList.contains(univ)) {
+ registerView(v);
+ } else if (v.active) {
+ evaluateAllCanvases();
+ return;
+ }
+
+ if ((univ.activeViewCount == 0)) {
+ univ.geometryStructure.resetConditionMet();
+ univ.behaviorStructure.resetConditionMet();
+ }
+
+ if (v.isRunning) {
+ numActiveViews++;
+ univ.activeViewCount++;
+ renderingAttributesStructure.updateThread.active = true;
+ univ.transformStructure.updateThread.active = true;
+ univ.geometryStructure.updateThread.active = true;
+ univ.soundStructure.updateThread.active = true;
+ univ.renderingEnvironmentStructure.updateThread.active = true;
+ }
+ univ.behaviorScheduler.active = true;
+ univ.behaviorStructure.updateThread.active = true;
+
+
+ activeUniverseList.addUnique(univ);
+
+ if (v.isRunning) {
+ v.soundScheduler.activate();
+ v.renderBin.updateThread.active = true;
+ }
+ v.active = true;
+
+ if (v.physicalEnvironment.activeViewRef++ == 0) {
+ v.physicalEnvironment.inputsched.activate();
+ }
+
+
+ if (univ.getCurrentView() == null) {
+ assignNewPrimaryView(univ);
+ }
+
+ evaluateAllCanvases();
+ v.inRenderThreadData = true;
+ threadListsChanged = true;
+ // Notify GeometryStructure to query visible atom again
+ // We should send message instead of just setting
+ // v.vDirtyMask = View.VISIBILITY_POLICY_DIRTY;
+ // since RenderBin may not run immediately next time.
+ // In this case the dirty flag will lost since
+ // updateViewCache() will reset it to 0.
+ v.renderBin.reactivateView = true;
+ }
+
+ /**
+ * Release context associate with view
+ */
+ private void freeContext(View v) {
+ Canvas3D[][] canvasList = v.getCanvasList(false);
+
+ for (int j=canvasList.length-1; j>=0; j--) {
+ for (int k=canvasList[j].length-1; k>=0; k--) {
+ Canvas3D cv = canvasList[j][k];
+ if (!cv.validCanvas) {
+ if ((cv.screen != null) &&
+ (cv.screen.renderer != null)) {
+ rendererCleanupArgs[1] = cv;
+ rendererCleanupArgs[2] = FREECONTEXT_CLEANUP;
+ runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
+ cv.screen.renderer);
+ rendererCleanupArgs[1] = null;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This notifies MasterControl that the given view has been deactivated
+ */
+ private void viewDeactivate(View v) {
+
+ if (!views.contains(v) || !v.active) {
+ v.active = false;
+ evaluateAllCanvases();
+ return;
+ }
+
+ VirtualUniverse univ = v.universe;
+
+ if (v.isRunning) {
+ // if stopView() invoke before, no need to decrement count
+ --numActiveViews;
+ --univ.activeViewCount;
+ }
+
+ if (numActiveViews == 0) {
+ renderingAttributesStructure.updateThread.active = false;
+ }
+
+ if (univ.activeViewCount == 0) {
+ // check if destroyUniverseThread invoked before
+ if (univ.behaviorScheduler != null) {
+ univ.behaviorScheduler.deactivate();
+ univ.transformStructure.updateThread.active = false;
+ univ.geometryStructure.updateThread.active = false;
+ univ.behaviorStructure.updateThread.active = false;
+ univ.soundStructure.updateThread.active = false;
+ univ.renderingEnvironmentStructure.updateThread.active
+ = false;
+ activeUniverseList.remove(univ);
+ }
+ }
+
+ v.soundScheduler.deactivate();
+ v.renderBin.updateThread.active = false;
+ v.active = false;
+ if (--v.physicalEnvironment.activeViewRef == 0) {
+ v.physicalEnvironment.inputsched.deactivate();
+ }
+ assignNewPrimaryView(univ);
+
+
+ evaluateAllCanvases();
+
+ freeContext(v);
+
+ v.inRenderThreadData = false;
+ threadListsChanged = true;
+ }
+
+
+ /**
+ * This notifies MasterControl to start given view
+ */
+ private void startView(View v) {
+
+ if (!views.contains(v) || v.isRunning || !v.active) {
+ v.isRunning = true;
+ return;
+ }
+
+ numActiveViews++;
+ renderingAttributesStructure.updateThread.active = true;
+
+ VirtualUniverse univ = v.universe;
+
+ univ.activeViewCount++;
+ univ.transformStructure.updateThread.active = true;
+ univ.geometryStructure.updateThread.active = true;
+ univ.soundStructure.updateThread.active = true;
+ univ.renderingEnvironmentStructure.updateThread.active = true;
+ v.renderBin.updateThread.active = true;
+ v.soundScheduler.activate();
+ v.isRunning = true;
+ if (univ.getCurrentView() == null) {
+ assignNewPrimaryView(univ);
+ }
+ threadListsChanged = true;
+ }
+
+
+ /**
+ * This notifies MasterControl to stop given view
+ */
+ private void stopView(View v) {
+ if (!views.contains(v) || !v.isRunning || !v.active) {
+ v.isRunning = false;
+ return;
+ }
+
+ if (--numActiveViews == 0) {
+ renderingAttributesStructure.updateThread.active = false;
+ }
+ VirtualUniverse univ = v.universe;
+
+ if (--univ.activeViewCount == 0) {
+ univ.transformStructure.updateThread.active = false;
+ univ.geometryStructure.updateThread.active = false;
+ univ.renderingEnvironmentStructure.updateThread.active = false;
+ univ.soundStructure.updateThread.active = false;
+ }
+
+ v.renderBin.updateThread.active = false;
+ v.soundScheduler.deactivate();
+ v.isRunning = false;
+ assignNewPrimaryView(univ);
+ threadListsChanged = true;
+ }
+
+ // Call from user thread
+ void addInputDeviceScheduler(InputDeviceScheduler ds) {
+ synchronized (inputDeviceThreads) {
+ inputDeviceThreads.add(ds);
+ if (inputDeviceThreads.size() == 1) {
+ timerThread.addInputDeviceSchedCond();
+ }
+ }
+ postRequest(INPUTDEVICE_CHANGE, null);
+ }
+
+ // Call from user thread
+ void removeInputDeviceScheduler(InputDeviceScheduler ds) {
+ inputDeviceThreads.remove(ds);
+ postRequest(INPUTDEVICE_CHANGE, null);
+ }
+
+ /**
+ * Add an object to the mirror object list
+ */
+ void addMirrorObject(ObjectUpdate o) {
+ mirrorObjects.add(o);
+ }
+
+ /**
+ * This updates any mirror objects. It is called when threads
+ * are done.
+ */
+ void updateMirrorObjects() {
+ ObjectUpdate objs[] = (ObjectUpdate []) mirrorObjects.toArray(false);
+ int sz = mirrorObjects.arraySize();
+
+ for (int i = 0; i< sz; i++) {
+ objs[i].updateObject();
+ }
+ mirrorObjects.clear();
+ }
+
+
+ /**
+ * This fun little method does all the hard work of setting up the
+ * work thread list.
+ */
+ private void updateWorkThreads() {
+
+ stateWorkThreads.clear();
+ renderWorkThreads.clear();
+ requestRenderWorkThreads.clear();
+
+ // First the global rendering attributes structure update
+ if (numActiveViews > 0) {
+ addToStateThreads(renderingAttributesStructure.getUpdateThreadData());
+ }
+
+ // Next, each of the transform structure updates
+ VirtualUniverse universes[] = (VirtualUniverse [])
+ activeUniverseList.toArray(false);
+ VirtualUniverse univ;
+ int i;
+ int size = activeUniverseList.arraySize();
+
+ for (i=size-1; i>=0; i--) {
+ addToStateThreads(universes[i].transformStructure.getUpdateThreadData());
+ }
+ lastTransformStructureThread = stateWorkThreads.size();
+
+ // Next, the GeometryStructure, BehaviorStructure,
+ // RenderingEnvironmentStructure, and SoundStructure
+ for (i=size-1; i>=0; i--) {
+ univ = universes[i];
+ addToStateThreads(univ.geometryStructure.getUpdateThreadData());
+ addToStateThreads(univ.behaviorStructure.getUpdateThreadData());
+ addToStateThreads(univ.renderingEnvironmentStructure.getUpdateThreadData());
+ addToStateThreads(univ.soundStructure.getUpdateThreadData());
+ }
+
+ lastStructureUpdateThread = stateWorkThreads.size();
+
+ // Next, the BehaviorSchedulers
+ for (i=size-1; i>=0; i--) {
+ addToStateThreads(universes[i].behaviorScheduler.
+ getThreadData(null, null));
+ }
+
+
+ // Now InputDeviceScheduler
+
+ InputDeviceScheduler ds[] = (InputDeviceScheduler [])
+ inputDeviceThreads.toArray(true);
+ for (i=inputDeviceThreads.size()-1; i >=0; i--) {
+ J3dThreadData threadData = ds[i].getThreadData();
+ threadData.thread.active = true;
+ addToStateThreads(threadData);
+ }
+
+ // Now the RenderBins and SoundSchedulers
+ View viewArr[] = (View []) views.toArray(false);
+ J3dThreadData thread;
+
+ for (i=views.size()-1; i>=0; i--) {
+ View v = viewArr[i];
+ if (v.active && v.isRunning) {
+ addToStateThreads(v.renderBin.getUpdateThreadData());
+ addToStateThreads(v.soundScheduler.getUpdateThreadData());
+ Canvas3D canvasList[][] = v.getCanvasList(false);
+ int longestScreenList = v.getLongestScreenList();
+ Object args[] = null;
+ // renderer render
+ for (int j=0; j<longestScreenList; j++) {
+ for (int k=0; k < canvasList.length; k++) {
+ if (j < canvasList[k].length) {
+ Canvas3D cv = canvasList[k][j];
+ if (cv.active && cv.isRunningStatus && !cv.offScreen) {
+ if (cv.screen.renderer == null) {
+ continue;
+ }
+ thread = cv.screen.renderer.getThreadData(v, cv);
+ renderWorkThreads.add(thread);
+ args = (Object []) thread.threadArgs;
+ args[0] = RENDER;
+ args[1] = cv;
+ args[2] = v;
+ }
+ }
+ }
+ }
+
+ // renderer swap
+ for (int j=0; j<canvasList.length; j++) {
+ for (int k=0; k < canvasList[j].length; k++) {
+ Canvas3D cv = canvasList[j][k];
+ // create swap thread only if there is at
+ // least one active canvas
+ if (cv.active && cv.isRunningStatus && !cv.offScreen) {
+ if (cv.screen.renderer == null) {
+ // Should not happen
+ continue;
+ }
+ thread = cv.screen.renderer.getThreadData(v, null);
+ renderWorkThreads.add(thread);
+ args = (Object []) thread.threadArgs;
+ args[0] = SWAP;
+ args[1] = v;
+ args[2] = canvasList[j];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ thread = null;
+
+ for (Enumeration e = Screen3D.deviceRendererMap.elements();
+ e.hasMoreElements(); ) {
+ Renderer rdr = (Renderer) e.nextElement();
+ thread = rdr.getThreadData(null, null);
+ requestRenderWorkThreads.add(thread);
+ thread.threadOpts = J3dThreadData.CONT_THREAD;
+ ((Object[]) thread.threadArgs)[0] = REQUESTRENDER;
+ }
+
+ if (thread != null) {
+ thread.threadOpts |= J3dThreadData.WAIT_ALL_THREADS;
+ }
+
+ threadListsChanged = false;
+
+ // dumpWorkThreads();
+ }
+
+
+ void dumpWorkThreads() {
+ System.err.println("-----------------------------");
+ System.err.println("MasterControl/dumpWorkThreads");
+
+ J3dThreadData threads[];
+ int size = 0;
+
+ for (int k=0; k<3; k++) {
+ switch (k) {
+ case 0:
+ threads = (J3dThreadData []) stateWorkThreads.toArray(false);
+ size = stateWorkThreads.arraySize();
+ break;
+ case 1:
+ threads = (J3dThreadData []) renderWorkThreads.toArray(false);
+ size = renderWorkThreads.arraySize();
+ break;
+ default:
+ threads = (J3dThreadData []) requestRenderWorkThreads.toArray(false);
+ size = requestRenderWorkThreads.arraySize();
+ break;
+ }
+
+ for (int i=0; i<size; i++) {
+ J3dThreadData thread = threads[i];
+ System.err.println("Thread " + i + ": " + thread.thread);
+ System.err.println("\tOps: " + thread.threadOpts);
+ if (thread.threadArgs != null) {
+ Object[] args = (Object[]) thread.threadArgs;
+ System.err.print("\tArgs: ");
+ for (int j=0; j<args.length; j++) {
+ System.err.print(args[j] + " ");
+ }
+ }
+ System.err.println("");
+ }
+ }
+ System.err.println("-----------------------------");
+ }
+
+
+ /**
+ * A convienence wrapper function for various parts of the system
+ * to force MC to run.
+ */
+ final void setWork() {
+ runMonitor(SET_WORK, null, null, null, null);
+ }
+
+ final void setWorkForRequestRenderer() {
+ runMonitor(SET_WORK_FOR_REQUEST_RENDERER, null, null, null, null);
+ }
+
+ /**
+ * Call from GraphicsConfigTemplate to evaluate current
+ * capabilities using Renderer thread to invoke native
+ * graphics library functions. This avoid MT-safe problem
+ * when using thread directly invoke graphics functions.
+ */
+ void sendRenderMessage(GraphicsConfiguration gc,
+ Object arg, Integer mtype) {
+ Renderer rdr = createRenderer(gc);
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = null;
+ renderMessage.args[1] = arg;
+ renderMessage.args[2] = mtype;
+ rdr.rendererStructure.addMessage(renderMessage);
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+
+ /**
+ * This is the MasterControl work method for Java 3D
+ */
+ void doWork() {
+ runMonitor(CHECK_FOR_WORK, null, null, null, null);
+
+ if (pendingRequest) {
+ synchronized (timeLock) {
+ synchronized (requestObjList) {
+ handlePendingRequest();
+ }
+ }
+ }
+
+ if (!running) {
+ return;
+ }
+
+ if (threadListsChanged) { // Check for new Threads
+ updateWorkThreads();
+ }
+
+ synchronized (timeLock) {
+ // This is neccesary to prevent updating
+ // thread.lastUpdateTime from user thread
+ // in sendMessage() or sendRunMessage()
+ updateTimeValues();
+ }
+
+ //This is temporary until the view model is updated
+ View v[] = (View []) views.toArray(false);
+ for (int i=views.size()-1; i>=0; i--) {
+ if (v[i].active) {
+ v[i].updateViewCache();
+ // update OrientedShape3D
+ if ((v[i].viewCache.vcDirtyMask != 0 &&
+ !v[i].renderBin.orientedRAs.isEmpty()) ||
+ (v[i].renderBin.cachedDirtyOrientedRAs != null &&
+ !v[i].renderBin.cachedDirtyOrientedRAs.isEmpty())) {
+ v[i].renderBin.updateOrientedRAs();
+ }
+ }
+ }
+
+ runMonitor(RUN_THREADS, stateWorkThreads, renderWorkThreads,
+ requestRenderWorkThreads, null);
+
+ if (renderOnceList.size() > 0) {
+ clearRenderOnceList();
+ }
+
+ manageMemory();
+
+ }
+
+ private void handlePendingRequest() {
+
+ Object objs[];
+ Integer types[];
+ int size;
+ boolean rendererRun = false;
+
+ objs = requestObjList.toArray(false);
+ types = (Integer []) requestTypeList.toArray(false);
+ size = requestObjList.size();
+
+ for (int i=0; i < size; i++) {
+ // need to process request in order
+ Integer type = types[i];
+ Object o = objs[i];
+ if (type == RESET_CANVAS) {
+ Canvas3D cv = (Canvas3D) o;
+ if ((cv.screen != null) &&
+ (cv.screen.renderer != null)) {
+ rendererCleanupArgs[1] = o;
+ rendererCleanupArgs[2] = RESETCANVAS_CLEANUP;
+ runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
+ cv.screen.renderer);
+ rendererCleanupArgs[1] = null;
+ }
+ cv.reset();
+ cv.view = null;
+ cv.computeViewCache();
+ }
+ else if (type == ACTIVATE_VIEW) {
+ viewActivate((View) o);
+ }
+ else if (type == DEACTIVATE_VIEW) {
+ viewDeactivate((View) o);
+ } else if (type == REEVALUATE_CANVAS) {
+ evaluateAllCanvases();
+ } else if (type == INPUTDEVICE_CHANGE) {
+ inputDeviceThreads.clearMirror();
+ threadListsChanged = true;
+ } else if (type == START_VIEW) {
+ startView((View) o);
+ } else if (type == STOP_VIEW) {
+ View v = (View) o;
+ // Collision takes 3 rounds to finish its request
+ if (++v.stopViewCount > 4) {
+ v.stopViewCount = -1; // reset counter
+ stopView(v);
+ } else {
+ tempViewList.add(v);
+ }
+ } else if (type == UNREGISTER_VIEW) {
+ unregisterView((View) o);
+ } else if (type == PHYSICAL_ENV_CHANGE) {
+ evaluatePhysicalEnv((View) o);
+ } else if (type == EMPTY_UNIVERSE) {
+ if (views.isEmpty()) {
+ destroyUniverseThreads((VirtualUniverse) o);
+ threadListsChanged = true;
+ }
+ } else if (type == START_RENDERER) {
+ ((Canvas3D) o).isRunningStatus = true;
+ threadListsChanged = true;
+ } else if (type == STOP_RENDERER) {
+ if (o instanceof Canvas3D) {
+ ((Canvas3D) o).isRunningStatus = false;
+ } else {
+ ((Renderer) o).userStop = true;
+ }
+ threadListsChanged = true;
+ } else if (type == RENDER_ONCE) {
+ View v = (View) o;
+ // temporary start View for renderonce
+ // it will stop afterwards
+ startView(v);
+ renderOnceList.add(v);
+ sendRunMessage(v, J3dThread.UPDATE_RENDER);
+ threadListsChanged = true;
+ rendererRun = true;
+ } else if (type == FREE_CONTEXT) {
+ Canvas3D cv = (Canvas3D ) ((Object []) o)[0];
+ if ((cv.screen != null) &&
+ (cv.screen.renderer != null)) {
+ rendererCleanupArgs[1] = o;
+ rendererCleanupArgs[2] = REMOVECTX_CLEANUP;
+ runMonitor(RUN_RENDERER_CLEANUP, null, null, null,
+ cv.screen.renderer);
+ rendererCleanupArgs[1] = null;
+ }
+ rendererRun = true;
+ } else if (type == FREE_DRAWING_SURFACE) {
+ DrawingSurfaceObjectAWT.freeDrawingSurface(o);
+ } else if (type == GETBESTCONFIG) {
+ GraphicsConfiguration gc = ((GraphicsConfiguration [])
+ ((GraphicsConfigTemplate3D) o).testCfg)[0];
+ sendRenderMessage(gc, o, type);
+ rendererRun = true;
+ } else if (type == ISCONFIGSUPPORT) {
+ GraphicsConfiguration gc = (GraphicsConfiguration)
+ ((GraphicsConfigTemplate3D) o).testCfg;
+ sendRenderMessage(gc, o, type);
+ rendererRun = true;
+ } else if ((type == SET_GRAPHICSCONFIG_FEATURES) ||
+ (type == SET_QUERYPROPERTIES)) {
+ GraphicsConfiguration gc = (GraphicsConfiguration)
+ ((Canvas3D) o).graphicsConfiguration;
+ sendRenderMessage(gc, o, type);
+ rendererRun = true;
+ } else if (type == SET_VIEW) {
+ Canvas3D cv = (Canvas3D) o;
+ cv.view = cv.pendingView;
+ cv.computeViewCache();
+ }
+ }
+
+ // Do it only after all universe/View is register
+ for (int i=0; i < size; i++) {
+ Integer type = types[i];
+ if (type == FREE_MESSAGE) {
+ if (objs[i] instanceof VirtualUniverse) {
+ VirtualUniverse u = (VirtualUniverse) objs[i];
+ if (!regUniverseList.contains(u)) {
+ emptyMessageList(u.behaviorStructure, null);
+ emptyMessageList(u.geometryStructure, null);
+ emptyMessageList(u.soundStructure, null);
+ emptyMessageList(u.renderingEnvironmentStructure, null);
+ }
+ } else if (objs[i] instanceof View) {
+ View v = (View) objs[i];
+ if (!views.contains(v)) {
+ emptyMessageList(v.soundScheduler, v);
+ emptyMessageList(v.renderBin, v);
+ if (v.resetUnivCount == v.universeCount) {
+ v.reset();
+ v.universe = null;
+ if (running == false) {
+ // MC is about to terminate
+
+ /*
+ // Don't free list cause there may
+ // have some other thread returning ID
+ // after it.
+ FreeListManager.clearList(FreeListManager.DISPLAYLIST);
+ FreeListManager.clearList(FreeListManager.TEXTURE2D);
+ FreeListManager.clearList(FreeListManager.TEXTURE3D);
+
+ synchronized (textureIdLock) {
+ textureIdCount = 0;
+ }
+ */
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+ requestObjList.clear();
+ requestTypeList.clear();
+
+ size = tempViewList.size();
+ if (size > 0) {
+ if (running) {
+ for (int i=0; i < size; i++) {
+ requestTypeList.add(STOP_VIEW);
+ requestObjList.add(tempViewList.get(i));
+ }
+ setWork();
+ } else { // MC will shutdown
+ for (int i=0; i < size; i++) {
+ View v = (View) tempViewList.get(i);
+ v.stopViewCount = -1;
+ v.isRunning = false;
+ }
+ }
+ tempViewList.clear();
+ pendingRequest = true;
+ } else {
+ pendingRequest = rendererRun || (requestObjList.size() > 0);
+
+ }
+
+ size = freeMessageList.size();
+ if (size > 0) {
+ for (int i=0; i < size; i++) {
+ requestTypeList.add(FREE_MESSAGE);
+ requestObjList.add(freeMessageList.get(i));
+ }
+ pendingRequest = true;
+ freeMessageList.clear();
+ }
+ if (!running && (renderOnceList.size() > 0)) {
+ clearRenderOnceList();
+ }
+
+ if (pendingRequest) {
+ setWork();
+ }
+
+ if (rendererRun) {
+ running = true;
+ }
+
+ }
+
+ private void clearRenderOnceList() {
+ for (int i=renderOnceList.size()-1; i>=0; i--) {
+ View v = (View) renderOnceList.get(i);
+ v.renderOnceFinish = true;
+ // stop after render once
+ stopView(v);
+ }
+ renderOnceList.clear();
+ threadListsChanged = true;
+
+ }
+
+ synchronized void runMonitor(int action,
+ UnorderList stateThreadList,
+ UnorderList renderThreadList,
+ UnorderList requestRenderThreadList,
+ J3dThread nthread) {
+
+ switch (action) {
+ case RUN_THREADS:
+ int currentStateThread = 0;
+ int currentRenderThread = 0;
+ int currentRequestRenderThread = 0;
+ View view;
+ boolean done;
+ J3dThreadData thread;
+ J3dThreadData renderThreads[] = (J3dThreadData [])
+ renderThreadList.toArray(false);
+ J3dThreadData stateThreads[] = (J3dThreadData [])
+ stateThreadList.toArray(false);
+ J3dThreadData requestRenderThreads[] = (J3dThreadData [])
+ requestRenderThreadList.toArray(false);
+ int renderThreadSize = renderThreadList.arraySize();
+ int stateThreadSize = stateThreadList.arraySize();
+ int requestRenderThreadSize = requestRenderThreadList.arraySize();
+
+ done = false;
+
+ //lock all the needed geometry and image component
+ View[] allView = (View []) views.toArray(false);
+ View currentV;
+ int i;
+
+ if (lockGeometry)
+ {
+ for( i = views.arraySize()-1; i >= 0; i--) {
+ currentV = allView[i];
+ currentV.renderBin.lockGeometry();
+ }
+ }
+
+
+ while (!done) {
+ // First try a RenderThread
+ while (!renderWaiting &&
+ currentRenderThread != renderThreadSize) {
+ thread = renderThreads[currentRenderThread++];
+ if (!thread.needsRun) {
+ continue;
+ }
+ if ((thread.threadOpts & J3dThreadData.START_TIMER) != 0) {
+ view = (View)((Object[])thread.threadArgs)[2];
+ view.frameNumber++;
+ view.startTime = System.currentTimeMillis();
+ }
+
+
+ renderPending++;
+
+ if (cpuLimit == 1) {
+ thread.thread.args = (Object[])thread.threadArgs;
+ thread.thread.doWork(currentTime);
+ } else {
+ threadPending++;
+ thread.thread.runMonitor(J3dThread.RUN,
+ currentTime,
+ (Object[])thread.threadArgs);
+ }
+
+ if ((thread.threadOpts & J3dThreadData.STOP_TIMER) != 0) {
+ view = (View)((Object[])thread.threadArgs)[3];
+ timestampUpdateList.add(view);
+ }
+
+ if ((thread.threadOpts & J3dThreadData.LAST_STOP_TIMER) != 0) {
+ // release lock on locked geometry and image component
+ for( i = 0; i < views.arraySize(); i++) {
+ currentV = allView[i];
+ currentV.renderBin.releaseGeometry();
+ }
+ }
+
+ if ((cpuLimit != 1) &&
+ (thread.threadOpts &
+ J3dThreadData.WAIT_ALL_THREADS) != 0) {
+
+ renderWaiting = true;
+ }
+
+
+ if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
+ state = WAITING_FOR_CPU;
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ state = RUNNING;
+ }
+
+ }
+ // Now try state threads
+ while (!stateWaiting &&
+ currentStateThread != stateThreadSize) {
+ thread = stateThreads[currentStateThread++];
+
+ if (!thread.needsRun) {
+ continue;
+ }
+
+ statePending++;
+
+ if (cpuLimit == 1) {
+ thread.thread.args = (Object[])thread.threadArgs;
+ thread.thread.doWork(currentTime);
+ } else {
+ threadPending++;
+ thread.thread.runMonitor(J3dThread.RUN,
+ currentTime,
+ (Object[])thread.threadArgs);
+ }
+ if (cpuLimit != 1 && (thread.threadOpts &
+ J3dThreadData.WAIT_ALL_THREADS) != 0) {
+ stateWaiting = true;
+ }
+
+ if ((cpuLimit != 1) && (cpuLimit <= threadPending)) {
+ // Fix bug 4686766 - always allow
+ // renderer thread to continue if not finish
+ // geomLock can release for Behavior thread to
+ // continue.
+ if (currentRenderThread == renderThreadSize) {
+ state = WAITING_FOR_CPU;
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ state = RUNNING;
+ } else {
+ // Run renderer thread next time
+ break;
+ }
+
+ }
+ }
+
+ // Now try requestRender threads
+ if (!renderWaiting &&
+ (currentRenderThread == renderThreadSize)) {
+ currentRequestRenderThread = 0;
+ while (!renderWaiting &&
+ (currentRequestRenderThread !=
+ requestRenderThreadSize)) {
+
+ thread =
+ requestRenderThreads[currentRequestRenderThread++];
+
+ renderPending++;
+
+ if (cpuLimit == 1) {
+ thread.thread.args = (Object[])thread.threadArgs;
+ thread.thread.doWork(currentTime);
+ } else {
+ threadPending++;
+ thread.thread.runMonitor(J3dThread.RUN,
+ currentTime,
+ (Object[])thread.threadArgs);
+ }
+ if (cpuLimit != 1 && (thread.threadOpts &
+ J3dThreadData.WAIT_ALL_THREADS) != 0) {
+ renderWaiting = true;
+ }
+ if (cpuLimit != 1 && cpuLimit <= threadPending) {
+ state = WAITING_FOR_CPU;
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ state = RUNNING;
+ }
+ }
+ }
+
+ if (cpuLimit != 1) {
+ if ((renderWaiting &&
+ (currentStateThread == stateThreadSize)) ||
+ (stateWaiting &&
+ currentRenderThread == renderThreadSize) ||
+ (renderWaiting && stateWaiting)) {
+ if (!requestRenderWorkToDo) {
+ state = WAITING_FOR_THREADS;
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ state = RUNNING;
+ }
+ requestRenderWorkToDo = false;
+ }
+ }
+
+ if ((currentStateThread == stateThreadSize) &&
+ (currentRenderThread == renderThreadSize) &&
+ (currentRequestRenderThread == requestRenderThreadSize) &&
+ (threadPending == 0)) {
+ for (int k=timestampUpdateList.size()-1; k >=0;
+ k--) {
+ View v = (View) timestampUpdateList.get(k);
+ v.setFrameTimingValues();
+ v.universe.behaviorStructure.incElapsedFrames();
+ }
+ timestampUpdateList.clear();
+ updateMirrorObjects();
+ done = true;
+ }
+ }
+ break;
+ case THREAD_DONE:
+ if (state != WAITING_FOR_RENDERER_CLEANUP) {
+ threadPending--;
+ if (nthread.type == J3dThread.RENDER_THREAD) {
+ View v = (View) nthread.args[3];
+ if (v != null) { // STOP_TIMER
+ v.stopTime = System.currentTimeMillis();
+ }
+
+ if (--renderPending == 0) {
+ renderWaiting = false;
+ }
+ } else {
+ if (--statePending == 0) {
+ stateWaiting = false;
+ }
+ }
+ if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS) {
+ notify();
+ }
+ } else {
+ notify();
+ state = RUNNING;
+ }
+ break;
+ case WAIT_FOR_ALL:
+ while (threadPending != 0) {
+ state = WAITING_FOR_THREADS;
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ }
+ break;
+ case CHECK_FOR_WORK:
+ if (!workToDo) {
+ state = SLEEPING;
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ state = RUNNING;
+ }
+ workToDo = false;
+ break;
+ case SET_WORK:
+ workToDo = true;
+ if (state == SLEEPING) {
+ notify();
+ }
+ break;
+ case SET_WORK_FOR_REQUEST_RENDERER:
+ requestRenderWorkToDo = true;
+ if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS ||
+ state == SLEEPING) {
+ workToDo = true;
+ notify();
+ }
+ break;
+ case RUN_RENDERER_CLEANUP:
+ nthread.runMonitor(J3dThread.RUN, currentTime,
+ rendererCleanupArgs);
+ state = WAITING_FOR_RENDERER_CLEANUP;
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ break;
+ case SLEEP:
+ state = SLEEPING;
+ try {
+ wait(sleepTime);
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ }
+ }
+
+ // Static initializer
+ static {
+ /*
+ // Determine whether the JVM is version JDK1.5 or later.
+ // TODO: replace this with code that checks for the existence
+ // of a class or method that is defined in 1.5, but not in 1.4
+ String versionString =
+ (String) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return System.getProperty("java.version");
+ }
+ });
+ jvm15OrBetter = !(versionString.startsWith("1.4") ||
+ versionString.startsWith("1.3") ||
+ versionString.startsWith("1.2") ||
+ versionString.startsWith("1.1"));
+ */
+
+ // create ThreadGroup
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ ThreadGroup parent;
+ Thread thread = Thread.currentThread();
+ threadPriority = thread.getPriority();
+ rootThreadGroup = thread.getThreadGroup();
+ while ((parent = rootThreadGroup.getParent()) != null) {
+ rootThreadGroup = parent;
+ }
+ rootThreadGroup = new ThreadGroup(rootThreadGroup,
+ "Java3D");
+ // use the default maximum group priority
+ return null;
+ }
+ });
+ }
+
+
+ static String mtype[] = {
+ "-INSERT_NODES ",
+ "-REMOVE_NODES ",
+ "-RUN ",
+ "-TRANSFORM_CHANGED ",
+ "-UPDATE_VIEW ",
+ "-STOP_THREAD ",
+ "-COLORINGATTRIBUTES_CHANGED ",
+ "-LINEATTRIBUTES_CHANGED ",
+ "-POINTATTRIBUTES_CHANGED ",
+ "-POLYGONATTRIBUTES_CHANGED ",
+
+ "-RENDERINGATTRIBUTES_CHANGED ",
+ "-TEXTUREATTRIBUTES_CHANGED ",
+ "-TRANSPARENCYATTRIBUTES_CHANGED ",
+ "-MATERIAL_CHANGED ",
+ "-TEXCOORDGENERATION_CHANGED ",
+ "-TEXTURE_CHANGED ",
+ "-MORPH_CHANGED ",
+ "-GEOMETRY_CHANGED ",
+ "-APPEARANCE_CHANGED ",
+ "-LIGHT_CHANGED ",
+
+ "-BACKGROUND_CHANGED ",
+ "-CLIP_CHANGED ",
+ "-FOG_CHANGED ",
+ "-BOUNDINGLEAF_CHANGED ",
+ "-SHAPE3D_CHANGED ",
+ "-TEXT3D_TRANSFORM_CHANGED ",
+ "-TEXT3D_DATA_CHANGED ",
+ "-SWITCH_CHANGED ",
+ "-COND_MET ",
+ "-BEHAVIOR_ENABLE ",
+
+ "-BEHAVIOR_DISABLE ",
+ "-INSERT_RENDERATOMS ",
+ "-ORDERED_GROUP_INSERTED ",
+ "-ORDERED_GROUP_REMOVED ",
+ "-COLLISION_BOUND_CHANGED ",
+ "-REGION_BOUND_CHANGED ",
+ "-MODELCLIP_CHANGED ",
+ "-BOUNDS_AUTO_COMPUTE_CHANGED ",
+
+ "-SOUND_ATTRIB_CHANGED ",
+ "-AURALATTRIBUTES_CHANGED ",
+ "-SOUNDSCAPE_CHANGED ",
+ "-ALTERNATEAPPEARANCE_CHANGED ",
+ "-RENDER_OFFSCREEN ",
+ "-RENDER_RETAINED ",
+ "-RENDER_IMMEDIATE ",
+ "-SOUND_STATE_CHANGED ",
+ "-ORIENTEDSHAPE3D_CHANGED ",
+ "-TEXTURE_UNIT_STATE_CHANGED ",
+ "-UPDATE_VIEWPLATFORM ",
+ "-BEHAVIOR_ACTIVATE ",
+ "-GEOMETRYARRAY_CHANGED ",
+ "-MEDIA_CONTAINER_CHANGED ",
+ "-RESIZE_CANVAS ",
+ "-TOGGLE_CANVAS ",
+ "-IMAGE_COMPONENT_CHANGED ",
+ "-SCHEDULING_INTERVAL_CHANGED ",
+ "-VIEWSPECIFICGROUP_CHANGED ",
+ "-VIEWSPECIFICGROUP_INIT ",
+ "-VIEWSPECIFICGROUP_CLEAR ",
+ "-ORDERED_GROUP_TABLE_CHANGED"};
+
+
+ static void dumpThreads(int threads) {
+ //dump Threads type
+ if ((threads & J3dThread.BEHAVIOR_SCHEDULER) != 0)
+ System.out.println(" BEHAVIOR_SCHEDULER");
+ if ((threads & J3dThread.SOUND_SCHEDULER) != 0)
+ System.out.println(" SOUND_SCHEDULER");
+ if ((threads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0)
+ System.out.println(" INPUT_DEVICE_SCHEDULER");
+
+ if ((threads & J3dThread.RENDER_THREAD) != 0)
+ System.out.println(" RENDER_THREAD");
+
+ if ((threads & J3dThread.UPDATE_GEOMETRY) != 0)
+ System.out.println(" UPDATE_GEOMETRY");
+ if ((threads & J3dThread.UPDATE_RENDER) != 0)
+ System.out.println(" UPDATE_RENDER");
+ if ((threads & J3dThread.UPDATE_BEHAVIOR) != 0)
+ System.out.println(" UPDATE_BEHAVIOR");
+ if ((threads & J3dThread.UPDATE_SOUND) != 0)
+ System.out.println(" UPDATE_SOUND");
+ if ((threads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0)
+ System.out.println(" UPDATE_RENDERING_ATTRIBUTES");
+ if ((threads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0)
+ System.out.println(" UPDATE_RENDERING_ENVIRONMENT");
+ if ((threads & J3dThread.UPDATE_TRANSFORM) != 0)
+ System.out.println(" UPDATE_TRANSFORM");
+ }
+
+ static void dumpmsg(J3dMessage m) {
+ //dump message type
+ System.out.println(mtype[m.type]);
+
+ dumpThreads(m.threads);
+ }
+
+
+ int frameCount = 0;
+ private int frameCountCutoff = 100;
+
+ private void manageMemory() {
+ if (++frameCount > frameCountCutoff) {
+ FreeListManager.manageLists();
+ frameCount = 0;
+ }
+ }
+
+ /**
+ * Yields the current thread, by sleeping for a small amount of
+ * time. Unlike <code>Thread.yield()</code>, this method
+ * guarantees that the current thread will yield to another thread
+ * waiting to run. It also ensures that the other threads will
+ * run for at least a small amount of time before the current
+ * thread runs again.
+ */
+ static final void threadYield() {
+ // Note that we can't just use Thread.yield(), since it
+ // doesn't guarantee that it will actually yield the thread
+ // (and, in fact, it appears to be a no-op under Windows). So
+ // we will sleep for 1 msec instead. Since most threads use
+ // wait/notify, and only use this when they are waiting for
+ // another thread to finish something, this shouldn't be a
+ // performance concern.
+
+ //Thread.yield();
+ try {
+ Thread.sleep(1);
+ }
+ catch (InterruptedException e) {
+ // Do nothing, since we really don't care how long (or
+ // even whether) we sleep
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/MasterControlThread.java b/src/classes/share/javax/media/j3d/MasterControlThread.java
new file mode 100644
index 0000000..7a0e869
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MasterControlThread.java
@@ -0,0 +1,48 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Master control thread. The MasterControlThread object and thread
+ * are created dynamically whenever needed. Once created, the thread
+ * runs until all other threads are terminated. Then the master
+ * control thread terminates. There is never more than one
+ * MasterControl object or thread in existence at any one time.
+ */
+class MasterControlThread extends Thread {
+
+ MasterControlThread(ThreadGroup threadGroup) {
+ super(threadGroup, "J3D-MasterControl");
+ VirtualUniverse.mc.createMCThreads();
+ this.start();
+ }
+
+ public void run() {
+
+ do {
+ while (VirtualUniverse.mc.running) {
+ VirtualUniverse.mc.doWork();
+
+ // NOTE: no need to call Thread.yield(), since we will
+ // call wait() if there is no work to do (yield seems
+ // to be a no-op on Windows anyway)
+ }
+ } while (!VirtualUniverse.mc.mcThreadDone());
+
+ if(J3dDebug.devPhase) {
+ J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1,
+ "MC: MasterControl Thread Terminate");
+ }
+
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Material.java b/src/classes/share/javax/media/j3d/Material.java
new file mode 100644
index 0000000..16b8ac1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Material.java
@@ -0,0 +1,689 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+
+/**
+ * The Material object defines the appearance of an object under
+ * illumination.
+ * If the Material object in an Appearance object is <code>null</code>,
+ * lighting is disabled for all nodes that use that Appearance object.
+ * <P>
+ * The properties that can be set for a Material object are:
+ * <P><UL>
+ * <LI>Ambient color - the ambient RGB color reflected off the surface
+ * of the material. The range of values is 0.0 to 1.0. The default ambient
+ * color is (0.2, 0.2, 0.2).<p></LI>
+ * <LI>Diffuse color - the RGB color of the material when illuminated.
+ * The range of values is 0.0 to 1.0. The default diffuse color is
+ * (1.0, 1.0, 1.0).<p></LI>
+ * <LI>Specular color - the RGB specular color of the material (highlights).
+ * The range of values is 0.0 to 1.0. The default specular color
+ * is (1.0, 1.0, 1.0).<p></LI>
+ * <LI>Emissive color - the RGB color of the light the material emits, if
+ * any. The range of values is 0.0 to 1.0. The default emissive
+ * color is (0.0, 0.0, 0.0).<p></LI>
+ * <LI>Shininess - the material's shininess, in the range [1.0, 128.0]
+ * with 1.0 being not shiny and 128.0 being very shiny. Values outside
+ * this range are clamped. The default value for the material's
+ * shininess is 64.<p></LI>
+ * <LI>Color target - the material color target for per-vertex colors,
+ * one of: AMBIENT, EMISSIVE, DIFFUSE, SPECULAR, or AMBIENT_AND_DIFFUSE.
+ * The default target is DIFFUSE.<p></LI>
+ * </UL>
+ *
+ * The Material object also enables or disables lighting.
+ */
+public class Material extends NodeComponent {
+
+ /**
+ * For material object, specifies that Material allows reading
+ * individual component field information.
+ */
+ public static final int
+ ALLOW_COMPONENT_READ = CapabilityBits.MATERIAL_ALLOW_COMPONENT_READ;
+
+ /**
+ * For material object, specifies that Material allows reading
+ * individual component field information.
+ */
+ public static final int
+ ALLOW_COMPONENT_WRITE = CapabilityBits.MATERIAL_ALLOW_COMPONENT_WRITE;
+
+ /**
+ * Specifies that per-vertex colors replace the ambient material color.
+ * @see #setColorTarget
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int AMBIENT = 0;
+
+ /**
+ * Specifies that per-vertex colors replace the emissive material color.
+ * @see #setColorTarget
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int EMISSIVE = 1;
+
+ /**
+ * Specifies that per-vertex colors replace the diffuse material color.
+ * This is the default target.
+ * @see #setColorTarget
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int DIFFUSE = 2;
+
+ /**
+ * Specifies that per-vertex colors replace the specular material color.
+ * @see #setColorTarget
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int SPECULAR = 3;
+
+ /**
+ * Specifies that per-vertex colors replace both the ambient and the
+ * diffuse material color.
+ * @see #setColorTarget
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int AMBIENT_AND_DIFFUSE = 4;
+
+
+ /**
+ * Constructs and initializes a Material object using default parameters.
+ * The default values are as follows:
+ * <ul>
+ * lighting enable : true<br>
+ * ambient color : (0.2, 0.2, 0.2)<br>
+ * emmisive color : (0.0, 0.0, 0.0)<br>
+ * diffuse color : (1.0, 1.0, 1.0)<br>
+ * specular color : (1.0, 1.0, 1.0)<br>
+ * shininess : 64<br>
+ * color target : DIFFUSE
+ * </ul>
+ */
+ public Material() {
+ // Just use the defaults
+ }
+
+ /**
+ * Constructs and initializes a new material object using the specified
+ * parameters. Lighting is enabled by default.
+ * @param ambientColor the material's ambient color
+ * @param emissiveColor the material's emissive color
+ * @param diffuseColor the material's diffuse color when illuminated by a
+ * light
+ * @param specularColor the material's specular color when illuminated
+ * to generate a highlight
+ * @param shininess the material's shininess in the
+ * range [1.0, 128.0] with 1.0 being not shiny and 128.0 being very shiny.
+ * Values outside this range are clamped.
+ */
+ public Material(Color3f ambientColor,
+ Color3f emissiveColor,
+ Color3f diffuseColor,
+ Color3f specularColor,
+ float shininess) {
+ ((MaterialRetained)this.retained).createMaterial(ambientColor,
+ emissiveColor, diffuseColor, specularColor,
+ shininess);
+ }
+
+ /**
+ * Creates a retained mode MaterialRetained object that this
+ * Material component object will point to.
+ */
+ void createRetained() {
+ this.retained = new MaterialRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Sets this material's ambient color.
+ * This specifies how much ambient light is reflected by
+ * the surface.
+ * The ambient color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is either AMBIENT or AMBIENT_AND_DIFFUSE, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's ambient color in the lighting equation.
+ *
+ * @param color the material's ambient color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setAmbientColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+ if (isLive()) {
+ ((MaterialRetained)this.retained).setAmbientColor(color);
+ }
+ else {
+ ((MaterialRetained)this.retained).initAmbientColor(color);
+ }
+ }
+
+ /**
+ * Sets this material's ambient color.
+ * This specifies how much ambient light is reflected by
+ * the surface.
+ * The ambient color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is either AMBIENT or AMBIENT_AND_DIFFUSE, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's ambient color in the lighting equation.
+ *
+ * @param r the new ambient color's red component
+ * @param g the new ambient color's green component
+ * @param b the new ambient color's blue component
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setAmbientColor(float r, float g, float b) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+ if (isLive()) {
+ ((MaterialRetained)this.retained).setAmbientColor(r,g,b);
+ }
+ else {
+ ((MaterialRetained)this.retained).initAmbientColor(r,g,b);
+ }
+ }
+
+ /**
+ * Retrieves this material's ambient color.
+ * @param color that will contain the material's ambient color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getAmbientColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material2"));
+
+ ((MaterialRetained)this.retained).getAmbientColor(color);
+ }
+
+ /**
+ * Sets this material's emissive color.
+ * This is the color of light, if any, that the material emits.
+ * The emissive color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is EMISSIVE, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's emissive color in the lighting equation.
+ *
+ * @param color the new emissive color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setEmissiveColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+ if (isLive())
+ ((MaterialRetained)this.retained).setEmissiveColor(color);
+ else
+ ((MaterialRetained)this.retained).initEmissiveColor(color);
+
+
+
+
+ }
+
+ /**
+ * Sets this material's emissive color.
+ * This is the color of light, if any, that the material emits.
+ * The emissive color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is EMISSIVE, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's emissive color in the lighting equation.
+ *
+ * @param r the new emissive color's red component
+ * @param g the new emissive color's green component
+ * @param b the new emissive color's blue component
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setEmissiveColor(float r, float g, float b) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setEmissiveColor(r,g,b);
+ else
+ ((MaterialRetained)this.retained).initEmissiveColor(r,g,b);
+ }
+
+ /**
+ * Retrieves this material's emissive color and stores it in the
+ * argument provided.
+ * @param color the vector that will receive this material's emissive color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getEmissiveColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material2"));
+
+ ((MaterialRetained)this.retained).getEmissiveColor(color);
+ }
+
+ /**
+ * Sets this material's diffuse color.
+ * This is the color of the material when illuminated by a light source.
+ * The diffuse color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is either DIFFUSE or AMBIENT_AND_DIFFUSE, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's diffuse color in the lighting equation.
+ *
+ * @param color the new diffuse color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setDiffuseColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setDiffuseColor(color);
+ else
+ ((MaterialRetained)this.retained).initDiffuseColor(color);
+ }
+
+ /**
+ * Sets this material's diffuse color.
+ * This is the color of the material when illuminated by a light source.
+ * The diffuse color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is either DIFFUSE or AMBIENT_AND_DIFFUSE, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's diffuse color in the lighting equation.
+ *
+ * @param r the new diffuse color's red component
+ * @param g the new diffuse color's green component
+ * @param b the new diffuse color's blue component
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setDiffuseColor(float r, float g, float b) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setDiffuseColor(r,g,b);
+ else
+ ((MaterialRetained)this.retained).initDiffuseColor(r,g,b);
+ }
+
+ /**
+ * Sets this material's diffuse color plus alpha.
+ * This is the color of the material when illuminated by a light source.
+ * The diffuse color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is either DIFFUSE or AMBIENT_AND_DIFFUSE, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's diffuse color in the lighting equation.
+ *
+ * @param r the new diffuse color's red component
+ * @param g the new diffuse color's green component
+ * @param b the new diffuse color's blue component
+ * @param a the alpha component used to set transparency
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setDiffuseColor(float r, float g, float b, float a) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setDiffuseColor(r,g,b,a);
+ else
+ ((MaterialRetained)this.retained).initDiffuseColor(r,g,b,a);
+ }
+
+ /**
+ * Retrieves this material's diffuse color.
+ * @param color the vector that will receive this material's diffuse color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDiffuseColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material2"));
+
+ ((MaterialRetained)this.retained).getDiffuseColor(color);
+ }
+
+ /**
+ * Sets this material's specular color.
+ * This is the specular highlight color of the material.
+ * The specular color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is SPECULAR, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's specular color in the lighting equation.
+ *
+ * @param color the new specular color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setSpecularColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setSpecularColor(color);
+ else
+ ((MaterialRetained)this.retained).initSpecularColor(color);
+ }
+
+ /**
+ * Sets this material's specular color.
+ * This is the specular highlight color of the material.
+ * The specular color in this Material object may be overridden by
+ * per-vertex colors in some cases. If vertex colors are present
+ * in the geometry, and lighting is enabled, and the colorTarget
+ * is SPECULAR, and vertex colors are
+ * not being ignored, then the vertex colors are used in place of
+ * this Material's specular color in the lighting equation.
+ *
+ * @param r the new specular color's red component
+ * @param g the new specular color's green component
+ * @param b the new specular color's blue component
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see #setColorTarget
+ */
+ public void setSpecularColor(float r, float g, float b) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setSpecularColor(r,g,b);
+ else
+ ((MaterialRetained)this.retained).initSpecularColor(r,g,b);
+ }
+
+ /**
+ * Retrieves this material's specular color.
+ * @param color the vector that will receive this material's specular color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getSpecularColor(Color3f color) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material2"));
+
+ ((MaterialRetained)this.retained).getSpecularColor(color);
+ }
+
+ /**
+ * Sets this material's shininess.
+ * This specifies a material specular scattering exponent, or
+ * shininess. It takes a floating point number in the range [1.0, 128.0]
+ * with 1.0 being not shiny and 128.0 being very shiny.
+ * Values outside this range are clamped.
+ * @param shininess the material's shininess
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setShininess(float shininess) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material0"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setShininess(shininess);
+ else
+ ((MaterialRetained)this.retained).initShininess(shininess);
+ }
+
+ /**
+ * Retrieves this material's shininess.
+ * @return the material's shininess
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getShininess() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material2"));
+
+ return ((MaterialRetained)this.retained).getShininess();
+ }
+
+ /**
+ * Enables or disables lighting for this appearance component object.
+ * @param state true or false to enable or disable lighting
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setLightingEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material15"));
+ if (isLive())
+ ((MaterialRetained)this.retained).setLightingEnable(state);
+ else
+ ((MaterialRetained)this.retained).initLightingEnable(state);
+ }
+
+ /**
+ * Retrieves the state of the lighting enable flag.
+ * @return true if lighting is enabled, false if lighting is disabled
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getLightingEnable() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material16"));
+ return ((MaterialRetained)this.retained).getLightingEnable();
+ }
+
+ /**
+ * Sets the color target for per-vertex colors. When lighting is
+ * enabled and per-vertex colors are present (and not ignored) in
+ * the geometry for a given Shape3D node, those per-vertex colors
+ * are used in place of the specified material color(s) for this
+ * Material object. The color target is ignored when lighting is
+ * disabled or when per-vertex colors are not used.
+ * The ColorInterpolator behavior also uses the color target to
+ * determine which color in the associated Material is modified.
+ * The default target is DIFFUSE.
+ *
+ * @param colorTarget one of: AMBIENT, EMISSIVE, DIFFUSE, SPECULAR, or
+ * AMBIENT_AND_DIFFUSE.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see RenderingAttributes#setIgnoreVertexColors
+ * @see ColorInterpolator
+ *
+ * @since Java 3D 1.3
+ */
+ public void setColorTarget(int colorTarget) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material3"));
+
+ if (isLive())
+ ((MaterialRetained)this.retained).setColorTarget(colorTarget);
+ else
+ ((MaterialRetained)this.retained).initColorTarget(colorTarget);
+ }
+
+ /**
+ * Retrieves the current color target for this material.
+ *
+ * @return one of: AMBIENT, EMISSIVE, DIFFUSE, SPECULAR, or
+ * AMBIENT_AND_DIFFUSE.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getColorTarget() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Material4"));
+
+ return ((MaterialRetained)this.retained).getColorTarget();
+ }
+
+ /**
+ * Returns a String representation of this Materials values.
+ * If the scene graph is live only those values with their
+ * Capability read bit set will be displayed.
+ */
+ public String toString() {
+ StringBuffer str=new StringBuffer("Material Object:");
+ Color3f color=new Color3f();
+ try {
+ getAmbientColor(color);
+ str.append("AmbientColor="+color);
+ } catch (CapabilityNotSetException e) {str.append("AmbientColor=N/A");}
+ try {
+ getEmissiveColor(color);
+ str.append(" EmissiveColor="+color);
+ } catch (CapabilityNotSetException ex) {str.append(" EmissiveColor=N/A");}
+ try {
+ getDiffuseColor(color);
+ str.append(" DiffuseColor="+color);
+ } catch (CapabilityNotSetException exc) {str.append(" DiffuseColor=N/A");}
+ try {
+ getSpecularColor(color);
+ str.append(" SpecularColor="+color);
+ } catch (CapabilityNotSetException exce) {str.append(" SpecularColor=N/A");}
+ try {
+ float f=getShininess();
+ str.append(" Shininess="+f);
+ } catch (CapabilityNotSetException excep) {str.append(" Shininess=N/A");}
+ try {
+ boolean b=getLightingEnable();
+ str.append(" LightingEnable="+b);
+ } catch (CapabilityNotSetException except) {str.append(" LightingEnable=N/A");}
+ try {
+ int i=getColorTarget();
+ str.append(" ColorTarget="+i);
+ } catch (CapabilityNotSetException except) {str.append(" ColorTarget=N/A");}
+ return new String(str);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ Material m = new Material();
+ m.duplicateNodeComponent(this);
+ return m;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent,
+ forceDuplicate);
+
+ MaterialRetained mat = (MaterialRetained)
+ originalNodeComponent.retained;
+ MaterialRetained rt = (MaterialRetained) retained;
+
+ Color3f c = new Color3f();
+ mat.getAmbientColor(c);
+
+ rt.initAmbientColor(c);
+ mat.getEmissiveColor(c);
+ rt.initEmissiveColor(c);
+ mat.getDiffuseColor(c);
+ rt.initDiffuseColor(c);
+ mat.getSpecularColor(c);
+ rt.initSpecularColor(c);
+ rt.initShininess(mat.getShininess());
+ rt.initLightingEnable(mat.getLightingEnable());
+ rt.initColorTarget(mat.getColorTarget());
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/MaterialRetained.java b/src/classes/share/javax/media/j3d/MaterialRetained.java
new file mode 100644
index 0000000..2df0e3f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MaterialRetained.java
@@ -0,0 +1,555 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+import java.util.ArrayList;
+
+/**
+ * The MaterialRetained object defines the appearance of an object under
+ * illumination.
+ */
+class MaterialRetained extends NodeComponentRetained {
+ // Initialize default values for all class variables
+ Color3f ambientColor = new Color3f(0.2f, 0.2f, 0.2f);
+ Color3f emissiveColor = new Color3f(0.0f, 0.0f, 0.0f);
+ Color3f diffuseColor = new Color3f(1.0f, 1.0f, 1.0f);
+ Color3f specularColor = new Color3f(1.0f, 1.0f, 1.0f);
+ float shininess = 64.0f;
+ int colorTarget = Material.DIFFUSE;
+
+ // Lighting enable switch for this material object
+ boolean lightingEnable = true;
+
+ // A list of pre-defined bits to indicate which component
+ // in this Material object changed.
+ static final int AMBIENT_COLOR_CHANGED = 0x01;
+
+ static final int EMISSIVE_COLOR_CHANGED = 0x02;
+
+ static final int DIFFUSE_COLOR_CHANGED = 0x04;
+
+ static final int SPECULAR_COLOR_CHANGED = 0x08;
+
+ static final int SHININESS_CHANGED = 0x10;
+
+ static final int ENABLE_CHANGED = 0x20;
+
+ static final int COLORTARGET_CHANGED = 0x40;
+
+ /**
+ * Constructs and initializes a new material object using the specified
+ * parameters.
+ * @param ambientColor the material's ambient color
+ * @param emissiveColor the material's emissive color
+ * @param diffuseColor the material's diffuse color when illuminated by a
+ * light
+ * @param specularColor the material's specular color when illuminated
+ * to generate a highlight
+ * @param shininess the material's shininess in the
+ * range [1.0, 128.0] with 1.0 being not shiny and 128.0 being very shiny
+ */
+ void createMaterial(Color3f aColor,
+ Color3f eColor,
+ Color3f dColor,
+ Color3f sColor,
+ float shine)
+ {
+ ambientColor.set(aColor);
+ emissiveColor.set(eColor);
+ diffuseColor.set(dColor);
+ specularColor.set(sColor);
+ shininess = shine;
+ }
+
+ /** Initializes this material's ambient color
+ * This specifies how much ambient light is reflected by
+ * the surface. The ambient light color is the product of this
+ * color and the material diffuseColor.
+ * @param color the material's ambient color
+ */
+ final void initAmbientColor(Color3f color) {
+ this.ambientColor.set(color);
+ }
+
+ /**
+ * Sets this material's ambient color and sends a message notifying
+ * the interested structures of the change.
+ * This specifies how much ambient light is reflected by
+ * the surface. The ambient light color is the product of this
+ * color and the material diffuseColor.
+ * @param color the material's ambient color
+ */
+ final void setAmbientColor(Color3f color) {
+ initAmbientColor(color);
+ sendMessage(AMBIENT_COLOR_CHANGED, new Color3f(color));
+ }
+
+ /**
+ * Sets this material's ambient color
+ * @param r the new ambient color's red component
+ * @param g the new ambient color's green component
+ * @param b the new ambient color's blue component
+ */
+ final void initAmbientColor(float r, float g, float b) {
+ this.ambientColor.set(r, g, b);
+ }
+
+
+ /**
+ * Sets this material's ambient color and sends a message notifying
+ * the interested structures of the change.
+ * @param r the new ambient color's red component
+ * @param g the new ambient color's green component
+ * @param b the new ambient color's blue component
+ */
+ final void setAmbientColor(float r, float g, float b) {
+ initAmbientColor(r, g, b);
+ sendMessage(AMBIENT_COLOR_CHANGED, new Color3f(r, g, b));
+ }
+
+ /**
+ * Retrieves this material's ambient color.
+ * @return the material's ambient color
+ */
+ final void getAmbientColor(Color3f color) {
+ color.set(this.ambientColor);
+ }
+
+ /**
+ * Sets this material's emissive color
+ * This is the color of light, if any, that the material emits.
+ * @param color the new emissive color
+ */
+ final void initEmissiveColor(Color3f color) {
+ this.emissiveColor.set(color);
+ }
+
+ /**
+ * Sets this material's emissive color and sends a message notifying
+ * the interested structures of the change.
+ * This is the color of light, if any, that the material emits.
+ * @param color the new emissive color
+ */
+ final void setEmissiveColor(Color3f color) {
+ initEmissiveColor(color);
+ sendMessage(EMISSIVE_COLOR_CHANGED, new Color3f(color));
+ }
+
+ /**
+ * Sets this material's emissive color.
+ * This is the color of light, if any, that the material emits.
+ * @param r the new emissive color's red component
+ * @param g the new emissive color's green component
+ * @param b the new emissive color's blue component
+ */
+ final void initEmissiveColor(float r, float g, float b) {
+ this.emissiveColor.set(r, g, b);
+ }
+
+ /**
+ * Sets this material's emissive color and sends a message notifying
+ * the interested structures of the change.
+ * This is the color of light, if any, that the material emits.
+ * @param r the new emissive color's red component
+ * @param g the new emissive color's green component
+ * @param b the new emissive color's blue component
+ */
+ final void setEmissiveColor(float r, float g, float b) {
+ initEmissiveColor(r, g, b);
+ sendMessage(EMISSIVE_COLOR_CHANGED, new Color3f(r, g, b));
+ }
+
+ /**
+ * Retrieves this material's emissive color and stores it in the
+ * argument provided.
+ * @param color the vector that will receive this material's emissive color
+ */
+ final void getEmissiveColor(Color3f color) {
+ color.set(this.emissiveColor);
+ }
+
+ /**
+ * Sets this material's diffuse color.
+ * This is the color of the material when illuminated by a light source.
+ * @param color the new diffuse color
+ */
+ final void initDiffuseColor(Color3f color) {
+ this.diffuseColor.set(color);
+ }
+
+ /**
+ * Sets this material's diffuse color and sends a message notifying
+ * the interested structures of the change.
+ * This is the color of the material when illuminated by a light source.
+ * @param color the new diffuse color
+ */
+ final void setDiffuseColor(Color3f color) {
+ initDiffuseColor(color);
+ sendMessage(DIFFUSE_COLOR_CHANGED, new Color3f(color));
+ }
+
+ /**
+ * Sets this material's diffuse color.
+ * @param r the new diffuse color's red component
+ * @param g the new diffuse color's green component
+ * @param b the new diffuse color's blue component
+ */
+ final void initDiffuseColor(float r, float g, float b) {
+ this.diffuseColor.set(r, g, b);
+ }
+
+ /**
+ * Sets this material's diffuse color and sends a message notifying
+ * the interested structures of the change.
+ * @param r the new diffuse color's red component
+ * @param g the new diffuse color's green component
+ * @param b the new diffuse color's blue component
+ */
+ final void setDiffuseColor(float r, float g, float b) {
+ initDiffuseColor(r, g, b);
+ sendMessage(DIFFUSE_COLOR_CHANGED, new Color3f(r, g, b));
+ }
+
+ /**
+ * Sets this material's diffuse color plus alpha.
+ * This is the color of the material when illuminated by a light source.
+ * @param r the new diffuse color's red component
+ * @param g the new diffuse color's green component
+ * @param b the new diffuse color's blue component
+ * @param a the alpha component used to set transparency
+ */
+ final void initDiffuseColor(float r, float g, float b, float a) {
+ this.diffuseColor.set(r, g, b);
+ }
+
+ /**
+ * Sets this material's diffuse color plus alpha and sends
+ * a message notifying the interested structures of the change.
+ * This is the color of the material when illuminated by a light source.
+ * @param r the new diffuse color's red component
+ * @param g the new diffuse color's green component
+ * @param b the new diffuse color's blue component
+ * @param a the alpha component used to set transparency
+ */
+ final void setDiffuseColor(float r, float g, float b, float a) {
+ initDiffuseColor(r, g, b);
+ sendMessage(DIFFUSE_COLOR_CHANGED, new Color3f(r, g, b));
+ }
+
+ /**
+ * Retrieves this material's diffuse color.
+ * @param color the vector that will receive this material's diffuse color
+ */
+ final void getDiffuseColor(Color3f color) {
+ color.set(this.diffuseColor);
+ }
+
+ /**
+ * Sets this material's specular color.
+ * This is the specular highlight color of the material.
+ * @param color the new specular color
+ */
+ final void initSpecularColor(Color3f color) {
+ this.specularColor.set(color);
+ }
+
+ /**
+ * Sets this material's specular color and sends a message notifying
+ * the interested structures of the change.
+ * This is the specular highlight color of the material.
+ * @param color the new specular color
+ */
+ final void setSpecularColor(Color3f color) {
+ initSpecularColor(color);
+ sendMessage(SPECULAR_COLOR_CHANGED, new Color3f(color));
+ }
+
+ /**
+ * Sets this material's specular color.
+ * This is the specular highlight color of the material.
+ * @param r the new specular color's red component
+ * @param g the new specular color's green component
+ * @param b the new specular color's blue component
+ */
+ final void initSpecularColor(float r, float g, float b) {
+ this.specularColor.set(r, g, b);
+ }
+
+
+ /**
+ * Sets this material's specular color and sends a message notifying
+ * the interested structures of the change.
+ * This is the specular highlight color of the material.
+ * @param r the new specular color's red component
+ * @param g the new specular color's green component
+ * @param b the new specular color's blue component
+ */
+ final void setSpecularColor(float r, float g, float b) {
+ initSpecularColor(r, g, b);
+ sendMessage(SPECULAR_COLOR_CHANGED, new Color3f(r, g, b));
+ }
+
+ /**
+ * Retrieves this material's specular color.
+ * @param color the vector that will receive this material's specular color
+ */
+ final void getSpecularColor(Color3f color) {
+ color.set(this.specularColor);
+ }
+
+ /**
+ * Sets this material's shininess.
+ * This specifies a material specular exponent, or shininess.
+ * It takes a floating point number in the range [1.0, 128.0]
+ * with 1.0 being not shiny and 128.0 being very shiny.
+ * @param shininess the material's shininess
+ */
+ final void initShininess(float shininess) {
+ // Clamp shininess value
+ if (shininess < 1.0f)
+ this.shininess = 1.0f;
+ else if (shininess > 128.0f)
+ this.shininess = 128.0f;
+ else
+ this.shininess = shininess;
+
+ }
+
+ /**
+ * Sets this material's shininess and sends a message notifying
+ * the interested structures of the change.
+ * This specifies a material specular exponent, or shininess.
+ * It takes a floating point number in the range [1.0, 128.0]
+ * with 1.0 being not shiny and 128.0 being very shiny.
+ * @param shininess the material's shininess
+ */
+ final void setShininess(float shininess) {
+ initShininess(shininess);
+ sendMessage(SHININESS_CHANGED, new Float(this.shininess));
+ }
+
+ /**
+ * Retrieves this material's shininess.
+ * @return the material's shininess
+ */
+ final float getShininess() {
+ return this.shininess;
+ }
+
+ /**
+ * Enables or disables lighting for this appearance component object.
+ * @param state true or false to enable or disable lighting
+ */
+ void initLightingEnable(boolean state) {
+ lightingEnable = state;
+ }
+
+ /**
+ * Enables or disables lighting for this appearance component object
+ * and sends a message notifying
+ * the interested structures of the change.
+ * @param state true or false to enable or disable lighting
+ */
+ void setLightingEnable(boolean state) {
+ initLightingEnable(state);
+ sendMessage(ENABLE_CHANGED,
+ (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Retrieves the state of the lighting enable flag.
+ * @return true if lighting is enabled, false if lighting is disabled
+ */
+ boolean getLightingEnable() {
+ return lightingEnable;
+ }
+
+ void initColorTarget(int colorTarget) {
+ this.colorTarget = colorTarget;
+ }
+
+ final void setColorTarget(int colorTarget) {
+ initColorTarget(colorTarget);
+ sendMessage(COLORTARGET_CHANGED, new Integer(colorTarget));
+ }
+
+ final int getColorTarget() {
+ return colorTarget;
+ }
+
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ } else {
+ MaterialRetained mirrorMat = new MaterialRetained();
+ mirrorMat.set(this);
+ mirrorMat.source = source;
+ mirror = mirrorMat;
+ }
+ } else {
+ ((MaterialRetained) mirror).set(this);
+ }
+ }
+
+
+ /**
+ * Updates the native context.
+ */
+ native void updateNative(long ctx,
+ float red, float green, float blue, float alpha,
+ float ared, float agreen, float ablue,
+ float ered, float egreen, float eblue,
+ float dred, float dgreen, float dblue,
+ float sred, float sgreen, float sblue,
+ float shininess, int colorTarget, boolean enable);
+
+ /**
+ * Updates the native context.
+ */
+ void updateNative(long ctx,
+ float red, float green, float blue, float alpha,
+ boolean enableLighting) {
+ updateNative(ctx, red, green, blue, alpha,
+ ambientColor.x, ambientColor.y, ambientColor.z,
+ emissiveColor.x, emissiveColor.y, emissiveColor.z,
+ diffuseColor.x, diffuseColor.y, diffuseColor.z,
+ specularColor.x, specularColor.y, specularColor.z,
+ shininess, colorTarget, enableLighting);
+ }
+
+
+ /**
+ * Creates a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ MaterialRetained mirrorMaterial = (MaterialRetained)mirror;
+ mirrorMaterial.set(this);
+ }
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+ MaterialRetained mirrorMaterial = (MaterialRetained)mirror;
+ if ((component & AMBIENT_COLOR_CHANGED) != 0) {
+ mirrorMaterial.ambientColor = (Color3f)value;
+ }
+ else if ((component & EMISSIVE_COLOR_CHANGED) != 0) {
+ mirrorMaterial.emissiveColor = (Color3f)value;
+ }
+ else if ((component & DIFFUSE_COLOR_CHANGED) != 0) {
+ mirrorMaterial.diffuseColor = (Color3f)value;
+ }
+ else if ((component & SPECULAR_COLOR_CHANGED) != 0) {
+ mirrorMaterial.specularColor = (Color3f)value;
+ }
+ else if ((component & SHININESS_CHANGED) != 0) {
+ mirrorMaterial.shininess = ((Float)value).floatValue();
+ }
+ else if ((component & ENABLE_CHANGED) != 0) {
+ mirrorMaterial.lightingEnable = ((Boolean)value).booleanValue();
+ }
+ else if ((component & COLORTARGET_CHANGED) != 0) {
+ mirrorMaterial.colorTarget = ((Integer)value).intValue();
+ }
+
+ }
+
+
+ boolean equivalent(MaterialRetained m) {
+ return ((m != null) &&
+ lightingEnable == m.lightingEnable &&
+ diffuseColor.equals(m.diffuseColor) &&
+ emissiveColor.equals(m.emissiveColor) &&
+ specularColor.equals(m.specularColor) &&
+ ambientColor.equals(m.ambientColor) &&
+ colorTarget == m.colorTarget &&
+ shininess == m.shininess);
+ }
+
+
+ // This functions clones the retained side only and is used
+ // internally
+ protected Object clone() {
+ MaterialRetained mr = (MaterialRetained)super.clone();
+ // color can't share the same reference
+ mr.ambientColor = new Color3f(ambientColor);
+ mr.emissiveColor = new Color3f(emissiveColor);
+ mr.diffuseColor = new Color3f(diffuseColor);
+ mr.specularColor = new Color3f(specularColor);
+ // other attributes are copy by clone() automatically
+ return mr;
+ }
+
+ protected void set(MaterialRetained mat) {
+ super.set(mat);
+
+ // duplicate any referenced data
+ ambientColor.set(mat.ambientColor);
+ emissiveColor.set(mat.emissiveColor);
+ diffuseColor.set(mat.diffuseColor);
+ specularColor.set(mat.specularColor);
+ shininess = mat.shininess;
+ lightingEnable = mat.lightingEnable;
+ colorTarget = mat.colorTarget;
+ }
+
+
+ final void sendMessage(int attrMask, Object attr) {
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.MATERIAL_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ int size = univList.size();
+ for(int i=0; i<size; i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.MATERIAL_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ }
+
+ void handleFrequencyChange(int bit) {
+ if (bit == Material.ALLOW_COMPONENT_WRITE) {
+ setFrequencyChangeMask(Material.ALLOW_COMPONENT_WRITE, 0x1);
+ }
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/MediaContainer.java b/src/classes/share/javax/media/j3d/MediaContainer.java
new file mode 100644
index 0000000..fcba207
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MediaContainer.java
@@ -0,0 +1,320 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.net.URL;
+import java.io.InputStream;
+
+/**
+ * The MediaContainer object defines all sound data: cached state flag, and
+ * associated sound media. Currently this references the sound media in
+ * one of three forms: URL String, URL object, or InputStream object.
+ * In future releases media data will include references to Java Media
+ * Player objects.
+ * Only one type of sound media data specified using
+ * <code>setURLString</code>, <code>setURLObject</code>,
+ * or <code>setInputStream</code> may be
+ * non-null (or they may all be null). An attempt to set more
+ * than one of these attributes to a non-null reference will
+ * result in an exception being thrown. If all sound media data
+ * references are null, there is no sound associated with this
+ * MediaContainer and Sound nodes referencing this object cannot
+ * be played.
+ */
+public class MediaContainer extends NodeComponent {
+ /**
+ * For MediaContainer component objects, specifies that this object
+ * allows the reading of its cached flag.
+ */
+ public static final int
+ ALLOW_CACHE_READ = CapabilityBits.MEDIA_CONTAINER_ALLOW_CACHE_READ;
+
+ /**
+ * For MediaContainer component objects, specifies that this object
+ * allows the writing of its cached flag.
+ */
+ public static final int
+ ALLOW_CACHE_WRITE = CapabilityBits.MEDIA_CONTAINER_ALLOW_CACHE_WRITE;
+
+ /**
+ * For MediaContainer component objects, specifies that this object
+ * allows the reading of it's sound data.
+ */
+ public static final int
+ ALLOW_URL_READ = CapabilityBits.MEDIA_CONTAINER_ALLOW_URL_READ;
+
+ /**
+ * For MediaContainer component objects, specifies that this object
+ * allows the writing of it's URL path.
+ */
+ public static final int
+ ALLOW_URL_WRITE = CapabilityBits.MEDIA_CONTAINER_ALLOW_URL_WRITE;
+
+ /**
+ * Constructs a MediaContainer object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * URL String data : null<br>
+ * URL object data : null<br>
+ * InputStream data : null<br>
+ * cache enable : true<br>
+ * </ul>
+ */
+ public MediaContainer() {
+ // Just use default values
+ }
+
+ /**
+ * Constructs and initializes a MediaContainer object using specified
+ * parameters.
+ * @param path string of URL path containing sound data
+ * @exception SoundException if the URL is not valid or cannot be opened
+ */
+ public MediaContainer(String path) {
+ ((MediaContainerRetained)this.retained).setURLString(path);
+ }
+
+ /**
+ * Constructs and initializes a MediaContainer object using specified
+ * parameters.
+ * @param url URL path containing sound data
+ * @exception SoundException if the URL is not valid or cannot be opened
+ */
+ public MediaContainer(URL url) {
+ ((MediaContainerRetained)this.retained).setURLObject(url);
+ }
+
+ /**
+ * Constructs and initializes a MediaContainer object using specified
+ * parameters.
+ * @param stream input stream containing sound data
+ *
+ * @since Java 3D 1.2
+ */
+ public MediaContainer(InputStream stream) {
+ ((MediaContainerRetained)this.retained).setInputStream(stream);
+ }
+
+ /**
+ * Creates the retained mode MediaContainerRetained object that this
+ * component object will point to.
+ */
+ void createRetained() {
+ this.retained = new MediaContainerRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Set Cache Enable state flag.
+ * Allows the writing of sound data explicitly into the MediaContainer
+ * rather than just referencing a JavaMedia container.
+ * @param flag boolean denoting if sound data is cached in this instance
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setCacheEnable(boolean flag) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CACHE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer1"));
+
+ ((MediaContainerRetained)this.retained).setCacheEnable(flag);
+ }
+
+ /**
+ * Retrieve Cache Enable state flag.
+ * @return flag denoting is sound data is non-cached or cached
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getCacheEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CACHE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer2"));
+
+ return ((MediaContainerRetained)this.retained).getCacheEnable();
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setURLString</code>
+ */
+ public void setURL(String path) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_URL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer3"));
+ }
+
+ ((MediaContainerRetained)this.retained).setURLString(path);
+ }
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>setURLObject</code>
+ */
+ public void setURL(URL url) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_URL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer3"));
+ ((MediaContainerRetained)this.retained).setURLObject(url);
+ }
+
+ /**
+ * Set URL String.
+ * @param path string of URL containing sound data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception SoundException if the URL is not valid or cannot be opened
+ * @exception IllegalArgumentException if the specified sound data is
+ * non-null and any other sound data reference is also non-null.
+ * @since Java 3D 1.2
+ */
+ public void setURLString(String path) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_URL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer3"));
+ }
+ ((MediaContainerRetained)this.retained).setURLString(path);
+ }
+
+ /**
+ * Set URL Object.
+ * @param url URL object containing sound data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception SoundException if the URL is not valid or cannot be opened
+ * @exception IllegalArgumentException if the specified sound data is
+ * non-null and any other sound data reference is also non-null.
+ * @since Java 3D 1.2
+ */
+ public void setURLObject(URL url) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_URL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer3"));
+ ((MediaContainerRetained)this.retained).setURLObject(url);
+ }
+
+ /**
+ * Set Input Stream.
+ * @param stream input stream object containing sound data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception SoundException if InputStream is bad
+ * @exception IllegalArgumentException if the specified sound data is
+ * non-null and any other sound data reference is also non-null.
+ * @since Java 3D 1.2
+ */
+ public void setInputStream(InputStream stream) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_URL_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer3"));
+ ((MediaContainerRetained)this.retained).setInputStream(stream);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>getURLString</code>
+ */
+ public String getURL() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_URL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer4"));
+ return ((MediaContainerRetained)this.retained).getURLString();
+ }
+
+ /**
+ * Retrieve URL String.
+ * @return string of URL containing sound data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.2
+ */
+ public String getURLString() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_URL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer4"));
+ return ((MediaContainerRetained)this.retained).getURLString();
+ }
+
+ /**
+ * Retrieve URL Object.
+ * @return URL containing sound data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.2
+ */
+ public URL getURLObject() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_URL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer4"));
+ return ((MediaContainerRetained)this.retained).getURLObject();
+ }
+
+ /**
+ * Retrieve Input Stream.
+ * @return reference to input stream containing sound data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.2
+ */
+ public InputStream getInputStream() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_URL_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("MediaContainer4"));
+ return ((MediaContainerRetained)this.retained).getInputStream();
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced with
+ * <code>cloneNodeComponent(boolean forceDuplicate)</code>
+ */
+ public NodeComponent cloneNodeComponent() {
+ MediaContainer mc = new MediaContainer();
+ mc.duplicateNodeComponent(this);
+ return mc;
+ }
+
+
+ /**
+ * Copies all MediaContainer information from
+ * <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>cloneNodeComponent</code> method and <code>duplicateNodeComponent</code>
+ * method which is, in turn, called by the
+ * <code>cloneTree</code> method.<P>
+ *
+ * @param originalNodeComponent the original node component 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 component'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#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ MediaContainerRetained mc = (MediaContainerRetained)
+ originalNodeComponent.retained;
+ MediaContainerRetained rt = (MediaContainerRetained) retained;
+ rt.setCacheEnable(mc.getCacheEnable());
+ rt.setURLString(mc.getURLString(), false);
+ rt.setURLObject(mc.getURLObject(), false);
+ rt.setInputStream(mc.getInputStream(), false);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/MediaContainerRetained.java b/src/classes/share/javax/media/j3d/MediaContainerRetained.java
new file mode 100644
index 0000000..f63ae95
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MediaContainerRetained.java
@@ -0,0 +1,196 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.net.URL;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.io.File;
+import java.security.*;
+import java.io.InputStream;
+
+/**
+ * The MediaContainerRetained object defines all rendering state that can
+ * be set as a component object of a retained Soundscape node.
+ */
+class MediaContainerRetained extends NodeComponentRetained {
+ /**
+ * Gain Scale Factor applied to source with this attribute
+ */
+ boolean cached = true;
+
+ /**
+ * URL string that references the sound data
+ */
+ URL url = null;
+ String urlString = null;
+ InputStream inputStream = null;
+
+
+ /**
+ * Set Cached flag
+ * @param state flag denoting sound data is cached by app within node
+ */
+ void setCacheEnable(boolean state) {
+ this.cached = state;
+ // changing this AFTER sound data attached to node is ignored
+ // notifyUsers();
+ }
+
+ /**
+ * Retrieve Attrribute Gain (amplitude)
+ * @return gain amplitude scale factor
+ */
+ boolean getCacheEnable() {
+ return this.cached;
+ }
+
+ /**
+ * Set URL object that references the sound data
+ * @param url URL object that references the sound data
+ */
+ void setURLObject(URL url) {
+ setURLObject(url, true);
+ }
+
+ /**
+ * Set URL object that references the sound data
+ * @param url URL object that references the sound data
+ * @param forceLoad ensures that message about change is sent to scheduler
+ */
+ void setURLObject(URL url, boolean forceLoad) {
+ // can NOT set URL object field unless the other related fields are null
+ if (url != null) {
+ if (urlString != null || inputStream != null)
+ throw new IllegalArgumentException(J3dI18N.getString("MediaContainer5"));
+ // Test if url object is valid by openning it
+ try {
+ InputStream stream;
+ stream = url.openStream();
+ stream.close();
+ }
+ catch (Exception e) {
+ throw new SoundException(javax.media.j3d.J3dI18N.getString("MediaContainer0"));
+ }
+ }
+ this.url = url;
+ // notifyUsers();
+ // avoid re-loading SAME MediaContainer when duplicateAttrib calls
+ if (forceLoad)
+ dispatchMessage();
+ }
+
+ /**
+ * Set URL path that references the sound data
+ * @param path string of URL that references the sound data
+ */
+ void setURLString(String path) {
+ setURLString(path, true);
+ }
+
+ /**
+ * Set URL path that references the sound data
+ * @param path string of URL that references the sound data
+ * @param forceLoad ensures that message about change is sent to scheduler
+ */
+ void setURLString(String path, boolean forceLoad) {
+ // can NOT set string field unless the other related fields are null
+ if (path != null) {
+ if (this.url != null || inputStream != null)
+ throw new IllegalArgumentException(J3dI18N.getString("MediaContainer5"));
+ // Test if path string is valid URL by trying to generate a URL
+ // and then openning it
+ try {
+ URL url = new URL(path);
+ InputStream stream;
+ stream = url.openStream();
+ stream.close();
+ }
+ catch (Exception e) {
+ throw new SoundException(javax.media.j3d.J3dI18N.getString("MediaContainer0"));
+ }
+ }
+ this.urlString = path;
+ // notifyUsers();
+ // avoid re-loading SAME MediaContainer when duplicateAttrib calls
+ if (forceLoad)
+ dispatchMessage();
+ }
+
+ /**
+ * Set input stream reference to sound data
+ * @param stream InputStream that references the sound data
+ * @param forceLoad ensures that message about change is sent to scheduler
+ */
+ void setInputStream(InputStream stream) {
+ setInputStream(stream, true);
+ }
+
+ /**
+ * Set input stream reference to sound data
+ * @param stream InputStream that references the sound data
+ */
+ void setInputStream(InputStream stream, boolean forceLoad) {
+ // %%% TODO AudioDevice not intellegent enough to process InputStreams yet
+ // can NOT set stream field unless the other related fields are null
+ if (stream != null) {
+ if (url != null || urlString != null)
+ throw new IllegalArgumentException(J3dI18N.getString("MediaContainer5"));
+ }
+ this.inputStream = stream;
+ // notifyUsers();
+ // avoid re-loading SAME MediaContainer when duplicateAttrib calls
+ if (forceLoad)
+ dispatchMessage();
+ }
+
+ /**
+ * Retrieve URL String
+ * @return URL string that references the sound data
+ */
+ String getURLString() {
+ return this.urlString;
+ }
+
+ /**
+ * Retrieve URL objects
+ * @return URL object that references the sound data
+ */
+ URL getURLObject() {
+ return this.url;
+ }
+
+ /**
+ * Retrieve InputData
+ * @return InputString that references the sound data
+ */
+ InputStream getInputStream() {
+ return this.inputStream;
+ }
+
+ /**
+ * Dispatch a message about a media container change
+ */
+ void dispatchMessage() {
+ // Send message including a integer argumentD
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.SOUND_SCHEDULER;
+ createMessage.type = J3dMessage.MEDIA_CONTAINER_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(SoundRetained.SOUND_DATA_DIRTY_BIT);
+ createMessage.args[2]= new Integer(users.size());
+ createMessage.args[3] = users;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/MemoryFreeList.java b/src/classes/share/javax/media/j3d/MemoryFreeList.java
new file mode 100644
index 0000000..7b8abd0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MemoryFreeList.java
@@ -0,0 +1,264 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * Class for storing various free lists. This class must be
+ * synchronized because different threads may try to access the lists.
+ */
+class MemoryFreeList {
+
+ // 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;
+
+ MemoryFreeList(String className) {
+ this(className, 10);
+ }
+
+ MemoryFreeList(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;
+ }
+
+ /*
+ MemoryFreeList(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;
+ }
+ */
+
+ synchronized int size() {
+ return size;
+ }
+
+
+ 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;
+ }
+ }
+
+ protected 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;
+ }
+
+
+ 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;
+ }
+
+ 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;
+ }
+
+ synchronized Object getObject() {
+ if (size > 0) {
+ return removeLastElement();
+ }
+ else {
+ try {
+ return c.newInstance();
+ }
+ catch (Exception e) {
+ System.out.println(e);
+ return null;
+ }
+ }
+ }
+
+}
+
diff --git a/src/classes/share/javax/media/j3d/ModelClip.java b/src/classes/share/javax/media/j3d/ModelClip.java
new file mode 100644
index 0000000..7dd0a40
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ModelClip.java
@@ -0,0 +1,702 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Enumeration;
+
+/**
+ * The ModelClip leaf node defines a set of 6 arbitrary clipping
+ * planes in the virtual universe. The planes are specified in the
+ * local coordinate system of this node, and may be individually
+ * enabled or disabled. This node also specifies a region of
+ * influence in which this set of planes is active.
+ *<p>
+ * A ModelClip node also contains a list of Group nodes that specifies the
+ * hierarchical scope of this ModelClip. If the scope list is empty, then
+ * the ModelClip node has universe scope: all nodes within the region of
+ * influence are affected by this ModelClip node. If the scope list is
+ * non-empty, then only those Leaf nodes under the Group nodes in the
+ * scope list are affected by this ModelClip node (subject to the
+ * influencing bounds).
+ * <p>
+ * If the regions of influence of multiple ModelClip nodes overlap, the
+ * Java 3D system will choose a single set of model clip planes for those
+ * objects that lie in the intersection. This is done in an
+ * implementation-dependent manner, but in general, the ModelClip node that
+ * is "closest" to the object is chosen.
+ * <p>
+ * The individual planes specify a half-space defined by the equation:
+ * <ul>
+ * Ax + By + Cz + D <= 0
+ * </ul>
+ * where A, B, C, D are the parameters that specify the plane. The
+ * parameters are passed in the x, y, z, and w fields, respectively,
+ * of a Vector4d object. The intersection of the set of half-spaces
+ * corresponding to the enabled planes in this ModelClip node defines
+ * a region in which points are accepted. Points in this acceptance
+ * region will be rendered (subject to view clipping and other
+ * attributes). Points that are not in the acceptance region will not
+ * be rendered.
+ *
+ * @since Java 3D 1.2
+ */
+
+public class ModelClip extends Leaf {
+ /**
+ * Specifies that the ModelClip node allows read access to its influencing
+ * bounds and bounding leaf at runtime.
+ */
+ public static final int ALLOW_INFLUENCING_BOUNDS_READ =
+ CapabilityBits.MODEL_CLIP_ALLOW_INFLUENCING_BOUNDS_READ;
+
+ /**
+ * Specifies that the ModelClip node allows write access to its influencing
+ * bounds and bounding leaf at runtime.
+ */
+ public static final int ALLOW_INFLUENCING_BOUNDS_WRITE =
+ CapabilityBits.MODEL_CLIP_ALLOW_INFLUENCING_BOUNDS_WRITE;
+
+ /**
+ * Specifies that the ModelClip node allows read access to its planes
+ * at runtime.
+ */
+ public static final int ALLOW_PLANE_READ =
+ CapabilityBits.MODEL_CLIP_ALLOW_PLANE_READ;
+
+ /**
+ * Specifies that the ModelClip node allows write access to its planes
+ * at runtime.
+ */
+ public static final int ALLOW_PLANE_WRITE =
+ CapabilityBits.MODEL_CLIP_ALLOW_PLANE_WRITE;
+
+ /**
+ * Specifies that the ModelClip node allows read access to its enable
+ * flags at runtime.
+ */
+ public static final int ALLOW_ENABLE_READ =
+ CapabilityBits.MODEL_CLIP_ALLOW_ENABLE_READ;
+
+ /**
+ * Specifies that the ModelClip node allows write access to its enable
+ * flags at runtime.
+ */
+ public static final int ALLOW_ENABLE_WRITE =
+ CapabilityBits.MODEL_CLIP_ALLOW_ENABLE_WRITE;
+
+ /**
+ * Specifies that this ModelClip node allows read access to its scope
+ * information at runtime.
+ */
+ public static final int ALLOW_SCOPE_READ =
+ CapabilityBits.MODEL_CLIP_ALLOW_SCOPE_READ;
+
+ /**
+ * Specifies that this ModelClip node allows write access to its scope
+ * information at runtime.
+ */
+ public static final int ALLOW_SCOPE_WRITE =
+ CapabilityBits.MODEL_CLIP_ALLOW_SCOPE_WRITE;
+
+
+ /**
+ * Constructs a ModelClip node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * planes[0] : x <= 1 (1,0,0,-1)<br>
+ * planes[1] : -x <= 1 (-1,0,0,-1)<br>
+ * planes[2] : y <= 1 (0,1,0,-1)<br>
+ * planes[3] : -y <= 1 (0,-1,0,-1)<br>
+ * planes[4] : z <= 1 (0,0,1,-1)<br>
+ * planes[5] : -z <= 1 (0,0,-1,-1)<br>
+ * enables : all planes enabled<br>
+ * scope : empty (universe scope)<br>
+ * influencing bounds : null<br>
+ * influencing bounding leaf : null<br>
+ * </ul>
+ */
+ public ModelClip() {
+ // Just use the defaults
+ }
+
+
+ /**
+ * Constructs a ModelClip node using the specified planes. The individual
+ * planes are copied into this node. All planes are enabled.
+ * @param planes an array of 6 model clipping planes
+ */
+ public ModelClip(Vector4d[] planes) {
+ ((ModelClipRetained)this.retained).initPlanes(planes);
+ }
+
+
+ /**
+ * Constructs a ModelClip node using the specified planes and enable
+ * flags. The individual
+ * planes and enable flags are copied into this node.
+ * @param planes an array of 6 model clipping planes
+ * @param enables an array of 6 enable flags
+ */
+ public ModelClip(Vector4d[] planes, boolean[] enables) {
+ ((ModelClipRetained)this.retained).initPlanes(planes);
+ ((ModelClipRetained)this.retained).initEnables(enables);
+ }
+
+
+ /**
+ * Set the ModelClip node's influencing region to the specified bounds.
+ * This is used when the influencing bounding leaf is set to null.
+ * @param region the bounds that contains the new influencing
+ * region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip0"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).setInfluencingBounds(region);
+ else
+ ((ModelClipRetained)this.retained).initInfluencingBounds(region);
+ }
+
+
+ /**
+ * Retrieves the ModelClip node's influencing bounds.
+ * @return this node's influencing bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getInfluencingBounds() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip1"));
+
+ return ((ModelClipRetained)this.retained).getInfluencingBounds();
+ }
+
+
+ /**
+ * Set the ModelClip node's influencing region to the specified
+ * bounding leaf.
+ * When set to a value other than null, this overrides the influencing
+ * bounds object.
+ * @param region the bounding leaf node used to specify the
+ * new influencing region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_INFLUENCING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip13"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).setInfluencingBoundingLeaf(region);
+ else
+ ((ModelClipRetained)this.retained).initInfluencingBoundingLeaf(region);
+ }
+
+
+ /**
+ * Retrieves the ModelClip node's influencing bounding leaf.
+ * @return this node's influencing bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getInfluencingBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_INFLUENCING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip14"));
+
+ return ((ModelClipRetained)this.retained).getInfluencingBoundingLeaf();
+ }
+
+
+ /**
+ * Replaces the node at the specified index in this ModelClip node's
+ * list of scopes with the specified Group node.
+ * By default, ModelClip nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be stored at the specified index.
+ * @param index the index of the Group node to be replaced.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void setScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip7"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).setScope(scope, index);
+ else
+ ((ModelClipRetained)this.retained).initScope(scope, index);
+ }
+
+
+ /**
+ * Retrieves the Group node at the specified index from this ModelClip node's
+ * list of scopes.
+ * @param index the index of the Group node to be returned.
+ * @return the Group node at the specified index.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Group getScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip8"));
+
+ return ((ModelClipRetained)this.retained).getScope(index);
+ }
+
+
+ /**
+ * Inserts the specified Group node into this ModelClip node's
+ * list of scopes at the specified index.
+ * By default, ModelClip nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be inserted at the specified index.
+ * @param index the index at which the Group node is inserted.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void insertScope(Group scope, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip9"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).insertScope(scope, index);
+ else
+ ((ModelClipRetained)this.retained).initInsertScope(scope, index);
+ }
+
+
+ /**
+ * Removes the node at the specified index from this ModelClip node's
+ * list of scopes. If this operation causes the list of scopes to
+ * become empty, then this ModelClip will have universe scope: all nodes
+ * within the region of influence will be affected by this ModelClip node.
+ * @param index the index of the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the group node at the
+ * specified index is part of a compiled scene graph
+ */
+ public void removeScope(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip10"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).removeScope(index);
+ else
+ ((ModelClipRetained)this.retained).initRemoveScope(index);
+ }
+
+
+ /**
+ * Returns an enumeration of this ModelClip node's list of scopes.
+ * @return an Enumeration object containing all nodes in this ModelClip node's
+ * list of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Enumeration getAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip11"));
+
+ return (Enumeration) ((ModelClipRetained)this.retained).getAllScopes();
+ }
+
+
+ /**
+ * Appends the specified Group node to this ModelClip node's list of scopes.
+ * By default, ModelClip nodes are scoped only by their influencing
+ * bounds. This allows them to be further scoped by a list of
+ * nodes in the hierarchy.
+ * @param scope the Group node to be appended.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ */
+ public void addScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip12"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).addScope(scope);
+ else
+ ((ModelClipRetained)this.retained).initAddScope(scope);
+ }
+
+
+ /**
+ * Returns the number of nodes in this ModelClip node's list of scopes.
+ * If this number is 0, then the list of scopes is empty and this
+ * ModelClip node has universe scope: all nodes within the region of
+ * influence are affected by this ModelClip node.
+ * @return the number of nodes in this ModelClip node's list of scopes.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int numScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip11"));
+
+ return ((ModelClipRetained)this.retained).numScopes();
+ }
+
+
+ /**
+ * Retrieves the index of the specified Group node in this
+ * ModelClip node's list of scopes.
+ *
+ * @param scope the Group node to be looked up.
+ * @return the index of the specified Group node;
+ * returns -1 if the object is not in the list.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip8"));
+ return ((ModelClipRetained)this.retained).indexOfScope(scope);
+ }
+
+
+ /**
+ * Removes the specified Group node from this ModelClip
+ * node's list of scopes. If the specified object is not in the
+ * list, the list is not modified. If this operation causes the
+ * list of scopes to become empty, then this ModelClip
+ * will have universe scope: all nodes within the region of
+ * influence will be affected by this ModelClip node.
+ *
+ * @param scope the Group node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if the specified group node
+ * is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeScope(Group scope) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip10"));
+ if (isLive())
+ ((ModelClipRetained)this.retained).removeScope(scope);
+ else
+ ((ModelClipRetained)this.retained).initRemoveScope(scope);
+ }
+
+
+ /**
+ * Removes all Group nodes from this ModelClip node's
+ * list of scopes. The ModelClip node will then have
+ * universe scope: all nodes within the region of influence will
+ * be affected by this ModelClip node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if any group node in this
+ * node's list of scopes is part of a compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllScopes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCOPE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip10"));
+ if (isLive())
+ ((ModelClipRetained)this.retained).removeAllScopes();
+ else
+ ((ModelClipRetained)this.retained).initRemoveAllScopes();
+ }
+
+
+ /**
+ * Sets the clipping planes of this ModelClip node to the
+ * specified planes.
+ * The individual planes are copied into this node.
+ * @param planes an array of 6 model clipping planes
+ */
+ public void setPlanes(Vector4d[] planes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_PLANE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip2"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).setPlanes(planes);
+ else
+ ((ModelClipRetained)this.retained).initPlanes(planes);
+ }
+
+
+ /**
+ * Retrieves the clipping planes from this ModelClip node.
+ * The individual planes are copied into the specified planes, which
+ * must be allocated by the caller. The array must be large
+ * enough to hold all of the vectors.
+ * @param planes an array of 6 vectors that will receive the model
+ * clipping planes from this node
+ */
+ public void getPlanes(Vector4d[] planes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_PLANE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip3"));
+
+ ((ModelClipRetained)this.retained).getPlanes(planes);
+ }
+
+
+ /**
+ * Sets the specified clipping plane of this ModelClip node.
+ * The specified plane is copied into this node.
+ * @param planeNum specifies which model clipping plane (0-5) is replaced
+ * @param plane new model clipping plane
+ */
+ public void setPlane(int planeNum, Vector4d plane) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_PLANE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip2"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).setPlane(planeNum, plane);
+ else
+ ((ModelClipRetained)this.retained).initPlane(planeNum, plane);
+
+ }
+
+
+ /**
+ * Retrieves the specified clipping plane from this ModelClip node.
+ * The plane is copied into the specified plane, which
+ * must be allocated by the caller.
+ * @param planeNum specifies which model clipping plane (0-5) is retrieved
+ * @param plane a vector that will receive the specified model
+ * clipping plane from this node
+ */
+ public void getPlane(int planeNum, Vector4d plane) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_PLANE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip3"));
+
+ ((ModelClipRetained)this.retained).getPlane(planeNum, plane);
+ }
+
+
+ /**
+ * Sets the per-plane enable flags of this ModelClip node to the
+ * specified values.
+ * @param enables an array of 6 enable flags
+ */
+ public void setEnables(boolean[] enables) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ENABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip4"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).setEnables(enables);
+ else
+ ((ModelClipRetained)this.retained).initEnables(enables);
+ }
+
+
+ /**
+ * Retrieves the per-plane enable flags from this ModelClip node.
+ * The enable flags are copied into the specified array.
+ * The array must be large enough to hold all of the enables.
+ * @param enables an array of 6 booleans that will receive the
+ * enable flags from this node
+ */
+ public void getEnables(boolean[] enables) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ENABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip5"));
+
+ ((ModelClipRetained)this.retained).getEnables(enables);
+ }
+
+
+ /**
+ * Sets the specified enable flag of this ModelClip node.
+ * @param planeNum specifies which enable flag (0-5) is set
+ * @param enable new enable flag
+ */
+ public void setEnable(int planeNum, boolean enable) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ENABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip4"));
+
+ if (isLive())
+ ((ModelClipRetained)this.retained).setEnable(planeNum, enable);
+ else
+ ((ModelClipRetained)this.retained).initEnable(planeNum, enable);
+ }
+
+
+ /**
+ * Retrieves the specified enable flag from this ModelClip node.
+ * @param planeNum specifies which enable flag (0-5) is retrieved
+ * @return the specified enable flag
+ */
+ public boolean getEnable(int planeNum) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ENABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ModelClip5"));
+
+ return ((ModelClipRetained)this.retained).getEnable(planeNum);
+ }
+
+ /**
+ * Creates the retained mode ModelClipRetained object that
+ * this ModelClip node will point to.
+ */
+ void createRetained() {
+ this.retained = new ModelClipRetained();
+ this.retained.setSource(this);
+ }
+
+
+
+ /**
+ * 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) {
+ ModelClip c = new ModelClip();
+ c.duplicateNode(this, forceDuplicate);
+ return c;
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ ModelClipRetained rt = (ModelClipRetained) retained;
+ BoundingLeaf bl = rt.getInfluencingBoundingLeaf();
+
+ // check for influencingBoundingLeaf
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference( bl);
+ rt.initInfluencingBoundingLeaf((BoundingLeaf) o);
+ }
+
+ int num = rt.numScopes();
+ for (int i=0; i < num; i++) {
+ rt.initScope((Group) referenceTable.
+ getNewObjectReference(rt.getScope(i)), i);
+ }
+ }
+
+
+ /**
+ * Copies all Clip 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ModelClipRetained attr = (ModelClipRetained)
+ originalNode.retained;
+ ModelClipRetained rt = (ModelClipRetained) retained;
+
+ Vector4d plane = new Vector4d();
+
+ for (int i=5; i >=0; i--) {
+ attr.getPlane(i, plane);
+ rt.initPlane(i, plane);
+ rt.initEnable(i, attr.getEnable(i));
+ }
+ rt.initInfluencingBounds(attr.getInfluencingBounds());
+
+ Enumeration elm = attr.getAllScopes();
+ while (elm.hasMoreElements()) {
+ // this reference will set correctly in updateNodeReferences() callback
+ rt.initAddScope((Group) elm.nextElement());
+ }
+
+ // correct value will set in updateNodeReferences
+ rt.initInfluencingBoundingLeaf(attr.getInfluencingBoundingLeaf());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ModelClipRetained.java b/src/classes/share/javax/media/j3d/ModelClipRetained.java
new file mode 100644
index 0000000..0e6c571
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ModelClipRetained.java
@@ -0,0 +1,1055 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.ArrayList;
+import javax.vecmath.*;
+
+/**
+ * The ModelClip retained object.
+ */
+class ModelClipRetained extends LeafRetained {
+
+ // Statics used when something in the fog changes
+ static final int PLANE_CHANGED = 0x0001;
+ static final int PLANES_CHANGED = 0x0002;
+ static final int ENABLE_CHANGED = 0x0004;
+ static final int ENABLES_CHANGED = 0x0008;
+ static final int BOUNDS_CHANGED = 0x0010;
+ static final int BOUNDINGLEAF_CHANGED = 0x0020;
+ static final int SCOPE_CHANGED = 0x0040;
+ static final int INIT_MIRROR = 0x0080;
+ static final int CLEAR_MIRROR = 0x0100;
+ static final int LAST_DEFINED_BIT = 0x0100;
+
+ /**
+ * The clip planes and the enable bits
+ */
+ Vector4d[] planes = new Vector4d[6];
+ boolean[] enables = new boolean[6];
+
+ Vector4d[] xformPlanes = new Vector4d[6];
+
+ // enableFlag is true if one of the enables is true
+ // only used by mirror object
+ boolean enableFlag = false;
+
+ /**
+ * The Boundary object defining the model clip's region of influencing
+ */
+ Bounds regionOfInfluence = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * The transformed value of the influencingRegion.
+ */
+ Bounds region = null;
+
+ /**
+ * Vector of GroupRetained nodes that scopes this model clip.
+ */
+ Vector scopes = new Vector();
+
+ //Boolean to indicate if this object is scoped (only used for mirror objects
+ boolean isScoped = false;
+
+ // The object that contains the dynamic HashKey - a string type object
+ // Used in scoping
+ HashKey tempKey = new HashKey(250);
+
+ // This is true when this model clip is referenced in an immediate mode context
+ boolean inImmCtx = false;
+
+ // The mirror copy of this modelClip
+ ModelClipRetained mirrorModelClip = null;
+
+ // A reference to the scene graph model clip
+ ModelClipRetained sgModelClip = null;
+
+ // Target threads to be notified when model clip changes
+ final static int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+ /**
+ * The EnvironmentSets which reference this model clip.
+ * Note that multiple RenderBin update thread may access
+ * this shared environmentSets simultaneously.
+ * So we use UnorderList which sync. all the operations.
+ */
+ UnorderList environmentSets = new UnorderList(1, EnvironmentSet.class);
+
+ // Is true, if the mirror clip is viewScoped
+ boolean isViewScoped = false;
+
+ /**
+ * Constructs and initializes model clip planes
+ */
+ ModelClipRetained() {
+
+ // planes contains the negate default values
+ planes[0] = new Vector4d( 1.0, 0.0, 0.0,-1.0);
+ planes[1] = new Vector4d(-1.0, 0.0, 0.0,-1.0);
+ planes[2] = new Vector4d( 0.0, 1.0, 0.0,-1.0);
+ planes[3] = new Vector4d( 0.0,-1.0, 0.0,-1.0);
+ planes[4] = new Vector4d( 0.0, 0.0, 1.0,-1.0);
+ planes[5] = new Vector4d( 0.0, 0.0, -1.0,-1.0);
+
+ for (int i = 0; i < 6; i++)
+ xformPlanes[i] = new Vector4d(planes[i]);
+
+ enables[0] = enables[1] = enables[2] = enables[3] =
+ enables[4] = enables[5] = true;
+ }
+
+ /**
+ * Initializes planes before the object is live
+ */
+ void initPlanes(Vector4d[] planes) {
+
+ if (staticTransform != null) {
+ Transform3D xform = staticTransform.getNormalTransform();
+ for (int i = 0; i < 6; i++) {
+ this.planes[i].set(planes[i]);
+ xform.transform(this.planes[i], this.xformPlanes[i]);
+ }
+ } else {
+ for (int i = 0; i < 6; i++) {
+ this.planes[i].set(planes[i]);
+ this.xformPlanes[i].set(this.planes[i]);
+ }
+ }
+ }
+
+ /**
+ * Sets the clip planes and send a message
+ */
+ void setPlanes(Vector4d[] planes) {
+ Vector4d[] pl = new Vector4d[6];
+ initPlanes(planes);
+
+ for (int i = 0; i < 6; i++) {
+ pl[i] = new Vector4d(this.xformPlanes[i]);
+ }
+
+ sendMessage(PLANES_CHANGED, pl, null);
+ }
+
+ /**
+ * Initializes planes before the object is live
+ */
+ void initPlane(int planeNum, Vector4d plane) {
+ if (planeNum < 0 || planeNum > 5)
+ throw new IllegalArgumentException(J3dI18N.getString("ModelClip6"));
+
+ if (staticTransform != null) {
+ Transform3D xform = staticTransform.getNormalTransform();
+ this.planes[planeNum].set(plane);
+ xform.transform(this.planes[planeNum], this.xformPlanes[planeNum]);
+ } else {
+ this.planes[planeNum].set(plane);
+ this.xformPlanes[planeNum].set(plane);
+ }
+ }
+
+ /**
+ * Sets the clip planes and send a message
+ */
+ void setPlane(int planeNum, Vector4d plane) {
+ initPlane(planeNum, plane);
+ sendMessage(PLANE_CHANGED,
+ new Integer(planeNum),
+ new Vector4d(this.xformPlanes[planeNum]));
+ }
+
+ /**
+ * Gets planes
+ */
+ void getPlanes(Vector4d[] planes){
+
+ for (int i = 0; i < 6; i++) {
+ planes[i].set(this.planes[i]);
+ }
+ }
+
+ /**
+ * Gets the specified clipping plane
+ */
+ void getPlane(int planeNum, Vector4d plane) {
+ if (planeNum < 0 || planeNum > 5)
+ throw new IllegalArgumentException(J3dI18N.getString("ModelClip6"));
+ plane.set(this.planes[planeNum]);
+ }
+
+ /**
+ * Initializes planes before the object is live
+ */
+ void initEnables(boolean[] enables) {
+ this.enables[0] = enables[0];
+ this.enables[1] = enables[1];
+ this.enables[2] = enables[2];
+ this.enables[3] = enables[3];
+ this.enables[4] = enables[4];
+ this.enables[5] = enables[5];
+ }
+
+ /**
+ * Sets the clip planes and send a message
+ */
+ void setEnables(boolean[] enables) {
+ Boolean[] en = new Boolean[6];
+
+ initEnables(enables);
+ en[0] = (enables[0] ? Boolean.TRUE: Boolean.FALSE);
+ en[1] = (enables[1] ? Boolean.TRUE: Boolean.FALSE);
+ en[2] = (enables[2] ? Boolean.TRUE: Boolean.FALSE);
+ en[3] = (enables[3] ? Boolean.TRUE: Boolean.FALSE);
+ en[4] = (enables[4] ? Boolean.TRUE: Boolean.FALSE);
+ en[5] = (enables[5] ? Boolean.TRUE: Boolean.FALSE);
+
+ sendMessage(ENABLES_CHANGED, en, null);
+ }
+
+ /**
+ * Initializes planes before the object is live
+ */
+ void initEnable(int planeNum, boolean enable) {
+ if (planeNum < 0 || planeNum > 5)
+ throw new IllegalArgumentException(J3dI18N.getString("ModelClip6"));
+ this.enables[planeNum] = enable;
+ }
+
+ /**
+ * Sets the clip planes and send a message
+ */
+ void setEnable(int planeNum, boolean enable) {
+ initEnable(planeNum, enable);
+ sendMessage(ENABLE_CHANGED,
+ new Integer(planeNum),
+ (enable ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Gets enables
+ */
+ void getEnables(boolean[] enables) {
+ enables[0] = this.enables[0];
+ enables[1] = this.enables[1];
+ enables[2] = this.enables[2];
+ enables[3] = this.enables[3];
+ enables[4] = this.enables[4];
+ enables[5] = this.enables[5];
+ }
+
+ /**
+ * Gets the specified enable
+ */
+ boolean getEnable(int planeNum) {
+ if (planeNum < 0 || planeNum > 5)
+ throw new IllegalArgumentException(J3dI18N.getString("ModelClip6"));
+ return (this.enables[planeNum]);
+ }
+
+ /**
+ * Set the Model Clip's region of influencing
+ */
+ void initInfluencingBounds(Bounds region) {
+ if (region != null) {
+ this.regionOfInfluence = (Bounds) region.clone();
+ if (staticTransform != null) {
+ regionOfInfluence.transform(staticTransform.transform);
+ }
+ } else {
+ this.regionOfInfluence = null;
+ }
+ }
+
+ /**
+ * Set the Model Clip's region of influencing and send message
+ */
+ void setInfluencingBounds(Bounds region) {
+ initInfluencingBounds(region);
+ sendMessage(BOUNDS_CHANGED,
+ (region != null ? (Bounds) region.clone(): null),
+ null);
+ }
+
+ /**
+ * Get the Model Clip's region of influencing.
+ */
+ Bounds getInfluencingBounds() {
+ Bounds b = null;
+
+ if (regionOfInfluence != null) {
+ b = (Bounds) regionOfInfluence.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Set the Model Clip's region of influencing to the specified Leaf node.
+ */
+ void initInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ } else {
+ boundingLeaf = null;
+ }
+ }
+
+ /**
+ * Set the Model Clip's region of influencing to the specified Leaf node.
+ */
+ void setInfluencingBoundingLeaf(BoundingLeaf region) {
+ if (boundingLeaf != null)
+ boundingLeaf.mirrorBoundingLeaf.removeUser(mirrorModelClip);
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorModelClip);
+ } else {
+ boundingLeaf = null;
+ }
+
+ sendMessage(BOUNDINGLEAF_CHANGED,
+ (boundingLeaf != null ?
+ boundingLeaf.mirrorBoundingLeaf : null),
+ null);
+ }
+
+
+ /**
+ * Get the Model Clip's region of influencing.
+ */
+ BoundingLeaf getInfluencingBoundingLeaf() {
+ return (boundingLeaf != null ?
+ (BoundingLeaf)boundingLeaf.source : null);
+ }
+
+ /**
+ * Replaces the specified scope with the scope provided.
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void initScope(Group scope, int index) {
+ scopes.setElementAt((GroupRetained)(scope.retained), index);
+ }
+
+ /**
+ * Replaces the specified scope with the scope provided.
+ * @param scope the new scope
+ * @param index which scope to replace
+ */
+ void setScope(Group scope, int index) {
+
+ ArrayList addScopeList = new ArrayList();
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group;
+ Object[] scopeInfo = new Object[3];
+
+ group = (GroupRetained) scopes.get(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedModelClip(mirrorModelClip, removeScopeList, tempKey);
+
+ group = (GroupRetained)scope.retained;
+ initScope(scope, index);
+ tempKey.reset();
+ // If its a group, then add the scope to the group, if
+ // its a shape, then keep a list to be added during
+ // updateMirrorObject
+ group.addAllNodesForScopedModelClip(mirrorModelClip,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE:Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo, null);
+ }
+
+ /**
+ * Inserts the specified scope at specified index
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void initInsertScope(Node scope, int index) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ group.setMclipScope();
+ scopes.insertElementAt((GroupRetained)(scope.retained), index);
+ }
+
+ /**
+ * Inserts the specified scope at specified index and sends
+ * a message
+ * @param scope the new scope
+ * @param index position to insert new scope at
+ */
+ void insertScope(Node scope, int index) {
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+
+ initInsertScope(scope, index);
+ GroupRetained group = (GroupRetained)scope.retained;
+ tempKey.reset();
+ group.addAllNodesForScopedModelClip(mirrorModelClip,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo, null);
+ }
+
+
+ void initRemoveScope(int index) {
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+ group.removeMclipScope();
+ scopes.removeElementAt(index);
+
+ }
+
+ void removeScope(int index) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList removeScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+
+ initRemoveScope(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedModelClip(mirrorModelClip, removeScopeList, tempKey);
+
+ scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo, null);
+ }
+
+ /**
+ * Removes the specified Group node from this ModelClip's list of
+ * scopes if the specified node is not found in the list of scoped
+ * nodes, method returns quietly.
+ *
+ * @param Group node to be removed
+ */
+ void removeScope(Group node) {
+ int ind = indexOfScope(node);
+ if(ind >= 0)
+ removeScope(ind);
+ }
+
+ void initRemoveScope(Group node) {
+ int ind = indexOfScope(node);
+ if(ind >= 0)
+ initRemoveScope(ind);
+ }
+
+ /**
+ * Removes all the Group nodes from the ModelClip's scope
+ * list. The ModelClip reverts to universal scope.
+ */
+ void removeAllScopes() {
+ Object[] scopeInfo = new Object[3];
+ ArrayList removeScopeList = new ArrayList();
+ int n = scopes.size();
+ for(int index = n-1; index >= 0; index--) {
+ GroupRetained group = (GroupRetained)scopes.elementAt(index);
+ initRemoveScope(index);
+ tempKey.reset();
+ group.removeAllNodesForScopedModelClip(mirrorModelClip, removeScopeList, tempKey);
+ }
+
+ scopeInfo[0] = null;
+ scopeInfo[1] = removeScopeList;
+ scopeInfo[2] = (Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo, null);
+ }
+
+
+ void initRemoveAllScopes() {
+ int n = scopes.size();
+ for(int i = n-1; i >= 0; i--) {
+ initRemoveScope(i);
+ }
+ }
+
+ /**
+ * Returns the scope specified by the index.
+ * @param index which scope to return
+ * @return the scoperen at location index
+ */
+ Group getScope(int index) {
+ return (Group)(((GroupRetained)(scopes.elementAt(index))).source);
+ }
+
+ /**
+ * Returns an enumeration object of the scoperen.
+ * @return an enumeration object of the scoperen
+ */
+ Enumeration getAllScopes() {
+ Enumeration elm = scopes.elements();
+ Vector v = new Vector(scopes.size());
+ while (elm.hasMoreElements()) {
+ v.add( ((GroupRetained) elm.nextElement()).source);
+ }
+ return v.elements();
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes before
+ * the fog is alive
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void initAddScope(Group scope) {
+ GroupRetained group = (GroupRetained)scope.retained;
+ scopes.addElement((GroupRetained)(scope.retained));
+ group.setMclipScope();
+ }
+
+ /**
+ * Appends the specified scope to this node's list of scopes.
+ * @param scope the scope to add to this node's list of scopes
+ */
+ void addScope(Group scope) {
+
+ Object[] scopeInfo = new Object[3];
+ ArrayList addScopeList = new ArrayList();
+ GroupRetained group = (GroupRetained)scope.retained;
+
+ initAddScope(scope);
+ tempKey.reset();
+ group.addAllNodesForScopedModelClip(mirrorModelClip,addScopeList, tempKey);
+ scopeInfo[0] = addScopeList;
+ scopeInfo[1] = null;
+ scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE);
+ sendMessage(SCOPE_CHANGED, scopeInfo, null);
+ }
+
+ /**
+ * Returns a count of this nodes' scopes.
+ * @return the number of scopes descendant from this node
+ */
+ int numScopes() {
+ return scopes.size();
+ }
+
+ /**
+ * Returns the index of the specified Group node within the ModelClip's list of scoped
+ * Group nodes
+ * @param Group node whose index is desired
+ * @return index of this node
+ */
+ int indexOfScope(Group node) {
+ if(node != null)
+ return scopes.indexOf((GroupRetained)node.retained);
+ else
+ return scopes.indexOf(null);
+ }
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+
+ /**
+ * This method and its native counterpart update the native context
+ * model clip planes.
+ */
+ native void update(long ctx, int planeNum, boolean enableFlag,
+ double A, double B, double C, double D);
+
+ void update(Canvas3D cv, int enableMask) {
+ cv.setModelViewMatrix(cv.ctx,
+ cv.vworldToEc.mat,
+ getLastLocalToVworld());
+ update(cv.ctx, enableMask, getLastLocalToVworld());
+ }
+
+ void update(long ctx, int enableMask, Transform3D trans) {
+ if (!VirtualUniverse.mc.isD3D()) {
+ for (int i = 0; i < 6; i ++) {
+ update(ctx, i, ((enableMask & (1 << i)) != 0),
+ xformPlanes[i].x, xformPlanes[i].y,
+ xformPlanes[i].z, xformPlanes[i].w);
+ }
+ return;
+ }
+
+ // For D3D we need to transform the plane equations from local to
+ // world coordinate.
+ Transform3D invtrans = new Transform3D(trans);
+
+ // can't call getNormalTransform() since it will cache
+ // normalTransform and may return previous result next time.
+ invtrans.invert();
+ invtrans.transpose();
+
+ for (int i=0; i < 6; i++) {
+ if ((enableMask & (1 << i)) != 0) {
+
+ Vector4d vec = new Vector4d(xformPlanes[i].x, xformPlanes[i].y,
+ xformPlanes[i].z, xformPlanes[i].w);
+ vec.normalize();
+ invtrans.transform(vec);
+ update(ctx, i, true, vec.x, vec.y, vec.z, vec.w);
+
+ } else {
+ update(ctx, i, false, 0, 0, 0, 0);
+ }
+ }
+ }
+
+ void initMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ Object[] scopeInfo = (Object[]) args[2];
+ Boolean scoped = (Boolean)scopeInfo[0];
+ ArrayList shapeList = (ArrayList)scopeInfo[1];
+ BoundingLeafRetained bl=(BoundingLeafRetained)((Object[])args[4])[0];
+ Bounds bnds = (Bounds)((Object[])args[4])[1];
+
+ for (int i = 0; i < shapeList.size(); i++) {
+ shape = ((GeometryAtom)shapeList.get(i)).source;
+ shape.addModelClip(mirrorModelClip);
+ }
+ mirrorModelClip.isScoped = scoped.booleanValue();
+
+ if (bl != null) {
+ mirrorModelClip.boundingLeaf = bl.mirrorBoundingLeaf;
+ mirrorModelClip.region = boundingLeaf.transformedRegion;
+ } else {
+ mirrorModelClip.boundingLeaf = null;
+ mirrorModelClip.region = null;
+ }
+
+ if (bnds != null) {
+ mirrorModelClip.regionOfInfluence = bnds;
+ if (mirrorModelClip.region == null) {
+ mirrorModelClip.region = (Bounds)regionOfInfluence.clone();
+ mirrorModelClip.region.transform(regionOfInfluence, getLastLocalToVworld());
+ }
+ }
+ else {
+ mirrorModelClip.regionOfInfluence = null;
+ }
+ boolean[] ens = (boolean[])((Object[])args[4])[2];
+
+ for (int i = 0; i < ens.length; i++) {
+ mirrorModelClip.enables[i] = ens[i];
+ }
+ mirrorModelClip.enableFlag = mirrorModelClip.enables[0] |
+ mirrorModelClip.enables[1] |
+ mirrorModelClip.enables[2] |
+ mirrorModelClip.enables[3] |
+ mirrorModelClip.enables[4] |
+ mirrorModelClip.enables[5] ;
+
+ }
+
+
+
+ void updateMirrorObject(Object[] objs) {
+ int component = ((Integer)objs[1]).intValue();
+ if ((component & PLANES_CHANGED) != 0) {
+ Vector4d[] pl = ((Vector4d[]) objs[2]);
+
+ for (int i = 0; i < 6; i++) {
+ mirrorModelClip.xformPlanes[i].set(pl[i]);
+ }
+ }
+ else if ((component & PLANE_CHANGED) != 0) {
+ int planeNum = ((Integer)objs[2]).intValue();
+
+ mirrorModelClip.xformPlanes[planeNum].set((Vector4d)objs[3]);
+ }
+ else if ((component & INIT_MIRROR) != 0) {
+ Vector4d[] pl = (Vector4d[]) objs[3];
+ for (int i = 0; i < 6; i++) {
+ mirrorModelClip.xformPlanes[i].set(pl[i]);
+ }
+ }
+ }
+
+ // The update Object function.
+ void updateImmediateMirrorObject(Object[] objs) {
+ int component = ((Integer)objs[1]).intValue();
+ Transform3D trans;
+
+
+ if ((component & BOUNDINGLEAF_CHANGED) != 0) {
+ mirrorModelClip.boundingLeaf = (BoundingLeafRetained)objs[2];
+ if (objs[2] != null) {
+ mirrorModelClip.region =
+ (Bounds)mirrorModelClip.boundingLeaf.transformedRegion;
+ }
+ else {
+ if (mirrorModelClip.regionOfInfluence != null) {
+ mirrorModelClip.region =
+ ((Bounds)mirrorModelClip.regionOfInfluence).copy(mirrorModelClip.region);
+ mirrorModelClip.region.transform(mirrorModelClip.regionOfInfluence,
+ getCurrentLocalToVworld());
+ }
+ else {
+ mirrorModelClip.region = null;
+ }
+
+ }
+ }
+
+ if ((component & BOUNDS_CHANGED) != 0) {
+ mirrorModelClip.regionOfInfluence = (Bounds) objs[2];
+ if (mirrorModelClip.boundingLeaf == null) {
+ if (objs[2] != null) {
+ mirrorModelClip.region =
+ ((Bounds)mirrorModelClip.regionOfInfluence).copy(mirrorModelClip.region);
+
+ mirrorModelClip.region.transform(mirrorModelClip.regionOfInfluence,
+ getCurrentLocalToVworld());
+ }
+ else {
+ mirrorModelClip.region = null;
+ }
+ }
+ }
+
+ if ((component & SCOPE_CHANGED) != 0) {
+ Object[] scopeList = (Object[])objs[2];
+ ArrayList addList = (ArrayList)scopeList[0];
+ ArrayList removeList = (ArrayList)scopeList[1];
+ boolean isScoped = ((Boolean)scopeList[2]).booleanValue();
+
+ if (addList != null) {
+ mirrorModelClip.isScoped = isScoped;
+ for (int i = 0; i < addList.size(); i++) {
+ Shape3DRetained obj = ((GeometryAtom)addList.get(i)).source;
+ obj.addModelClip(mirrorModelClip);
+ }
+ }
+
+ if (removeList != null) {
+ mirrorModelClip.isScoped = isScoped;
+ for (int i = 0; i < removeList.size(); i++) {
+ Shape3DRetained obj = ((GeometryAtom)removeList.get(i)).source;
+ obj.removeModelClip(mirrorModelClip);
+ }
+ }
+ }
+
+ if ((component & ENABLES_CHANGED) != 0) {
+ Boolean[] en = ((Boolean[]) objs[2]);
+
+ mirrorModelClip.enables[0] = en[0].booleanValue();
+ mirrorModelClip.enables[1] = en[1].booleanValue();
+ mirrorModelClip.enables[2] = en[2].booleanValue();
+ mirrorModelClip.enables[3] = en[3].booleanValue();
+ mirrorModelClip.enables[4] = en[4].booleanValue();
+ mirrorModelClip.enables[5] = en[5].booleanValue();
+ mirrorModelClip.enableFlag = mirrorModelClip.enables[0] |
+ mirrorModelClip.enables[1] |
+ mirrorModelClip.enables[2] |
+ mirrorModelClip.enables[3] |
+ mirrorModelClip.enables[4] |
+ mirrorModelClip.enables[5] ;
+ } else if ((component & ENABLE_CHANGED) != 0) {
+ int planeNum = ((Integer)objs[2]).intValue();
+
+ mirrorModelClip.enables[planeNum] = ((Boolean)objs[3]).booleanValue();
+ mirrorModelClip.enableFlag = mirrorModelClip.enables[0] |
+ mirrorModelClip.enables[1] |
+ mirrorModelClip.enables[2] |
+ mirrorModelClip.enables[3] |
+ mirrorModelClip.enables[4] |
+ mirrorModelClip.enables[5] ;
+ }
+ }
+
+
+ /** Note: This routine will only be called on
+ * the mirror object - will update the object's
+ * cached region and transformed region
+ */
+ void updateBoundingLeaf() {
+ if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) {
+ region = boundingLeaf.transformedRegion;
+ } else {
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence, getCurrentLocalToVworld());
+ } else {
+ region = null;
+ }
+ }
+ }
+
+
+ void setLive(SetLiveState s) {
+ GroupRetained group;
+
+ super.doSetLive(s);
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("ModelClipRetained1"));
+ }
+
+ // Create the mirror object
+
+
+ if (mirrorModelClip == null) {
+ mirrorModelClip = (ModelClipRetained)this.clone();
+ mirrorModelClip.boundingLeaf = null;
+ mirrorModelClip.sgModelClip = this;
+ }
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(mirrorModelClip);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(mirrorModelClip);
+ }
+
+ // If bounding leaf is not null, add the mirror object as a user
+ // so that any changes to the bounding leaf will be received
+ if (boundingLeaf != null) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorModelClip);
+ }
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorModelClip, Targets.ENV_TARGETS);
+ }
+ mirrorModelClip.switchState = (SwitchState)s.switchStates.get(0);
+
+ // add this model clip to the transform target
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorModelClip, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+ super.markAsLive();
+
+
+ // Initialize the mirror object, this needs to be done, when
+ // renderBin is not accessing any of the fields
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.MODELCLIP_CHANGED;
+ createMessage.args[0] = this;
+ // a snapshot of all attributes that needs to be initialized
+ // in the mirror object
+ createMessage.args[1]= new Integer(INIT_MIRROR);
+ ArrayList addScopeList = new ArrayList();
+ for (int i = 0; i < scopes.size(); i++) {
+ group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.addAllNodesForScopedModelClip(mirrorModelClip, addScopeList, tempKey);
+ }
+ Object[] scopeInfo = new Object[2];
+ scopeInfo[0] = ((scopes.size() > 0) ? Boolean.TRUE:Boolean.FALSE);
+ scopeInfo[1] = addScopeList;
+ createMessage.args[2] = scopeInfo;
+ createMessage.args[3] = xformPlanes.clone();
+
+ Object[] obj = new Object[3];
+ obj[0] = boundingLeaf;
+ obj[1] = (regionOfInfluence != null?regionOfInfluence.clone():null);
+ obj[2] = enables.clone();
+ createMessage.args[4] = obj;
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ }
+
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it removes itself to the list of model clip
+ */
+ void clearLive(SetLiveState s) {
+
+ super.clearLive(s);
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER;
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(mirrorModelClip, Targets.ENV_TARGETS);
+ }
+ // Remove this mirror light as users of the bounding leaf
+ if (mirrorModelClip.boundingLeaf != null)
+ mirrorModelClip.boundingLeaf.removeUser(mirrorModelClip);
+
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(mirrorModelClip);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(mirrorModelClip);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(mirrorModelClip, Targets.ENV_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+
+
+ if (scopes.size() > 0) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.MODELCLIP_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(CLEAR_MIRROR);
+ ArrayList removeScopeList = new ArrayList();
+ for (int i = 0; i < scopes.size(); i++) {
+ GroupRetained group = (GroupRetained)scopes.get(i);
+ tempKey.reset();
+ group.removeAllNodesForScopedModelClip(mirrorModelClip, removeScopeList, tempKey);
+ }
+ createMessage.args[2] = removeScopeList;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ // This is called on the parent object
+ void clearMirrorObject(Object[] args) {
+ Shape3DRetained shape;
+ ArrayList shapeList = (ArrayList)args[2];
+ ArrayList removeScopeList = new ArrayList();
+
+ for (int i = 0; i < shapeList.size(); i++) {
+ shape = ((GeometryAtom)shapeList.get(i)).source;
+ shape.removeModelClip(mirrorModelClip);
+ }
+
+ mirrorModelClip.isScoped = false;
+
+ }
+
+
+ // Clone the retained side only, internal use only
+ protected Object clone() {
+ ModelClipRetained mc = (ModelClipRetained)super.clone();
+
+ mc.planes = new Vector4d[6];
+ for (int i = 0; i < 6; i++) {
+ mc.planes[i] = new Vector4d(this.planes[i]);
+ mc.xformPlanes[i] = new Vector4d(this.xformPlanes[i]);
+ }
+
+ mc.enables = new boolean[6];
+ getEnables(mc.enables);
+
+ // Derive the enables flag
+ mc.enableFlag = (mc.enables[0] |
+ mc.enables[1] |
+ mc.enables[2] |
+ mc.enables[3] |
+ mc.enables[4] |
+ mc.enables[5] );
+
+ mc.inImmCtx = false;
+ mc.region = null;
+ mc.sgModelClip = null;
+ mc.mirrorModelClip = null;
+ mc.environmentSets = new UnorderList(1, EnvironmentSet.class);
+
+ if (regionOfInfluence != null) {
+ mc.regionOfInfluence = (Bounds) regionOfInfluence.clone();
+ }
+
+ return mc;
+ }
+
+
+ // Called on mirror object
+ void updateImmediateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (boundingLeaf == null) {
+ if (regionOfInfluence != null) {
+ region = regionOfInfluence.copy(region);
+ region.transform(regionOfInfluence,
+ sgModelClip.getCurrentLocalToVworld());
+ }
+
+ }
+ }
+
+
+ void printPlane(int index, String string)
+ {
+ System.err.println(string + " : < " + planes[index].toString()
+ + " > " + enables[index]);
+ }
+
+ void printPlanes(String string, Vector4d[] planes)
+ {
+ System.err.println(string);
+ printPlane(0, "[0]");
+ printPlane(1, "[1]");
+ printPlane(2, "[2]");
+ printPlane(3, "[3]");
+ printPlane(4, "[4]");
+ printPlane(5, "[5]");
+ }
+
+
+ void printEnables(String string, boolean[] enables)
+ {
+ System.err.println(string);
+ System.err.println("[0] : < " + enables[0] + " >");
+ System.err.println("[1] : < " + enables[1] + " >");
+ System.err.println("[2] : < " + enables[2] + " >");
+ System.err.println("[3] : < " + enables[3] + " >");
+ System.err.println("[4] : < " + enables[4] + " >");
+ System.err.println("[5] : < " + enables[5] + " >");
+ }
+
+ final void sendMessage(int attrMask, Object attr1, Object attr2) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.MODELCLIP_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr1;
+ createMessage.args[3] = attr2;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void mergeTransform(TransformGroupRetained staticTransform) {
+ super.mergeTransform(staticTransform);
+
+ if (regionOfInfluence != null) {
+ regionOfInfluence.transform(staticTransform.transform);
+ }
+
+ Transform3D xform = staticTransform.getNormalTransform();
+ for (int i = 0; i < 6; i++) {
+ xform.transform(planes[i], xformPlanes[i]);
+ }
+ }
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ leafList.add(mirrorModelClip);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Morph.java b/src/classes/share/javax/media/j3d/Morph.java
new file mode 100644
index 0000000..4661c85
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Morph.java
@@ -0,0 +1,654 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+import javax.vecmath.*;
+
+/**
+ * The Morph leaf node permits an application to morph between
+ * multiple GeometryArrays. The Morph node contains a single
+ * Appearance node, an array of GeometryArray objects, and an array of
+ * corresponding weights. The Morph node combines these GeometryArrays
+ * into an aggregate shape based on each GeometryArray's corresponding
+ * weight. Typically, Behavior nodes will modify the weights to
+ * achieve various morphing effects.
+ *
+ * <p>
+ * The following restrictions apply to each GeometryArray object
+ * in the specified array of GeometryArray objects:
+ *
+ * <ul>
+ * <li>
+ * All <i>N</i> geometry arrays must be of the
+ * same type (that is, the same subclass of GeometryArray).
+ * </li>
+ *
+ * <p>
+ * <li>
+ * The vertexFormat, texCoordSetCount, and validVertexCount must be
+ * the same for all <i>N</i> geometry arrays.
+ * </li>
+ *
+ * <p>
+ * <li>
+ * The texCoordSetMap array must be identical (element-by-element) for
+ * all <i>N</i> geometry arrays.
+ * </li>
+ *
+ * <p>
+ * <li>
+ * For IndexedGeometryArray objects, the validIndexCount must be the same
+ * for all <i>N</i> geometry arrays.
+ * </li>
+ *
+ * <p>
+ * <li>
+ * For GeometryStripArray objects, the stripVertexCounts array must
+ * be identical (element-by-element) for all <i>N</i> geometry arrays.
+ * </li>
+ *
+ * <p>
+ * <li>
+ * For IndexedGeometryStripArray objects, the stripIndexCounts array must
+ * be identical (element-by-element) for all <i>N</i> geometry arrays.
+ * </li>
+ *
+ * <p>
+ * <li>
+ * For indexed geometry by-reference, the array lengths of each
+ * enabled vertex component (coord, color, normal, texcoord)
+ * must be the same for all <i>N</i> geometry arrays.
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * For IndexedGeometryArray objects, the vertex arrays are morphed
+ * <i>before</i> the indexes are applied. Only the indexes in the
+ * first geometry array (geometry[0]) are used when rendering the
+ * geometry.
+ */
+
+public class Morph extends Leaf {
+
+ /**
+ * Specifies that the node allows read access to its geometry information.
+ */
+ public static final int
+ ALLOW_GEOMETRY_ARRAY_READ = CapabilityBits.MORPH_ALLOW_GEOMETRY_ARRAY_READ;
+
+ /**
+ * Specifies that the node allows write access to its geometry information.
+ */
+ public static final int
+ ALLOW_GEOMETRY_ARRAY_WRITE = CapabilityBits.MORPH_ALLOW_GEOMETRY_ARRAY_WRITE;
+
+ /**
+ * Specifies that the node allows read access to its appearance information.
+ */
+ public static final int
+ ALLOW_APPEARANCE_READ = CapabilityBits.MORPH_ALLOW_APPEARANCE_READ;
+
+ /**
+ * Specifies that the node allows write access to its appearance information.
+ */
+ public static final int
+ ALLOW_APPEARANCE_WRITE = CapabilityBits.MORPH_ALLOW_APPEARANCE_WRITE;
+
+ /**
+ * Specifies that the node allows read access to its morph
+ * weight vector.
+ */
+ public static final int
+ ALLOW_WEIGHTS_READ = CapabilityBits.MORPH_ALLOW_WEIGHTS_READ;
+
+ /**
+ * Specifies that the node allows write access to its morph
+ * weight vector.
+ */
+ public static final int
+ ALLOW_WEIGHTS_WRITE = CapabilityBits.MORPH_ALLOW_WEIGHTS_WRITE;
+
+ /**
+ * Specifies that the node allows reading its collision Bounds.
+ */
+ public static final int
+ ALLOW_COLLISION_BOUNDS_READ = CapabilityBits.MORPH_ALLOW_COLLISION_BOUNDS_READ;
+
+ /**
+ * Specifies the node allows writing its collision Bounds.
+ */
+ public static final int
+ ALLOW_COLLISION_BOUNDS_WRITE = CapabilityBits.MORPH_ALLOW_COLLISION_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this node allows reading its appearance override
+ * enable flag.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_APPEARANCE_OVERRIDE_READ =
+ CapabilityBits.MORPH_ALLOW_APPEARANCE_OVERRIDE_READ;
+
+ /**
+ * Specifies that this node allows writing its appearance override
+ * enable flag.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_APPEARANCE_OVERRIDE_WRITE =
+ CapabilityBits.MORPH_ALLOW_APPEARANCE_OVERRIDE_WRITE;
+
+
+ // non public default constructor
+ Morph() {
+ }
+
+ /**
+ * Constructs and initializes a Morph node with the specified array
+ * of GeometryArray objects. Default values are used for all other
+ * parameters as follows:
+ * <ul>
+ * appearance : null<br>
+ * weights : [1, 0, 0, 0, ...]<br>
+ * collision bounds : null<br>
+ * appearance override enable : false<br>
+ * </ul><P>
+ * A null appearance object specifies that default values are used
+ * for all appearance attributes.
+ *
+ * @param geometryArrays the geometry components of the morph;
+ * a null or zero-length array of GeometryArray objects is
+ * permitted, and specifies that no geometry is drawn. In this case,
+ * the array of weights is initialized to a zero-length array.
+ * @exception IllegalArgumentException if any of the specified
+ * geometry array objects differ from each other in any of the
+ * following ways:
+ * <ul>
+ * <li>Type of geometry array object (subclass of GeometryArray)</li>
+ * <li>vertexFormat</li>
+ * <li>texCoordSetCount</li>
+ * <li>texCoordSetMap</li>
+ * <li>validVertexCount</li>
+ * <li>validIndexCount, for IndexedGeometryArray objects</li>
+ * <li>stripVertexCounts array, for GeometryStripArray objects</li>
+ * <li>stripIndexCounts array, for IndexedGeometryStripArray objects</li>
+ * <li>the array lengths of each enabled vertex component
+ * (coord, color, normal, texcoord),
+ * for indexed geometry by-reference</li>
+ * </ul>
+ */
+ public Morph(GeometryArray geometryArrays[]) {
+ ((MorphRetained)retained).setGeometryArrays(geometryArrays);
+ }
+
+ /**
+ * Constructs and initializes a Morph node with the specified array
+ * of GeometryArray objects and the specified appearance object.
+ * @param geometryArrays the geometry components of the Morph node
+ * a null or zero-length array of GeometryArray objects is
+ * permitted, and specifies that no geometry is drawn. In this case,
+ * the array of weights is initialized to a zero-length array.
+ * @param appearance the appearance component of the Morph node
+ * @exception IllegalArgumentException if any of the specified
+ * geometry array objects differ from each other in any of the
+ * following ways:
+ * <ul>
+ * <li>Type of geometry array object (subclass of GeometryArray)</li>
+ * <li>vertexFormat</li>
+ * <li>texCoordSetCount</li>
+ * <li>texCoordSetMap</li>
+ * <li>validVertexCount</li>
+ * <li>validIndexCount, for IndexedGeometryArray objects</li>
+ * <li>stripVertexCounts array, for GeometryStripArray objects</li>
+ * <li>stripIndexCounts array, for IndexedGeometryStripArray objects</li>
+ * <li>the array lengths of each enabled vertex component
+ * (coord, color, normal, texcoord),
+ * for indexed geometry by-reference</li>
+ * </ul>
+ */
+ public Morph(GeometryArray geometryArrays[], Appearance appearance) {
+ ((MorphRetained)retained).setGeometryArrays(geometryArrays);
+ ((MorphRetained)this.retained).setAppearance(appearance);
+ }
+
+ /**
+ * Creates the retained mode MorphRetained object that this
+ * Morph object will point to.
+ */
+ void createRetained() {
+ retained = new MorphRetained();
+ retained.setSource(this);
+ }
+
+ /**
+ * Sets the collision bounds of a node.
+ * @param bounds the collision bounding object for a node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setCollisionBounds(Bounds bounds) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLLISION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph0"));
+
+ ((MorphRetained)this.retained).setCollisionBounds(bounds);
+ }
+
+ /**
+ * Returns the collision bounding object of this node.
+ * @return the node's collision bounding object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getCollisionBounds() {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLLISION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph1"));
+
+ return ((MorphRetained)this.retained).getCollisionBounds();
+ }
+
+
+ /**
+ * Sets the geometryArrays component of the Morph node.
+ *
+ * If the current array of GeometryArrays in this Morph object is
+ * non-null with a length greater than 0, the specified array of
+ * GeometryArrays must be the same length as the current array.
+ * If the current array of GeometryArrays in this Morph object is
+ * null or has a length of 0, and the specified array of
+ * GeometryArrays is non-null with a length greater than 0, the
+ * length of the incoming array defines the number of the geometry
+ * objects that will be morphed. In this case, the weights array
+ * is allocated to be of the same length as the geometry array;
+ * the first element (weights[0]) is initialized to 1.0 and all of
+ * the other weights are initialized to 0.0.
+ *
+ * @param geometryArrays the new geometryArrays component
+ * for the Morph node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * <p>
+ *
+ * @exception IllegalArgumentException if the length of the
+ * specified array of geometry arrays is not equal to the length
+ * of this Morph node's current array of geometry arrays (and the
+ * current array's length is non-zero), or if any of the specified
+ * geometry array objects differ from each other in any of the
+ * following ways:
+ * <ul>
+ * <li>Type of geometry array object (subclass of GeometryArray)</li>
+ * <li>vertexFormat</li>
+ * <li>texCoordSetCount</li>
+ * <li>texCoordSetMap</li>
+ * <li>validVertexCount</li>
+ * <li>validIndexCount, for IndexedGeometryArray objects</li>
+ * <li>stripVertexCounts array, for GeometryStripArray objects</li>
+ * <li>stripIndexCounts array, for IndexedGeometryStripArray objects</li>
+ * <li>the array lengths of each enabled vertex component
+ * (coord, color, normal, texcoord),
+ * for indexed geometry by-reference</li>
+ * </ul>
+ */
+ public void setGeometryArrays(GeometryArray geometryArrays[]) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_GEOMETRY_ARRAY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph2"));
+
+ ((MorphRetained)this.retained).setGeometryArrays(geometryArrays);
+ }
+
+ /**
+ * Retrieves the geometryArray component of this Morph node.
+ * @param index the index of GeometryArray to be returned
+ * @return the geometryArray component of this Morph node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public GeometryArray getGeometryArray(int index) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_GEOMETRY_ARRAY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph3"));
+
+ return ((MorphRetained)this.retained).getGeometryArray(index);
+ }
+
+ /**
+ * Sets the appearance component of this Morph node. A null
+ * appearance component specifies that default values are used for all
+ * appearance attributes.
+ * @param appearance the new appearance component for this Morph node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAppearance(Appearance appearance) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPEARANCE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph4"));
+
+ ((MorphRetained)this.retained).setAppearance(appearance);
+ }
+
+ /**
+ * Retrieves the appearance component of this morph node.
+ * @return the appearance component of this morph node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Appearance getAppearance() {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPEARANCE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph5"));
+
+ return ((MorphRetained)this.retained).getAppearance();
+ }
+
+
+ /**
+ * Checks whether the geometry in this morph node intersects with
+ * the specified pickShape.
+ *
+ * @param path the SceneGraphPath to this morph node
+ * @param pickShape the PickShape to be intersected
+ *
+ * @return true if the pick shape intersects this node; false
+ * otherwise.
+ *
+ * @exception IllegalArgumentException if pickShape is a PickPoint.
+ * Java 3D doesn't have spatial information of the surface.
+ * Use PickBounds with BoundingSphere and a small radius, instead.
+ *
+ * @exception CapabilityNotSetException if the Geometry.ALLOW_INTERSECT
+ * capability bit is not set in all of the Geometry objects
+ * referred to by this morph node.
+ */
+ public boolean intersect(SceneGraphPath path, PickShape pickShape) {
+ return intersect(path, pickShape, null);
+ }
+
+
+ /**
+ * Checks whether the geometry in this morph node intersects with
+ * the specified pickRay.
+ *
+ * @param path the SceneGraphPath to this morph node
+ * @param pickRay the PickRay to be intersected
+ * @param dist the closest distance of the intersection
+ *
+ * @return true if the pick shape intersects this node; false
+ * otherwise. If true, dist contains the closest distance of
+ * intersection.
+ *
+ * @exception CapabilityNotSetException if the Geometry.ALLOW_INTERSECT
+ * capability bit is not set in all of the Geometry objects
+ * referred to by this morph node.
+ */
+ public boolean intersect(SceneGraphPath path,
+ PickRay pickRay,
+ double[] dist) {
+
+ if (isLiveOrCompiled()) {
+ checkForAllowIntersect();
+ }
+
+ return ((MorphRetained)this.retained).intersect(path, pickRay, dist);
+ }
+
+
+ /**
+ * Checks whether the geometry in this morph node intersects with
+ * the specified pickShape.
+ *
+ * @param path the SceneGraphPath to this morph node
+ * @param pickShape the PickShape to be intersected
+ * @param dist the closest distance of the intersection
+ *
+ * @return true if the pick shape intersects this node; false
+ * otherwise. If true, dist contains the closest distance of
+ * intersection.
+ *
+ * @exception IllegalArgumentException if pickShape is a PickPoint.
+ * Java 3D doesn't have spatial information of the surface.
+ * Use PickBounds with BoundingSphere and a small radius, instead.
+ *
+ * @exception CapabilityNotSetException if the Geometry.ALLOW_INTERSECT
+ * capability bit is not set in all of the Geometry objects
+ * referred to by this morph node.
+ *
+ * @since Java 3D 1.3
+ */
+ public boolean intersect(SceneGraphPath path,
+ PickShape pickShape,
+ double[] dist) {
+
+ if (isLiveOrCompiled()) {
+ checkForAllowIntersect();
+ }
+
+ if (pickShape instanceof PickPoint) {
+ throw new IllegalArgumentException(J3dI18N.getString("Morph10"));
+ }
+
+ return ((MorphRetained)this.retained).intersect(path, pickShape, dist);
+ }
+
+
+ /**
+ * Sets this Morph node's morph weight vector. The Morph node "weights"
+ * the corresponding GeometryArray by the amount specified.
+ * The weights apply a morph weight vector component that creates
+ * the desired morphing effect.
+ * The length
+ * of the <code>weights</code> parameter must be equal to the length
+ * of the array with which this Morph node was created, otherwise
+ * an IllegalArgumentException is thown.
+ * @param weights the morph weight vector that the morph node will
+ * use in combining the node's geometryArrays. The morph node will "weight"
+ * the corresponding GeometryArray by the amount specified.
+ * N.B.: the sum of the weights should equal 1.0
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if sum of all 'weights' is
+ * NOT 1.0 or number of weights is NOT exqual to number of GeometryArrays.
+ */
+ public void setWeights(double weights[]) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_WEIGHTS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph8"));
+
+ ((MorphRetained)this.retained).setWeights(weights);
+ }
+
+ /**
+ * Retrieves the Morph node's morph weight vector.
+ * @return the morph weight vector component of this morph node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public double[] getWeights() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_WEIGHTS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph9"));
+
+ return ((MorphRetained)this.retained).getWeights();
+ }
+
+ /**
+ * Sets a flag that indicates whether this node's appearance can
+ * be overridden. If the flag is true, this node's
+ * appearance may be overridden by an AlternateAppearance leaf
+ * node, regardless of the value of the ALLOW_APPEARANCE_WRITE
+ * capability bit.
+ * The default value is false.
+ *
+ * @param flag the apperance override enable flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see AlternateAppearance
+ *
+ * @since Java 3D 1.2
+ */
+ public void setAppearanceOverrideEnable(boolean flag) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_APPEARANCE_OVERRIDE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph11"));
+
+ ((MorphRetained)this.retained).setAppearanceOverrideEnable(flag);
+ }
+
+ /**
+ * Retrieves the appearanceOverrideEnable flag for this node.
+ * @return true if the appearance can be overridden; false
+ * otherwise.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getAppearanceOverrideEnable() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_APPEARANCE_OVERRIDE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph12"));
+
+ return ((MorphRetained)this.retained).getAppearanceOverrideEnable();
+ }
+
+ /**
+ * Creates 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) {
+ Morph m = new Morph();
+ m.duplicateNode(this, forceDuplicate);
+ return m;
+ }
+
+ /**
+ * 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>Morph</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+
+ /**
+ * Copies all Morph 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ MorphRetained attr = (MorphRetained) originalNode.retained;
+ MorphRetained rt = (MorphRetained) retained;
+
+ Hashtable hashtable = originalNode.nodeHashtable;
+
+ double weights[] = attr.getWeights();
+
+ rt.setCollisionBounds(attr.getCollisionBounds());
+ rt.setAppearance((Appearance) getNodeComponent(
+ attr.getAppearance(),
+ forceDuplicate,
+ hashtable));
+
+ GeometryArray ga[] = new GeometryArray[weights.length];
+
+ for (int i=weights.length-1; i>=0; i--) {
+ ga[i] = (GeometryArray) getNodeComponent(
+ attr.getGeometryArray(i),
+ forceDuplicate,
+ hashtable);
+ }
+ rt.setGeometryArrays(ga);
+ rt.setWeights(weights);
+ }
+
+ // Method to check whether all geometries have allow intersect
+ // capability bit set; it will throw an exception if any don't
+ // have the bit set.
+ private void checkForAllowIntersect() {
+ MorphRetained morphR = ((MorphRetained)this.retained);
+ for (int i = 0; i < morphR.numGeometryArrays; i++) {
+ if (!morphR.geometryArrays[i].source.
+ getCapability(Geometry.ALLOW_INTERSECT)) {
+
+ throw new CapabilityNotSetException(J3dI18N.getString("Morph6"));
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/MorphRetained.java b/src/classes/share/javax/media/j3d/MorphRetained.java
new file mode 100644
index 0000000..ac3c0df
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MorphRetained.java
@@ -0,0 +1,1894 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+import java.util.Vector;
+
+/**
+ * A morph leaf node consisting of geometery and appearance properties.
+ */
+
+class MorphRetained extends LeafRetained implements GeometryUpdater {
+
+ // These bits should match the Shape3D bits ...(Since the mirrors for
+ // both are Shape3DRetained
+ static final int GEOMETRY_CHANGED = 0x00001;
+ static final int APPEARANCE_CHANGED = 0x00002;
+ static final int COLLISION_CHANGED = 0x00004;
+ static final int BOUNDS_CHANGED = 0x00008;
+ static final int APPEARANCEOVERRIDE_CHANGED = 0x00010;
+ static final int UPDATE_MORPH = 0x00020;
+
+ private static final double TOLERANCE = 1.0e-4;
+
+
+ /**
+ * The mirror Shape3DRetained nodes for this object. There is one
+ * mirror for each instance of this Shape3D node. If it is not in
+ * a SharedGroup, only index 0 is valid.
+ */
+ ArrayList mirrorShape3D = new ArrayList();
+
+
+ // Target threads to be notified when morph changes
+ final static int targetThreads = (J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_GEOMETRY);
+
+ /**
+ * The appearance component of the morph node.
+ */
+ AppearanceRetained appearance = null;
+
+ /**
+ * The Geosets associated with the morph node.
+ */
+ GeometryArrayRetained geometryArrays[];
+
+ int numGeometryArrays = 0;
+
+ /**
+ * The weight vector the morph node.
+ */
+ double weights[];
+
+ /**
+ * Reference to the BranchGroup path of this mirror shape
+ * This is used for picking only.
+ */
+ BranchGroupRetained branchGroupPath[];
+
+
+ // cache value for picking in mirror shape.
+ // True if all the node of the path from this to root are all pickable
+ boolean isPickable = true;
+
+
+ // cache value for collidable in mirror shape.
+ // True if all the node of the path from this to root are all collidable
+ boolean isCollidable = true;
+
+
+ // closest switch parent
+ SwitchRetained closestSwitchParent = null;
+
+ // the child index from the closest switch parent
+ int closestSwitchIndex = -1;
+
+ // Is this Morph visible ? The default is true.
+ boolean visible = true;
+
+ // geometry Bounds in local coordinate
+ Bounds bounds = null;
+
+ // geometry Bounds in virtual world coordinate
+ BoundingBox vwcBounds = new BoundingBox();
+
+ // collision Bound in local coordinate
+ Bounds collisionBound = null;
+
+ // collision Bounds in virtual world coordinate
+ Bounds collisionVwcBound = null;
+
+
+ GeometryArray morphedGeometryArray = null;
+
+ // Morph data
+ float[] Mcoord = null;
+ float[] Mcolor = null;
+ float[] Mnormal = null;
+ // First dimension is the coordSet, second dimenension is the vertex index
+ // each vertex has 2 or 3floats
+ float[][]MtexCoord = null;
+
+ // Whether the normal appearance is overrided by the alternate app
+ boolean appearanceOverrideEnable = false;
+
+ int changedFrequent = 0;
+
+
+ MorphRetained() {
+ this.nodeType = NodeRetained.MORPH;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ /**
+ * Sets the collision bounds of a node.
+ * @param bounds the bounding object for the node
+ */
+ void setCollisionBounds(Bounds bounds) {
+ if (bounds != null) {
+ collisionBound = (Bounds)bounds.clone();
+ } else {
+ collisionBound = null;
+ }
+ if (source.isLive()) {
+ // Notify Geometry Structure to set mirror shape collision
+ // bound and check for collision
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.COLLISION_BOUND_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM;
+ message.universe = universe;
+ message.args[1] = collisionBound;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ /**
+ * Sets the geometric bounds of a node.
+ * @param bounds the bounding object for the node
+ */
+ void setBounds(Bounds bounds) {
+ super.setBounds(bounds);
+ if (source.isLive() && !boundsAutoCompute) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.REGION_BOUND_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ targetThreads;
+ message.universe = universe;
+ message.args[0] = Shape3DRetained.getGeomAtomsArray(mirrorShape3D);
+ message.args[1] = localBounds;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ /**
+ * Gets the collision bounds of a node.
+ * @return the node's bounding object
+ */
+ Bounds getCollisionBounds() {
+ return (collisionBound == null? null : (Bounds)collisionBound.clone());
+ }
+
+ /**
+ * Sets the geometryArrays component of the Morph node.
+ * @param geometryArrays the new vector of geometryArrays for the morph node
+ */
+ void setGeometryArrays(GeometryArray geometryArrays[]) {
+ int i;
+
+ if ((geometryArrays == null || geometryArrays.length == 0) && numGeometryArrays == 0)
+ return;
+
+ GeometryArrayRetained geo, prevGeo;
+
+ if (numGeometryArrays != 0 && (geometryArrays == null || numGeometryArrays != geometryArrays.length))
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained0"));
+
+
+ for (i=1;i < geometryArrays.length;i++) {
+ if (geometryArrays[i] == null || geometryArrays[i-1] == null)
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ geo = (GeometryArrayRetained)geometryArrays[i].retained;
+ prevGeo = (GeometryArrayRetained)geometryArrays[i-1].retained;
+ if (prevGeo == null || geo == null) {
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+
+ }
+ doErrorCheck(prevGeo, geo);
+ }
+ // Check if the first one is in Immediate context
+ if (geometryArrays[0] != null) {
+ geo = (GeometryArrayRetained)geometryArrays[0].retained;
+ }
+
+ if (numGeometryArrays == 0) {
+ this.geometryArrays = new GeometryArrayRetained[geometryArrays.length];
+ numGeometryArrays = geometryArrays.length;
+ }
+
+ for (i=0;i < numGeometryArrays;i++) {
+ geo = (GeometryArrayRetained)geometryArrays[i].retained;
+ if (((Morph)this.source).isLive()) {
+ if (this.geometryArrays[i] != null) {
+ this.geometryArrays[i].clearLive(refCount);
+ this.geometryArrays[i].removeMorphUser(this);
+ }
+ if (geo != null) {
+ geo.setLive(inBackgroundGroup, refCount);
+ geo.addMorphUser(this);
+ }
+ }
+
+ this.geometryArrays[i] = geo;
+ }
+ if (this.geometryArrays[0] == null)
+ return;
+
+
+ if (weights == null) {
+ weights = new double[numGeometryArrays];
+ weights[0] = 1.0;
+ int vFormat = this.geometryArrays[0].vertexFormat;
+ // default is zero when new array
+ //for (i=1; i < numGeometryArrays;i++) weights[i] = 0.0;
+
+ int texCoordSetCount = this.geometryArrays[0].getTexCoordSetCount();
+ if (this.geometryArrays[0] instanceof IndexedGeometryArrayRetained) {
+ Mcoord = new float[this.geometryArrays[0].getNumCoordCount()* 3];
+
+ if ((vFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3)
+ Mcolor = new float[this.geometryArrays[0].getNumColorCount()* 3];
+ else if ((vFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4)
+ Mcolor = new float[this.geometryArrays[0].getNumColorCount()* 4];
+
+ MtexCoord = new float[texCoordSetCount][];
+ if ((vFormat & GeometryArray.NORMALS) != 0)
+ Mnormal = new float[this.geometryArrays[0].getNumNormalCount() *3];
+ for (int k = 0; k < texCoordSetCount; k++) {
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0)
+ MtexCoord[k] = new float[this.geometryArrays[0].getNumTexCoordCount(k) * 2];
+ else if (((vFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0))
+ MtexCoord[k] = new float[this.geometryArrays[0].getNumTexCoordCount(k) * 3];
+ else if (((vFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0))
+ MtexCoord[k] = new float[this.geometryArrays[0].getNumTexCoordCount(k) * 4];
+ }
+ }
+ else {
+ Mcoord = new float[this.geometryArrays[0].validVertexCount* 3];
+
+ if ((vFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
+ Mcolor = new float[this.geometryArrays[0].validVertexCount* 3];
+ } else if ((vFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ Mcolor = new float[this.geometryArrays[0].validVertexCount* 4];
+ }
+ MtexCoord = new float[texCoordSetCount][];
+ if ((vFormat & GeometryArray.NORMALS) != 0) {
+ Mnormal = new float[this.geometryArrays[0].validVertexCount *3];
+ }
+ for (int k = 0; k < texCoordSetCount; k++) {
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0)
+ MtexCoord[k] = new float[this.geometryArrays[0].validVertexCount * 2];
+ else if (((vFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0))
+ MtexCoord[k] = new float[this.geometryArrays[0].validVertexCount * 3];
+ else if (((vFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0))
+ MtexCoord[k] = new float[this.geometryArrays[0].validVertexCount * 4];
+ }
+ }
+ }
+
+ // create a new morphedGeometryArray
+ initMorphedGeometry();
+
+ if (source.isLive()) {
+
+ Shape3DRetained shape = (Shape3DRetained)mirrorShape3D.get(0);
+
+ shape.setMorphGeometry(morphedGeometryArray, mirrorShape3D);
+
+ J3dMessage mChangeMessage = null;
+ mChangeMessage = VirtualUniverse.mc.getMessage();
+ mChangeMessage.type = J3dMessage.MORPH_CHANGED;
+ mChangeMessage.threads = (J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_TRANSFORM);
+ // If its a indexed geometry array, unindexify in renderBin
+ if (this.geometryArrays[0] instanceof IndexedGeometryArrayRetained)
+ mChangeMessage.threads |= J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ mChangeMessage.args[0] = this;
+ mChangeMessage.args[1]= new Integer(GEOMETRY_CHANGED);
+ // a shadow copy of this ArrayList instance. (The elements themselves are not copied.)
+ mChangeMessage.args[3] = Shape3DRetained.getGeomAtomsArray(mirrorShape3D);
+ mChangeMessage.universe = universe;
+ VirtualUniverse.mc.processMessage(mChangeMessage);
+
+ if (boundsAutoCompute) {
+ GeometryArrayRetained mga = (GeometryArrayRetained)morphedGeometryArray.retained;
+ // Compute the bounds once
+ mga.incrComputeGeoBounds();// This compute the bbox if dirty
+ mga.decrComputeGeoBounds();
+ }
+ }
+
+
+
+ }
+
+ /**
+ * Retrieves the geometryArrays component of this Morph node.
+ * @param index the index of GeometryArray to be returned
+ * @return the geometryArray component of this morph node
+ */
+ GeometryArray getGeometryArray(int index) {
+ return (GeometryArray)this.geometryArrays[index].source;
+ }
+
+ /**
+ * Sets the appearance component of this Morph node.
+ * @param appearance the new apearance component for this morph node
+ */
+ void setAppearance(Appearance newAppearance) {
+ boolean visibleIsDirty = false;
+
+ if (((Morph)this.source).isLive()) {
+
+ if (appearance != null) {
+ this.appearance.clearLive(refCount);
+ for (int i=mirrorShape3D.size()-1; i>=0; i--) {
+ this.appearance.removeAMirrorUser(
+ (Shape3DRetained)mirrorShape3D.get(i));
+ }
+ }
+
+ if (newAppearance != null) {
+ ((AppearanceRetained)newAppearance.retained).setLive(inBackgroundGroup, refCount);
+ appearance = ((AppearanceRetained)newAppearance.retained);
+ int size= mirrorShape3D.size();
+ for (int i=0; i<size; i++) {
+ appearance.addAMirrorUser((Shape3DRetained)mirrorShape3D.get(i));
+ }
+ if((appearance.renderingAttributes != null) &&
+ (visible != appearance.renderingAttributes.visible)) {
+ visible = appearance.renderingAttributes.visible;
+ visibleIsDirty = true;
+ }
+ }
+ else {
+ if(visible == false) {
+ visible = true;
+ visibleIsDirty = true;
+ }
+ }
+
+ // Send a message
+ int size = 0;
+
+ if (visibleIsDirty)
+ size = 2;
+ else
+ size = 1;
+ J3dMessage[] createMessage = new J3dMessage[size];
+ createMessage[0] = VirtualUniverse.mc.getMessage();
+ createMessage[0].threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+ createMessage[0].type = J3dMessage.MORPH_CHANGED;
+ createMessage[0].universe = universe;
+ createMessage[0].args[0] = this;
+ createMessage[0].args[1]= new Integer(APPEARANCE_CHANGED);
+ Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()];
+ mirrorShape3D.toArray(s3dArr);
+ createMessage[0].args[2] = s3dArr;
+ Object[] obj = new Object[2];
+ if (newAppearance == null) {
+ obj[0] = null;
+ }
+ else {
+ obj[0] = appearance.mirror;
+ }
+ obj[1] = new Integer(changedFrequent);
+ createMessage[0].args[3] = obj;
+ createMessage[0].args[4] = Shape3DRetained.getGeomAtomsArray(mirrorShape3D);
+ if(visibleIsDirty) {
+ createMessage[1] = VirtualUniverse.mc.getMessage();
+ createMessage[1].threads = J3dThread.UPDATE_GEOMETRY;
+ createMessage[1].type = J3dMessage.SHAPE3D_CHANGED;
+ createMessage[1].universe = universe;
+ createMessage[1].args[0] = this;
+ createMessage[1].args[1]= new Integer(APPEARANCE_CHANGED);
+ createMessage[1].args[2]= visible?Boolean.TRUE:Boolean.FALSE;
+ createMessage[1].args[3]= createMessage[0].args[4];
+ }
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ else {
+ if (newAppearance == null) {
+ appearance = null;
+ } else {
+ appearance = (AppearanceRetained) newAppearance.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the morph node's appearance component.
+ * @return the morph node's appearance
+ */
+ Appearance getAppearance() {
+ return (appearance == null ? null :
+ (Appearance) this.appearance.source);
+ }
+
+ void setAppearanceOverrideEnable(boolean flag) {
+ if (((Morph)this.source).isLive()) {
+
+ // Send a message
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;;
+ createMessage.type = J3dMessage.MORPH_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(APPEARANCEOVERRIDE_CHANGED);
+ Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()];
+ mirrorShape3D.toArray(s3dArr);
+ createMessage.args[2] = s3dArr;
+ Object[] obj = new Object[2];
+ if (flag) {
+ obj[0] = Boolean.TRUE;
+ }
+ else {
+ obj[0] = Boolean.FALSE;
+ }
+ obj[1] = new Integer(changedFrequent);
+ createMessage.args[3] = obj;
+ createMessage.args[4] = Shape3DRetained.getGeomAtomsArray(mirrorShape3D);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ appearanceOverrideEnable = flag;
+ }
+
+ boolean getAppearanceOverrideEnable() {
+ return appearanceOverrideEnable;
+ }
+
+
+ /**
+ * Check if the geometry component of this shape node under path
+ * intersects with the pickShape.
+ * @return true if intersected else false. If return is true, dist
+ * contains the closest distance of intersection if it is not
+ * equal to null.
+ */
+ boolean intersect(SceneGraphPath path, PickShape pickShape,
+ double[] dist) {
+
+ // This method will not do bound intersect check, as it assume caller
+ // has already done that. ( For performance and code simplification
+ // reasons. )
+
+ Transform3D localToVworld = path.getTransform();
+
+ if (localToVworld == null) {
+ throw new RuntimeException(J3dI18N.getString("MorphRetained5"));
+ }
+
+ Transform3D vworldToLocal = VirtualUniverse.mc.getTransform3D(null);
+ vworldToLocal.invert(localToVworld);
+ PickShape newPS = pickShape.transform(vworldToLocal);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, vworldToLocal);
+
+ Point3d iPnt = Shape3DRetained.getPoint3d();
+
+ GeometryRetained geo = (GeometryRetained) (morphedGeometryArray.retained);
+
+ if (geo.mirrorGeometry != null) {
+ geo = geo.mirrorGeometry;
+ }
+
+ boolean isIntersect;
+ if (dist != null) {
+ isIntersect = geo.intersect(newPS, dist, iPnt);
+ if (isIntersect) {
+ // compute the real distance since the dist return
+ // above distance may scaled (non-uniform) by transform3d
+ localToVworld.transform(iPnt);
+ dist[0] = pickShape.distance(iPnt);
+ }
+ } else {
+ isIntersect = geo.intersect(newPS, null, iPnt);
+ }
+ Shape3DRetained.freePoint3d(iPnt);
+ return isIntersect;
+ }
+
+ /**
+ * Sets the Morph node's weight vector
+ * @param wieghts the new vector of weights for the morph node
+ */
+ void setWeights(double weights[]) {
+ int i;
+ double sum= 0.0;
+
+ if (weights.length != numGeometryArrays)
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained7"));
+
+ for (i=weights.length-1; i>=0; i--) {
+ sum += weights[i];
+ }
+
+ if (Math.abs(sum - 1.0) > TOLERANCE)
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained8"));
+
+ // Weights array is ALWAYS malloced in setGeometryArrays method
+ for (i=numGeometryArrays-1; i>=0; i--)
+ this.weights[i] = weights[i];
+
+
+ if (source.isLive()) {
+ ((GeometryArrayRetained)morphedGeometryArray.retained).updateData(this);
+ J3dMessage mChangeMessage = null;
+ mChangeMessage = VirtualUniverse.mc.getMessage();
+ mChangeMessage.type = J3dMessage.MORPH_CHANGED;
+ mChangeMessage.threads = (J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_TRANSFORM);
+ // If its a indexed geometry array, unindexify in renderBin
+ if (this.geometryArrays[0] instanceof IndexedGeometryArrayRetained)
+ mChangeMessage.threads |= J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ mChangeMessage.args[0] = this;
+ mChangeMessage.args[1]= new Integer(GEOMETRY_CHANGED);
+ mChangeMessage.args[3] = Shape3DRetained.getGeomAtomsArray(mirrorShape3D);
+ mChangeMessage.universe = universe;
+ VirtualUniverse.mc.processMessage(mChangeMessage);
+ }
+
+ }
+
+ /**
+ * Retrieves the Morph node's weight vector
+ * @return the morph node's weight vector.
+ */
+ double[] getWeights() {
+ return (double[]) weights.clone();
+ }
+
+ /**
+ * Gets the bounding object of a node.
+ * @return the node's bounding object
+ */
+ Bounds getBounds() {
+ if(boundsAutoCompute) {
+ GeometryArrayRetained mga =
+ (GeometryArrayRetained)morphedGeometryArray.retained;
+ if (mga != null) {
+ synchronized(mga.geoBounds) {
+ return (Bounds) mga.geoBounds.clone();
+ }
+ } else {
+ return null;
+ }
+ } else {
+ return super.getBounds();
+ }
+ }
+
+ Bounds getEffectiveBounds() {
+ if(boundsAutoCompute) {
+ return getBounds();
+ }
+ else {
+ return super.getEffectiveBounds();
+ }
+ }
+
+ /**
+ * ONLY needed for SHAPE, MORPH, and LINK node type.
+ * Compute the combine bounds of bounds and its localBounds.
+ */
+ void computeCombineBounds(Bounds bounds) {
+
+ if(boundsAutoCompute) {
+ GeometryArrayRetained mga =
+ (GeometryArrayRetained)morphedGeometryArray.retained;
+ if (mga != null) {
+ synchronized(mga.geoBounds) {
+ bounds.combine((Bounds) mga.geoBounds);
+ }
+ }
+ } else {
+ // Should this be lock too ? ( MT safe ? )
+ synchronized(localBounds) {
+ bounds.combine((Bounds) localBounds);
+ }
+ }
+ }
+
+ // If the geometry of a morph changes, make sure that the
+ // validVertexCount has not changed
+ void updateMorphedGeometryArray(GeometryArrayRetained geo, boolean coordinatesChanged) {
+ if (numGeometryArrays > 0) {
+ // check if not the first geo, then compare with the first geometry
+ if (geometryArrays[0] != geo) {
+ doErrorCheck(geo, geometryArrays[0]);
+ }
+ else {
+ // if first geo, compare with the second geo
+ if (numGeometryArrays > 1) {
+ doErrorCheck(geo, geometryArrays[1]);
+ }
+
+ }
+ }
+
+
+ ((GeometryArrayRetained)morphedGeometryArray.retained).updateData(this);
+ // Compute the bounds once
+ if (boundsAutoCompute && coordinatesChanged) {
+ GeometryArrayRetained mga = (GeometryArrayRetained)morphedGeometryArray.retained;
+ mga.incrComputeGeoBounds(); // This compute the bbox if dirty
+ mga.decrComputeGeoBounds();
+ }
+ }
+
+ /**
+ * Update GeometryArray computed by morphing input GeometryArrays
+ * with weights
+ */
+ public void updateData(Geometry mga) {
+
+ int i,j,k, vFormat, geoType, stripVCount[];
+ int iCount = 0;
+ int numStrips = 0;
+ int texCoordSetCount = 0;
+ float coord[] = new float[3], color[] = new float[4],
+ normal[] = new float[3], texCoord[] = new float[3];
+
+ vFormat = geometryArrays[0].vertexFormat;
+ geoType = ((GeometryArrayRetained)geometryArrays[0]).geoType;
+ texCoordSetCount = geometryArrays[0].getTexCoordSetCount();
+
+
+
+ int vc = 0, nc = 0, cc = 0, n = 0;
+ int count = 0;
+ if (geometryArrays[0] instanceof IndexedGeometryArrayRetained){
+ count = geometryArrays[0].getNumCoordCount();
+ } else {
+ count = geometryArrays[0].validVertexCount;
+ }
+
+ for (i=0; i < count; i++) {
+ Mcoord[vc++] = Mcoord[vc++] = Mcoord[vc++] = 0.0f;
+ }
+
+
+ if ((vFormat & GeometryArray.COLOR) != 0) {
+ if (geometryArrays[0] instanceof IndexedGeometryArrayRetained){
+ count = geometryArrays[0].getNumColorCount();
+ } else {
+ count = geometryArrays[0].validVertexCount;
+ }
+ for (i=0; i < count; i++) {
+ if ((vFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3)
+ Mcolor[cc++] = Mcolor[cc++] = Mcolor[cc++] = 0.0f;
+
+ else if ((vFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4)
+ Mcolor[cc++] = Mcolor[cc++] = Mcolor[cc++] = Mcolor[cc++] = 0.0f;
+ }
+ }
+
+
+ if ((vFormat & GeometryArray.NORMALS) != 0) {
+ if (geometryArrays[0] instanceof IndexedGeometryArrayRetained){
+ count = geometryArrays[0].getNumNormalCount();
+ } else {
+ count = geometryArrays[0].validVertexCount;
+ }
+ for (i=0; i < count; i++) {
+ Mnormal[nc++] = Mnormal[nc++] = Mnormal[nc++] = 0.0f;
+ }
+ }
+
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (k = 0; k < texCoordSetCount; k++) {
+ if (geometryArrays[0] instanceof IndexedGeometryArrayRetained){
+ count = geometryArrays[0].getNumTexCoordCount(k);
+ } else {
+ count = geometryArrays[0].validVertexCount;
+ }
+ int tcount = 0;
+ for (i=0; i < count; i++) {
+ MtexCoord[k][tcount++] = MtexCoord[k][tcount++] = 0.0f;
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ MtexCoord[k][tcount++] = 0.0f;
+ } else if ((vFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ MtexCoord[k][tcount++] = 0.0f;
+ MtexCoord[k][tcount++] = 0.0f;
+ }
+ }
+ }
+ }
+ // If by copy, then ...
+ if ((vFormat & GeometryArray.BY_REFERENCE) == 0) {
+ count = 0;
+ for (j=0;j < numGeometryArrays;j++) {
+ double w = weights[j];
+ if (w != 0) {
+ vc = 0; nc = 0; cc = 0;
+ int initialVertex = 0;
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ initialVertex = 0;
+ count = geometryArrays[j].getNumCoordCount();
+ }
+ else {
+ initialVertex = geometryArrays[j].getInitialVertexIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+ int endVertex = initialVertex + count;
+ for (i=initialVertex; i< endVertex; i++) {
+ geometryArrays[j].getCoordinate(i, coord);
+ Mcoord[vc++] += coord[0]*w;
+ Mcoord[vc++] += coord[1]*w;
+ Mcoord[vc++] += coord[2]*w;
+ }
+
+ if ((vFormat & GeometryArray.COLOR) != 0) {
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ count = geometryArrays[j].getNumColorCount();
+ }
+ endVertex = initialVertex + count;
+ for (i=initialVertex; i< endVertex; i++) {
+ geometryArrays[j].getColor(i, color);
+ Mcolor[cc++] += color[0]*w;
+ Mcolor[cc++] += color[1]*w;
+ Mcolor[cc++] += color[2]*w;
+ if ((vFormat & GeometryArray.WITH_ALPHA) != 0)
+ Mcolor[cc++] += color[3]*w;
+ }
+ }
+ if ((vFormat & GeometryArray.NORMALS) != 0) {
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ count = geometryArrays[j].getNumNormalCount();
+ }
+ endVertex = initialVertex + count;
+ for (i=initialVertex; i< endVertex; i++) {
+ geometryArrays[j].getNormal(i, normal);
+ Mnormal[nc++] += normal[0]*w;
+ Mnormal[nc++] += normal[1]*w;
+ Mnormal[nc++] += normal[2]*w;
+ }
+ }
+
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (k = 0; k < texCoordSetCount; k++) {
+ int tcount = 0;
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ count = geometryArrays[j].getNumTexCoordCount(i);
+ }
+ endVertex = initialVertex + count;
+ for (i=initialVertex; i< endVertex; i++) {
+ geometryArrays[j].getTextureCoordinate(k, i, texCoord);
+ MtexCoord[k][tcount++] += texCoord[0]*w;
+ MtexCoord[k][tcount++] += texCoord[1]*w;
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ MtexCoord[k][tcount++] += texCoord[2]*w;
+ } else if ((vFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ MtexCoord[k][tcount++] += texCoord[2]*w;
+ MtexCoord[k][tcount++] += texCoord[3]*w;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ int vIndex, tIndex, cIndex, nIndex, tstride = 0, cstride = 0;
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ tstride = 2;
+ } else if ((vFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ tstride = 3;
+ } else {
+ tstride = 4;
+ }
+ }
+
+ if ((vFormat & GeometryArray.COLOR) != 0) {
+ cstride = 3;
+ if ((vFormat & GeometryArray.WITH_ALPHA) != 0)
+ cstride = 4;
+ }
+
+ if ((vFormat & GeometryArray.INTERLEAVED) != 0) {
+ float[] vdata;
+ int stride;
+
+ stride = geometryArrays[0].stride();
+ int coffset = geometryArrays[0].colorOffset();
+ int noffset = geometryArrays[0].normalOffset();
+ int voffset = geometryArrays[0].coordinateOffset();
+ int offset = 0;
+
+ int initialVertex = 0;
+ for (j=0;j < numGeometryArrays;j++) {
+ double w = weights[j];
+ if (w != 0) {
+ vc = 0; nc = 0; cc = 0; n = 0;
+ vdata = geometryArrays[j].getInterleavedVertices();
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (k = 0; k < texCoordSetCount; k++) {
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ tIndex = 0;
+ count = geometryArrays[j].getNumCoordCount();
+ }
+ else {
+ tIndex = geometryArrays[j].getInitialVertexIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+ offset = (tIndex * stride)+k*tstride;
+ int tcount = 0;
+ for (i = 0; i < count; i++, offset += stride) {
+ MtexCoord[k][tcount++] += vdata[offset] * w;
+ MtexCoord[k][tcount++] += vdata[offset+1] * w;
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ MtexCoord[k][tcount++] += vdata[offset+2]*w;
+ } else if ((vFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ MtexCoord[k][tcount++] += vdata[offset+2]*w;
+ MtexCoord[k][tcount++] += vdata[offset+3]*w;
+ }
+ }
+ }
+
+ }
+ if ((vFormat & GeometryArray.COLOR) != 0) {
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ cIndex = 0;
+ count = geometryArrays[j].getNumCoordCount();
+ }
+ else {
+ cIndex = geometryArrays[j].getInitialVertexIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+ offset = (cIndex * stride)+coffset;
+ for (i = 0; i < count; i++, offset += stride) {
+ Mcolor[cc++] += vdata[offset]*w;
+ Mcolor[cc++] += vdata[offset+1]*w;
+ Mcolor[cc++] += vdata[offset+2]*w;
+ if ((vFormat & GeometryArray.WITH_ALPHA)!= 0)
+ Mcolor[cc++] += vdata[offset+3]*w;
+
+ }
+ }
+
+ if ((vFormat & GeometryArray.NORMALS) != 0) {
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ nIndex = 0;
+ count = geometryArrays[j].getNumCoordCount();
+ }
+ else {
+ nIndex = geometryArrays[j].getInitialVertexIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+ offset = (nIndex * stride)+noffset;
+ for (i = 0; i < count; i++, offset += stride) {
+ Mnormal[nc++] += vdata[offset]*w;
+ Mnormal[nc++] += vdata[offset+1]*w;
+ Mnormal[nc++] += vdata[offset+2]*w;
+ }
+ }
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ vIndex = 0;
+ count = geometryArrays[j].getNumCoordCount();
+ }
+ else {
+ vIndex = geometryArrays[j].getInitialVertexIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+ offset = (vIndex * stride)+voffset;
+ for (i = 0; i < count; i++, offset += stride) {
+ Mcoord[vc++] += vdata[offset]*w;
+ Mcoord[vc++] += vdata[offset+1]*w;
+ Mcoord[vc++] += vdata[offset+2]*w;
+
+ }
+ }
+ }
+ }
+ else {
+ float byteToFloatScale = 1.0f/255.0f;
+ for (j=0;j < numGeometryArrays;j++) {
+ double w = weights[j];
+ if (w != 0) {
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ switch ((geometryArrays[j].vertexType & GeometryArrayRetained.TEXCOORD_DEFINED)) {
+ case GeometryArrayRetained.TF:
+ for (k = 0; k < texCoordSetCount; k++) {
+ float[] tf = geometryArrays[j].getTexCoordRefFloat(k);
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ tIndex = 0;
+ count = geometryArrays[j].getNumTexCoordCount(k);
+ }
+ else {
+ tIndex = geometryArrays[j].getInitialTexCoordIndex(k);
+ count = geometryArrays[j].validVertexCount;
+ }
+ tIndex *= tstride;
+ int tcount = 0;
+ for (i=0; i< count; i++) {
+ MtexCoord[k][tcount++] += tf[tIndex++]*w;
+ MtexCoord[k][tcount++] += tf[tIndex++]*w;
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0)
+ MtexCoord[k][tcount++] += tf[tIndex++]*w;
+ }
+ }
+ break;
+ case GeometryArrayRetained.T2F:
+ for (k = 0; k < texCoordSetCount; k++) {
+ int tcount = 0;
+ float[] tf = geometryArrays[j].getTexCoordRefFloat(k);
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ tIndex = 0;
+ count = geometryArrays[j].getNumTexCoordCount(k);
+ }
+ else {
+ tIndex = geometryArrays[j].getInitialTexCoordIndex(k);
+ count = geometryArrays[j].validVertexCount;
+ }
+ TexCoord2f[] t2f = geometryArrays[j].getTexCoordRef2f(k);
+ for (i=0; i< count; i++, tIndex++) {
+ MtexCoord[k][tcount++] += t2f[tIndex].x*w;
+ MtexCoord[k][tcount++] += t2f[tIndex].y*w;
+ }
+ }
+ break;
+ case GeometryArrayRetained.T3F:
+ for (k = 0; k < texCoordSetCount; k++) {
+ int tcount = 0;
+ TexCoord3f[] t3f = geometryArrays[j].getTexCoordRef3f(k);
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ tIndex = 0;
+ count = geometryArrays[j].getNumTexCoordCount(k);
+ }
+ else {
+ tIndex = geometryArrays[j].getInitialTexCoordIndex(k);
+ count = geometryArrays[j].validVertexCount;
+ }
+ for (i=0; i< count; i++, tIndex++) {
+ MtexCoord[k][tcount++] += t3f[tIndex].x*w;
+ MtexCoord[k][tcount++] += t3f[tIndex].y*w;
+ MtexCoord[k][tcount++] += t3f[tIndex].z*w;
+ }
+ }
+ break;
+
+ }
+ }
+ if ((vFormat & GeometryArray.COLOR) != 0) {
+ double val = byteToFloatScale * w;
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ cIndex = 0;
+ count = geometryArrays[j].getNumColorCount();
+ }
+ else {
+ cIndex = geometryArrays[j].getInitialColorIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+
+ switch ((geometryArrays[j].vertexType & GeometryArrayRetained.COLOR_DEFINED)) {
+ case GeometryArrayRetained.CF:
+ float[] cf = geometryArrays[j].getColorRefFloat();
+ cc = 0;
+ cIndex *= cstride;
+ for (i=0; i< count; i++) {
+ Mcolor[cc++] += cf[cIndex++]*w;
+ Mcolor[cc++] += cf[cIndex++]*w;
+ Mcolor[cc++] += cf[cIndex++]*w;
+ if ((vFormat & GeometryArray.WITH_ALPHA)!= 0)
+ Mcolor[cc++] += cf[cIndex++]*w;
+ }
+ break;
+ case GeometryArrayRetained.CUB:
+ byte[] cub = geometryArrays[j].getColorRefByte();
+ cc = 0;
+ cIndex *= cstride;
+ for (i=0; i< count; i++) {
+ Mcolor[cc++] += (cub[cIndex++] & 0xff) * val;
+ Mcolor[cc++] += (cub[cIndex++] & 0xff) *val;
+ Mcolor[cc++] += (cub[cIndex++] & 0xff) *val;
+ if ((vFormat & GeometryArray.WITH_ALPHA)!= 0)
+ Mcolor[cc++] += (cub[cIndex++] & 0xff) *val;
+ }
+
+ break;
+ case GeometryArrayRetained.C3F:
+ Color3f[] c3f = geometryArrays[j].getColorRef3f();
+ cc = 0;
+ for (i=0; i< count; i++, cIndex++) {
+ Mcolor[cc++] += c3f[cIndex].x * w;
+ Mcolor[cc++] += c3f[cIndex].y * w;
+ Mcolor[cc++] += c3f[cIndex].z * w;
+ }
+ break;
+ case GeometryArrayRetained.C4F:
+ Color4f[] c4f = geometryArrays[j].getColorRef4f();
+ cc = 0;
+ for (i=0; i< count; i++, cIndex++) {
+ Mcolor[cc++] += c4f[cIndex].x * w;
+ Mcolor[cc++] += c4f[cIndex].y * w;
+ Mcolor[cc++] += c4f[cIndex].z * w;
+ Mcolor[cc++] += c4f[cIndex].w * w;
+ }
+ break;
+ case GeometryArrayRetained.C3UB:
+ Color3b[] c3b = geometryArrays[j].getColorRef3b();
+ cc = 0;
+ for (i=0; i< count; i++, cIndex++) {
+ Mcolor[cc++] += (c3b[cIndex].x & 0xff)* val;
+ Mcolor[cc++] += (c3b[cIndex].y & 0xff) * val;
+ Mcolor[cc++] += (c3b[cIndex].z & 0xff) * val;
+ }
+ break;
+ case GeometryArrayRetained.C4UB:
+ Color4b[] c4b = geometryArrays[j].getColorRef4b();
+ cc = 0;
+ for (i=0; i< count; i++, cIndex++) {
+ Mcolor[cc++] += (c4b[cIndex].x & 0xff)* val;
+ Mcolor[cc++] += (c4b[cIndex].y & 0xff) * val;
+ Mcolor[cc++] += (c4b[cIndex].z & 0xff) * val;
+ Mcolor[cc++] += (c4b[cIndex].w & 0xff) * val;
+ }
+ break;
+
+ }
+ }
+ if ((vFormat & GeometryArray.NORMALS) != 0) {
+ nc = 0;
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ nIndex = 0;
+ count = geometryArrays[j].getNumNormalCount();
+ }
+ else {
+ nIndex = geometryArrays[j].getInitialNormalIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+ switch ((geometryArrays[j].vertexType & GeometryArrayRetained.NORMAL_DEFINED)) {
+ case GeometryArrayRetained.NF:
+ float[] nf = geometryArrays[j].getNormalRefFloat();
+ nIndex *= 3;
+ for (i=0; i< count; i++) {
+ Mnormal[nc++] += nf[nIndex++]*w;
+ Mnormal[nc++] += nf[nIndex++]*w;
+ Mnormal[nc++] += nf[nIndex++]*w;
+ }
+ break;
+ case GeometryArrayRetained.N3F:
+ Vector3f[] n3f = geometryArrays[j].getNormalRef3f();
+ for (i=0; i< count; i++, nIndex++) {
+ Mnormal[nc++] += n3f[nIndex].x*w;
+ Mnormal[nc++] += n3f[nIndex].y*w;
+ Mnormal[nc++] += n3f[nIndex].z*w;
+ }
+ break;
+ }
+ }
+ // Handle vertices ..
+ vc = 0;
+ if (geometryArrays[j] instanceof IndexedGeometryArrayRetained) {
+ vIndex = 0;
+ count = geometryArrays[j].getNumCoordCount();
+ }
+ else {
+ vIndex = geometryArrays[j].getInitialCoordIndex();
+ count = geometryArrays[j].validVertexCount;
+ }
+ switch ((geometryArrays[j].vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
+ case GeometryArrayRetained.PF:
+ float[] pf = geometryArrays[j].getCoordRefFloat();
+ vIndex *= 3;
+ for (i=0; i< count; i++) {
+ Mcoord[vc++] += pf[vIndex++]*w;
+ Mcoord[vc++] += pf[vIndex++]*w;
+ Mcoord[vc++] += pf[vIndex++]*w;
+ }
+ break;
+ case GeometryArrayRetained.PD:
+ double[] pd = geometryArrays[j].getCoordRefDouble();
+ vIndex *= 3;
+ for (i=0; i< count; i++) {
+ Mcoord[vc++] += (float)pd[vIndex++]*w;
+ Mcoord[vc++] += (float)pd[vIndex++]*w;
+ Mcoord[vc++] += (float)pd[vIndex++]*w;
+ }
+ break;
+ case GeometryArrayRetained.P3F:
+ Point3f[] p3f = geometryArrays[j].getCoordRef3f();
+ for (i=0; i< count; i++, vIndex++) {
+ Mcoord[vc++] += p3f[vIndex].x*w;
+ Mcoord[vc++] += p3f[vIndex].y*w;
+ Mcoord[vc++] += p3f[vIndex].z*w;
+ }
+ break;
+ case GeometryArrayRetained.P3D:
+ Point3d[] p3d = geometryArrays[j].getCoordRef3d();
+ for (i=0; i< count; i++, vIndex++) {
+ Mcoord[vc++] += (float)p3d[vIndex].x*w;
+ Mcoord[vc++] += (float)p3d[vIndex].y*w;
+ Mcoord[vc++] += (float)p3d[vIndex].z*w;
+ }
+ break;
+
+ }
+
+ }
+ }
+ }
+ }
+
+ GeometryArrayRetained mgaR =
+ (GeometryArrayRetained)mga.retained;
+
+ mgaR.setCoordRefFloat(Mcoord);
+
+ if ((vFormat & GeometryArray.COLOR) != 0)
+ mgaR.setColorRefFloat(Mcolor);
+
+ // *******Need to normalize normals
+ if ((vFormat & GeometryArray.NORMALS) != 0)
+ mgaR.setNormalRefFloat(Mnormal);
+
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (k = 0; k < texCoordSetCount; k++) {
+ mgaR.setTexCoordRefFloat(k, MtexCoord[k]);
+ }
+ }
+ }
+
+ void updateImmediateMirrorObject(Object[] objs) {
+ int i;
+
+ int component = ((Integer)objs[1]).intValue();
+ Shape3DRetained[] msArr = (Shape3DRetained[]) objs[2];
+ if ((component & APPEARANCE_CHANGED) != 0) {
+ Object[] arg = (Object[])objs[3];
+ int val = ((Integer)arg[1]).intValue();
+ for ( i = msArr.length-1; i >=0; i--) {
+ msArr[i].appearance = (AppearanceRetained)arg[0];
+ msArr[i].changedFrequent = val;
+ }
+ }
+ if ((component & APPEARANCEOVERRIDE_CHANGED) != 0) {
+ Object[] arg = (Object[])objs[3];
+ int val = ((Integer)arg[1]).intValue();
+ System.out.println("ChangedFrequent = "+changedFrequent);
+ for ( i = msArr.length-1; i >=0; i--) {
+ msArr[i].appearanceOverrideEnable = ((Boolean)arg[0]).booleanValue();
+ msArr[i].changedFrequent = val;
+ }
+ }
+ }
+
+ /**
+ * assign a name to this node when it is made live.
+ */
+ void setLive(SetLiveState s) {
+ int i, j;
+ Shape3DRetained shape;
+ ArrayList msList = new ArrayList();
+ GeometryAtom ga;
+ int oldrefCount = refCount;
+
+ super.doSetLive(s);
+ nodeId = universe.getNodeId();
+
+
+ for (i = 0; i < numGeometryArrays; i++) {
+ synchronized(geometryArrays[i].liveStateLock) {
+ geometryArrays[i].setLive(inBackgroundGroup, s.refCount);
+ // Add this morph object as user the first time
+ if (oldrefCount <= 0) {
+ geometryArrays[i].addMorphUser(this);
+ }
+ }
+ }
+
+ if (this.morphedGeometryArray == null){
+ initMorphedGeometry();
+
+ }
+ ((GeometryArrayRetained)(morphedGeometryArray.retained)).setLive(inBackgroundGroup, s.refCount);
+
+ if (boundsAutoCompute) {
+ GeometryArrayRetained mga = (GeometryArrayRetained)morphedGeometryArray.retained;
+ // Compute the bounds once
+ mga.incrComputeGeoBounds(); // This compute the bbox if dirty
+ mga.decrComputeGeoBounds();
+ localBounds.setWithLock(mga.geoBounds);
+ }
+
+
+ if (inSharedGroup) {
+ for (i=0; i<s.keys.length; i++) {
+ shape = new Shape3DRetained();
+ shape.key = s.keys[i];
+ shape.localToVworld = new Transform3D[1][];
+ shape.localToVworldIndex = new int[1][];
+
+
+ j = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(j < 0) {
+ System.out.println("MorphRetained : Can't find hashKey");
+ }
+
+ shape.localToVworld[0] = localToVworld[j];
+ shape.localToVworldIndex[0] = localToVworldIndex[j];
+ shape.branchGroupPath = (BranchGroupRetained []) branchGroupPaths.get(j);
+ shape.isPickable = s.pickable[i];
+ shape.isCollidable = s.collidable[i];
+
+ shape.initMirrorShape3D(s, this, j);
+ mirrorShape3D.add(j, shape);
+
+ msList.add(shape);
+ // Add any scoped lights to the mirror shape
+ if (s.lights != null) {
+ ArrayList l = (ArrayList)s.lights.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addLight((LightRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped fog
+ if (s.fogs != null) {
+ ArrayList l = (ArrayList)s.fogs.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addFog((FogRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped modelClip
+ if (s.modelClips != null) {
+ ArrayList l = (ArrayList)s.modelClips.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addModelClip((ModelClipRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped alt app
+ if (s.altAppearances != null) {
+ ArrayList l = (ArrayList)s.altAppearances.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addAltApp((AlternateAppearanceRetained)l.get(m));
+ }
+ }
+ }
+
+ if (s.viewLists != null)
+ shape.viewList = (ArrayList)s.viewLists.get(i);
+ else
+ shape.viewList = null;
+
+ // ((GeometryArrayRetained)(morphedGeometryArray.retained)).addUser(shape);
+
+
+ ga = Shape3DRetained.getGeomAtom(shape);
+
+ // Add the geometry atom for this shape to the Targets
+ s.nodeList.add(ga);
+
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ga, Targets.GEO_TARGETS);
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(shape, Targets.GEO_TARGETS);
+ shape.closestSwitchParent = s.closestSwitchParents[i];
+ shape.closestSwitchIndex = s.closestSwitchIndices[i];
+ }
+ shape.switchState = (SwitchState)s.switchStates.get(j);
+
+ }
+ } else {
+ shape = new Shape3DRetained();
+ shape.localToVworld = new Transform3D[1][];
+ shape.localToVworldIndex = new int[1][];
+ shape.localToVworld[0] = this.localToVworld[0];
+ shape.localToVworldIndex[0] = this.localToVworldIndex[0];
+ shape.branchGroupPath = (BranchGroupRetained []) branchGroupPaths.get(0);
+ shape.isPickable = s.pickable[0];
+ shape.isCollidable = s.collidable[0];
+ shape.initMirrorShape3D(s, this, 0);
+ mirrorShape3D.add(shape);
+
+ msList.add(shape);
+ // Add any scoped lights to the mirror shape
+ if (s.lights != null) {
+ ArrayList l = (ArrayList)s.lights.get(0);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addLight((LightRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped fog
+ if (s.fogs != null) {
+ ArrayList l = (ArrayList)s.fogs.get(0);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addFog((FogRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped modelClip
+ if (s.modelClips != null) {
+ ArrayList l = (ArrayList)s.modelClips.get(0);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addModelClip((ModelClipRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped alt app
+ if (s.altAppearances != null) {
+ ArrayList l = (ArrayList)s.altAppearances.get(0);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addAltApp((AlternateAppearanceRetained)l.get(m));
+ }
+ }
+ }
+
+ if (s.viewLists != null)
+ shape.viewList = (ArrayList)s.viewLists.get(0);
+ else
+ shape.viewList = null;
+
+ // ((GeometryArrayRetained)(morphedGeometryArray.retained)).addUser(shape);
+
+ ga = Shape3DRetained.getGeomAtom(shape);
+
+ // Add the geometry atom for this shape to the Targets
+ s.nodeList.add(ga);
+
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ga, Targets.GEO_TARGETS);
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS);
+ shape.closestSwitchParent = s.closestSwitchParents[0];
+ shape.closestSwitchIndex = s.closestSwitchIndices[0];
+ }
+ shape.switchState = (SwitchState)s.switchStates.get(0);
+ }
+ if (appearance != null) {
+ synchronized(appearance.liveStateLock) {
+ appearance.setLive(inBackgroundGroup, s.refCount);
+ appearance.initMirrorObject();
+ if (appearance.renderingAttributes != null)
+ visible = appearance.renderingAttributes.visible;
+ for (int k = 0; k < msList.size(); k++) {
+ Shape3DRetained sh = (Shape3DRetained)msList.get(k);
+ sh.appearance = (AppearanceRetained)appearance.mirror;
+ appearance.addAMirrorUser(sh);
+ }
+ }
+
+ }
+ else {
+ for (int k = 0; k < msList.size(); k++) {
+ Shape3DRetained sh = (Shape3DRetained)msList.get(k);
+ sh.appearance = null;
+ }
+ }
+
+ s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_RENDERING_ATTRIBUTES);
+
+ // Need to clone the geometry , if its indexed ...
+ if (refCount == 1 && this.geometryArrays[0] instanceof IndexedGeometryArrayRetained) {
+ J3dMessage mChangeMessage = VirtualUniverse.mc.getMessage();
+ mChangeMessage.type = J3dMessage.MORPH_CHANGED;
+ mChangeMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ mChangeMessage.args[0] = this;
+ mChangeMessage.args[1]= new Integer(GEOMETRY_CHANGED);
+ mChangeMessage.universe = universe;
+ VirtualUniverse.mc.processMessage(mChangeMessage);
+ }
+ super.markAsLive();
+
+ }
+
+
+ /**
+ * assign a name to this node when it is made live.
+ */
+ void clearLive(SetLiveState s) {
+ int i, j;
+ Shape3DRetained shape;
+ Object[] shapes;
+ ArrayList appList = new ArrayList();
+ GeometryAtom ga;
+
+ super.clearLive(s);
+
+ for (i = 0; i < numGeometryArrays; i++) {
+ synchronized(geometryArrays[i].liveStateLock) {
+ geometryArrays[i].clearLive(s.refCount);
+ // Remove this morph object as user, when the last branch
+ // is clearlived
+ if (refCount <= 0) {
+ geometryArrays[i].removeMorphUser(this);
+ }
+ }
+ }
+ GeometryArrayRetained mga = (GeometryArrayRetained)morphedGeometryArray.retained;
+
+ mga.clearLive( s.refCount);
+
+ if (inSharedGroup) {
+ shapes = mirrorShape3D.toArray();
+ for (i=0; i<s.keys.length; i++) {
+ for (j=0; j<shapes.length; j++) {
+ shape = (Shape3DRetained)shapes[j];
+ if (shape.key.equals(s.keys[i])) {
+ // clearMirrorShape(shape);
+ mirrorShape3D.remove(j);
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(shape,
+ Targets.GEO_TARGETS);
+ }
+ if (appearance != null)
+ appList.add(shape);
+
+ // ((GeometryArrayRetained)(morphedGeometryArray.retained)).removeUser(shape);
+ ga = Shape3DRetained.getGeomAtom(shape);
+
+ // Add the geometry atoms for this shape to the Targets
+ s.nodeList.add(ga);
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ga,
+ Targets.GEO_TARGETS);
+ }
+ }
+ }
+ }
+ } else {
+ // Only entry 0 is valid
+ shape = (Shape3DRetained)mirrorShape3D.get(0);
+ // clearMirrorShape(shape);
+ mirrorShape3D.remove(0);
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS);
+ }
+ if (appearance != null)
+ appList.add(shape);
+
+ // ((GeometryArrayRetained)(morphedGeometryArray.retained)).removeUser(shape);
+ ga = Shape3DRetained.getGeomAtom(shape);
+
+ // Add the geometry atom for this shape to the Targets
+ s.nodeList.add(ga);
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ga, Targets.GEO_TARGETS);
+ }
+ }
+ if (appearance != null) {
+ synchronized(appearance.liveStateLock) {
+ appearance.clearLive(s.refCount);
+ for (int k = 0; k < appList.size(); k++) {
+ appearance.removeAMirrorUser((Shape3DRetained)appList.get(k));
+ }
+ }
+ }
+
+ s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_TRANSFORM |
+ // This is used to clear the scope info
+ // of all the mirror shapes
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER);
+
+ }
+
+
+ void updatePickable(HashKey keys[], boolean pick[]) {
+ super.updatePickable(keys, pick);
+
+ Shape3DRetained shape;
+
+ if (!inSharedGroup) {
+ shape = (Shape3DRetained) mirrorShape3D.get(0);
+ shape.isPickable = pick[0];
+ } else {
+ int size = mirrorShape3D.size();
+ for (int j=0; j< keys.length; j++) {
+ for (int i=0; i < size; i++) {
+ shape = (Shape3DRetained) mirrorShape3D.get(i);
+ if (keys[j].equals(shape.key)) {
+ shape.isPickable = pick[j];
+ break;
+ }
+
+ }
+ }
+ }
+ }
+
+
+ void updateCollidable(HashKey keys[], boolean collide[]) {
+ super.updateCollidable(keys, collide);
+ Shape3DRetained shape;
+
+ if (!inSharedGroup) {
+ shape = (Shape3DRetained) mirrorShape3D.get(0);
+ shape.isCollidable = collide[0];
+ } else {
+ int size = mirrorShape3D.size();
+ for (int j=0; j< keys.length; j++) {
+ for (int i=0; i < size; i++) {
+ shape = (Shape3DRetained) mirrorShape3D.get(i);
+ if (keys[j].equals(shape.key)) {
+ shape.isCollidable = collide[j];
+ break;
+ }
+
+ }
+ }
+ }
+ }
+
+ Shape3DRetained getMirrorShape(SceneGraphPath path) {
+ if (!inSharedGroup) {
+ return (Shape3DRetained) mirrorShape3D.get(0);
+ }
+ HashKey key = new HashKey("");
+ path.getHashKey(key);
+ return getMirrorShape(key);
+ }
+
+ Shape3DRetained getMirrorShape(HashKey key) {
+ int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ if (i>=0) {
+ return (Shape3DRetained) mirrorShape3D.get(i);
+ }
+
+ // Not possible
+ throw new RuntimeException("Shape3DRetained: MirrorShape Not found!");
+ }
+
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ Shape3DRetained ms;
+ if (inSharedGroup) {
+ ms = getMirrorShape(key);
+ }
+ else {
+ ms = (Shape3DRetained)mirrorShape3D.get(0);
+ }
+ GeometryAtom ga = Shape3DRetained.getGeomAtom(ms);
+ leafList.add(ga);
+
+ }
+
+ void setBoundsAutoCompute(boolean autoCompute) {
+ if (autoCompute != boundsAutoCompute) {
+ if (autoCompute) {
+ // localBounds may not have been set to bbox
+ localBounds = new BoundingBox();
+ if (source.isLive() && morphedGeometryArray != null) {
+ GeometryArrayRetained mga = (GeometryArrayRetained)morphedGeometryArray.retained;
+ mga.incrComputeGeoBounds(); // This compute the bbox if dirty
+ mga.decrComputeGeoBounds();
+ }
+ }
+
+
+ localBounds = getBounds();
+ super.setBoundsAutoCompute(autoCompute);
+ if (source.isLive()) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_RENDER;
+ message.universe = universe;
+ message.args[0] = Shape3DRetained.getGeomAtomsArray(mirrorShape3D);
+ message.args[1] = localBounds;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+ }
+
+ void updateBounds() {
+ localBounds = getEffectiveBounds();
+ if (source.isLive()) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_RENDER;
+ message.universe = universe;
+ message.args[0] = Shape3DRetained.getGeomAtomsArray(mirrorShape3D);
+ message.args[1] = localBounds;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ /**
+ * Initialization of morphed geometry
+ */
+ void initMorphedGeometry() {
+ int vFormat, geoType, stripVCount[];
+ int iCount = 0;
+ int numStrips = 0;
+ int texCoordSetCount = 0;
+ int texCoordSetMapLen = 0;
+ int [] texCoordSetMap = null;
+ int k;
+ GeometryArrayRetained geo = geometryArrays[0];
+ vFormat = ((geo.getVertexFormat() | (GeometryArray.BY_REFERENCE)) & ~(GeometryArray.INTERLEAVED)) ;
+ texCoordSetCount = geo.getTexCoordSetCount();
+ texCoordSetMapLen = geo.getTexCoordSetMapLength();
+ if (texCoordSetMapLen > 0) {
+ texCoordSetMap = new int[texCoordSetMapLen];
+ geo.getTexCoordSetMap(texCoordSetMap);
+ }
+ geoType = geo.geoType;
+
+ switch (geoType){
+ case GeometryRetained.GEO_TYPE_QUAD_SET:
+ this.morphedGeometryArray =
+ new QuadArray(geometryArrays[0].validVertexCount, vFormat, texCoordSetCount,
+ texCoordSetMap);
+ break;
+ case GeometryRetained.GEO_TYPE_TRI_SET:
+ this.morphedGeometryArray =
+ new TriangleArray(geometryArrays[0].validVertexCount, vFormat, texCoordSetCount,
+ texCoordSetMap);
+ break;
+ case GeometryRetained.GEO_TYPE_POINT_SET:
+ this.morphedGeometryArray =
+ new PointArray(geometryArrays[0].validVertexCount, vFormat, texCoordSetCount,
+ texCoordSetMap);
+ break;
+ case GeometryRetained.GEO_TYPE_LINE_SET:
+ this.morphedGeometryArray =
+ new LineArray(geometryArrays[0].validVertexCount, vFormat, texCoordSetCount,
+ texCoordSetMap);
+ break;
+ case GeometryRetained.GEO_TYPE_TRI_STRIP_SET:
+ numStrips = ((TriangleStripArrayRetained)geo).getNumStrips();
+ stripVCount = new int[numStrips];
+ ((TriangleStripArrayRetained)geo).getStripVertexCounts(stripVCount);
+ this.morphedGeometryArray =
+ new TriangleStripArray(geometryArrays[0].validVertexCount, vFormat, texCoordSetCount,
+ texCoordSetMap, stripVCount);
+ break;
+ case GeometryRetained.GEO_TYPE_TRI_FAN_SET:
+ numStrips = ((TriangleFanArrayRetained)geo).getNumStrips();
+ stripVCount = new int[numStrips];
+ ((TriangleFanArrayRetained)geo).getStripVertexCounts(stripVCount);
+ this.morphedGeometryArray =
+ new TriangleFanArray(geometryArrays[0].validVertexCount, vFormat, texCoordSetCount,
+ texCoordSetMap, stripVCount);
+ break;
+ case GeometryRetained.GEO_TYPE_LINE_STRIP_SET:
+ numStrips = ((LineStripArrayRetained)geo).getNumStrips();
+ stripVCount = new int[numStrips];
+ ((LineStripArrayRetained)geo).getStripVertexCounts(stripVCount);
+ this.morphedGeometryArray =
+ new LineStripArray(geometryArrays[0].validVertexCount, vFormat, texCoordSetCount,
+ texCoordSetMap, stripVCount);
+ break;
+
+ case GeometryRetained.GEO_TYPE_INDEXED_QUAD_SET:
+ iCount = ((IndexedGeometryArrayRetained)geo).getIndexCount();
+ this.morphedGeometryArray =
+ new IndexedQuadArray(geometryArrays[0].getNumCoordCount(), vFormat, texCoordSetCount,
+ texCoordSetMap, iCount);
+ break;
+ case GeometryRetained.GEO_TYPE_INDEXED_TRI_SET:
+ iCount = ((IndexedGeometryArrayRetained)geo).getIndexCount();
+ this.morphedGeometryArray =
+ new IndexedTriangleArray(geometryArrays[0].getNumCoordCount(), vFormat, texCoordSetCount,
+ texCoordSetMap, iCount);
+ break;
+ case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET:
+ iCount = ((IndexedGeometryArrayRetained)geo).getIndexCount();
+ this.morphedGeometryArray =
+ new IndexedPointArray(geometryArrays[0].getNumCoordCount(), vFormat, texCoordSetCount,
+ texCoordSetMap, iCount);
+ break;
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET:
+ iCount = ((IndexedGeometryArrayRetained)geo).getIndexCount();
+ this.morphedGeometryArray =
+ new IndexedLineArray(geometryArrays[0].getNumCoordCount(), vFormat, texCoordSetCount,
+ texCoordSetMap, iCount);
+ break;
+ case GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET:
+ iCount = ((IndexedGeometryArrayRetained)geo).getIndexCount();
+ numStrips = ((IndexedTriangleStripArrayRetained)geo).getNumStrips();
+ stripVCount = new int[numStrips];
+ ((IndexedTriangleStripArrayRetained)geo).getStripIndexCounts(stripVCount);
+ this.morphedGeometryArray =
+ new IndexedTriangleStripArray(geometryArrays[0].getNumCoordCount(), vFormat, texCoordSetCount,
+ texCoordSetMap, iCount, stripVCount);break;
+ case GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET:
+ iCount = ((IndexedGeometryArrayRetained)geo).getIndexCount();
+ numStrips = ((IndexedTriangleFanArrayRetained)geo).getNumStrips();
+ stripVCount = new int[numStrips];
+ ((IndexedTriangleFanArrayRetained)geo).getStripIndexCounts(stripVCount);
+ this.morphedGeometryArray =
+ new IndexedTriangleFanArray(geometryArrays[0].getNumCoordCount(), vFormat, texCoordSetCount,
+ texCoordSetMap, iCount, stripVCount);break;
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ iCount = ((IndexedGeometryArrayRetained)geo).getIndexCount();
+ numStrips = ((IndexedLineStripArrayRetained)geo).getNumStrips();
+ stripVCount = new int[numStrips];
+ ((IndexedLineStripArrayRetained)geo).getStripIndexCounts(stripVCount);
+ this.morphedGeometryArray =
+ new IndexedLineStripArray(geometryArrays[0].getNumCoordCount(), vFormat, texCoordSetCount,
+ texCoordSetMap, iCount, stripVCount);break;
+ }
+ if (geometryArrays[0] instanceof IndexedGeometryArrayRetained) {
+ IndexedGeometryArrayRetained igeo = (IndexedGeometryArrayRetained)
+ geometryArrays[0];
+ IndexedGeometryArray morphedGeo = (IndexedGeometryArray)
+ morphedGeometryArray;
+ if ((vFormat & GeometryArray.COORDINATES) != 0) {
+ morphedGeo.setCoordinateIndices(0, igeo.indexCoord);
+ }
+ if ((vFormat & GeometryArray.NORMALS) != 0) {
+ morphedGeo.setNormalIndices(0, igeo.indexNormal);
+ }
+ if ((vFormat & GeometryArray.COLOR) != 0) {
+ morphedGeo.setColorIndices(0, igeo.indexColor);
+ }
+ if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+ for (k = 0; k < texCoordSetCount; k++) {
+ morphedGeo.setTextureCoordinateIndices(k, 0,
+ (int[]) igeo.indexTexCoord[k]);
+ }
+ }
+ }
+ this.morphedGeometryArray.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE);
+
+ GeometryArrayRetained mga = (GeometryArrayRetained)morphedGeometryArray.retained;
+ mga.updateData(this);
+
+
+ }
+
+ void getMirrorShape3D(ArrayList list, HashKey k) {
+ Shape3DRetained ms;
+ if (inSharedGroup) {
+ ms = getMirrorShape(k);
+ }
+ else {
+ ms = (Shape3DRetained)mirrorShape3D.get(0);
+ }
+ list.add(ms);
+
+ }
+
+ void compile(CompileState compState) {
+
+ super.compile(compState);
+
+ // TODO: for now keep the static transform in the parent tg
+ compState.keepTG = true;
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numMorphs++;
+ }
+ }
+
+ void doErrorCheck(GeometryArrayRetained prevGeo, GeometryArrayRetained geo) {
+
+ // If indexed Geometry array check the entire list instead of just the vcount
+ if ((prevGeo.vertexFormat != geo.vertexFormat) ||
+ (prevGeo.validVertexCount != geo.validVertexCount) ||
+ (prevGeo.geoType != geo.geoType) ||
+ (prevGeo.texCoordSetCount != geo.texCoordSetCount)) {
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ }
+ if (geo.getTexCoordSetMapLength() != prevGeo.getTexCoordSetMapLength()){
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ }
+ int texCoordSetMapLen = geo.getTexCoordSetMapLength();
+ int[] prevSvc= prevGeo.texCoordSetMap;
+ int[] svc= geo.texCoordSetMap;
+ for (int k = 0; k < texCoordSetMapLen; k++) {
+ if (prevSvc[k] != svc[k])
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ }
+
+ if (geo instanceof GeometryStripArrayRetained) {
+ prevSvc= ((GeometryStripArrayRetained)prevGeo).stripVertexCounts;
+ svc= ((GeometryStripArrayRetained)geo).stripVertexCounts;
+ if (prevSvc.length != svc.length)
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ for (int k = 0; k < prevSvc.length; k++) {
+ if (prevSvc[k] != svc[k])
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ }
+ } else if (geo instanceof IndexedGeometryArrayRetained) {
+ if (((IndexedGeometryArrayRetained)prevGeo).validIndexCount != ((IndexedGeometryArrayRetained)geo).validIndexCount)
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+
+ // If by reference, then all array lengths should be same
+ if (geo.getNumCoordCount() != prevGeo.getNumCoordCount() ||
+ geo.getNumColorCount() != prevGeo.getNumColorCount() ||
+ geo.getNumNormalCount() != prevGeo.getNumNormalCount()) {
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ }
+ int texCoordSetCount = geo.getTexCoordSetCount();
+ for (int k = 0; k < texCoordSetCount; k++) {
+ if (geo.getNumTexCoordCount(k) != prevGeo.getNumTexCoordCount(k)) {
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ }
+ }
+
+ if (geo instanceof IndexedGeometryStripArrayRetained) {
+ prevSvc= ((IndexedGeometryStripArrayRetained)prevGeo).stripIndexCounts;
+ svc= ((IndexedGeometryStripArrayRetained)geo).stripIndexCounts;
+ if (prevSvc.length != svc.length)
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ for (int k = 0; k < prevSvc.length; k++) {
+ if (prevSvc[k] != svc[k])
+ throw new IllegalArgumentException(J3dI18N.getString("MorphRetained1"));
+ }
+ }
+ }
+ }
+
+ void handleFrequencyChange(int bit) {
+ int mask = 0;
+ if (bit == Morph.ALLOW_GEOMETRY_ARRAY_WRITE) {
+ mask = GEOMETRY_CHANGED;
+ }
+ else if (bit == Morph.ALLOW_APPEARANCE_WRITE) {
+ mask = APPEARANCE_CHANGED;
+ }
+ else if (bit == Morph.ALLOW_APPEARANCE_OVERRIDE_WRITE) {
+ mask = APPEARANCEOVERRIDE_CHANGED;
+ }
+ if (mask != 0) {
+ if (source.getCapabilityIsFrequent(bit))
+ changedFrequent |= mask;
+ else if (!source.isLive()) {
+ changedFrequent &= ~mask;
+ }
+ }
+ }
+
+ void searchGeometryAtoms(UnorderList list) {
+ list.add(Shape3DRetained.getGeomAtom(
+ (Shape3DRetained) mirrorShape3D.get(0)));
+ }
+}
+
+
+
+
+
diff --git a/src/classes/share/javax/media/j3d/MultipleParentException.java b/src/classes/share/javax/media/j3d/MultipleParentException.java
new file mode 100644
index 0000000..23092d8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/MultipleParentException.java
@@ -0,0 +1,37 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates
+ * an attempt to add a node that is already a child of one
+ * group node, into another group node.
+ */
+public class MultipleParentException extends IllegalSharingException {
+
+/**
+ * Create the exception object with default values.
+ */
+ public MultipleParentException(){
+ }
+
+/**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public MultipleParentException(String str){
+
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/NativeAPIInfo.java b/src/classes/share/javax/media/j3d/NativeAPIInfo.java
new file mode 100644
index 0000000..d1bf0ca
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NativeAPIInfo.java
@@ -0,0 +1,32 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * Portions of this code were derived from work done by the Blackdown
+ * group (www.blackdown.org), who did the initial Linux implementation
+ * of the Java 3D API.
+ */
+
+package javax.media.j3d;
+
+class NativeAPIInfo {
+
+ /**
+ * Returns the rendering API being used.
+ * @return the rendering API, one of:
+ * <code>MasterControl.RENDER_OPENGL_LINUX</code>,
+ * <code>MasterControl.RENDER_OPENGL_SOLARIS</code>,
+ * <code>MasterControl.RENDER_OPENGL_WIN32</code>,
+ * or <code>MasterControl.RENDER_DIRECT3D</code>
+ */
+ native int getRenderingAPI();
+}
diff --git a/src/classes/share/javax/media/j3d/NnuId.java b/src/classes/share/javax/media/j3d/NnuId.java
new file mode 100644
index 0000000..beb1f26
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NnuId.java
@@ -0,0 +1,25 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Defines a "not necessarily unique ID"
+ */
+
+interface NnuId {
+
+ abstract int equal(NnuId obj);
+
+ abstract int getId();
+
+}
diff --git a/src/classes/share/javax/media/j3d/NnuIdManager.java b/src/classes/share/javax/media/j3d/NnuIdManager.java
new file mode 100644
index 0000000..6cf3696
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NnuIdManager.java
@@ -0,0 +1,335 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class NnuIdManager {
+ static int nnuId = 0;
+
+ final static int getId() {
+ if(nnuId == Integer.MAX_VALUE) {
+ nnuId = 0;
+ }
+
+ return nnuId++;
+ }
+
+ final static int equals(NnuId nnuIdArr[], NnuId key, int start, int end) {
+ int mid;
+
+ mid = start +((end - start)/ 2);
+ if(nnuIdArr[mid] != null) {
+ int test = key.equal(nnuIdArr[mid]);
+
+ if((test < 0) && (start != mid))
+ return equals(nnuIdArr, key, start, mid);
+ else if((test > 0) && (start != mid))
+ return equals(nnuIdArr, key, mid, end);
+ else if(test == 0) {
+ // Since id is not necessary unique, we've to do
+ // some extra check.
+
+ // check for equal reference.
+ if(key == nnuIdArr[mid]) {
+ return mid;
+ }
+
+ int temp = mid - 1;
+ // Look to the left.
+ while((temp >= start) && (key.equal(nnuIdArr[temp]) == 0)) {
+ if(key == nnuIdArr[temp]) {
+ return temp;
+ }
+ temp--;
+ }
+
+ // Look to the right.
+ temp = mid + 1;
+ while((temp < end) && (key.equal(nnuIdArr[temp]) == 0)) {
+ if(key == nnuIdArr[temp]) {
+ return temp;
+ }
+ temp++;
+ }
+
+ // Fail equal reference check.
+ return -1;
+ }
+ else
+ return -1;
+ }
+ // A null NnuId object encountered.
+ return -2;
+ }
+
+ final static boolean equals(NnuId nnuIdArr[], NnuId key, int[] index,
+ int start, int end) {
+
+ int mid;
+
+ mid = start +((end - start)/ 2);
+
+ if(nnuIdArr[mid] != null) {
+ int test = key.equal(nnuIdArr[mid]);
+
+ if(start != mid) {
+ if(test < 0) {
+ return equals(nnuIdArr, key, index, start, mid);
+ }
+ else if(test > 0) {
+ return equals(nnuIdArr, key, index, mid, end);
+ }
+ }
+ else { // (start == mid)
+ if(test < 0) {
+ index[0] = mid;
+ return false;
+ }
+ else if(test > 0) {
+ index[0] = mid+1;
+ return false;
+ }
+ }
+
+ // (test == 0)
+ // Since id is not necessary unique, we've to do
+ // some extra check.
+
+ // check for equal reference.
+ if(key == nnuIdArr[mid]) {
+ index[0] = mid;
+ return true;
+ }
+
+ int temp = mid - 1;
+ // Look to the left.
+ while((temp >= start) && (key.equal(nnuIdArr[temp]) == 0)) {
+ if(key == nnuIdArr[temp]) {
+ index[0] = temp;
+ return true;
+ }
+ temp--;
+ }
+
+ // Look to the right.
+ temp = mid + 1;
+ while((temp < end) && (key.equal(nnuIdArr[temp]) == 0)) {
+ if(key == nnuIdArr[temp]) {
+ index[0] = temp;
+ return true;
+ }
+ temp++;
+ }
+
+ // Fail equal reference check.
+ index[0] = temp;
+ return false;
+
+ }
+ // A null entry encountered.
+ // But we still want to return the index where we encounter it.
+ index[0] = mid;
+ return false;
+ }
+
+ final static void sort(NnuId nnuIdArr[]) {
+ if (nnuIdArr.length < 20) {
+ insertSort(nnuIdArr);
+ } else {
+ quicksort(nnuIdArr, 0, nnuIdArr.length-1);
+ }
+ }
+
+ // Insertion sort on smaller array
+ final static void insertSort(NnuId nnuIdArr[]) {
+
+
+ for (int i=0; i<nnuIdArr.length; i++) {
+ for (int j=i; j>0 &&
+ (nnuIdArr[j-1].getId() > nnuIdArr[j].getId()); j--) {
+ NnuId temp = nnuIdArr[j];
+ nnuIdArr[j] = nnuIdArr[j-1];
+ nnuIdArr[j-1] = temp;
+ }
+ }
+ }
+
+ final static void quicksort( NnuId nnuIdArr[], int l, int r ) {
+ int i = l;
+ int j = r;
+ int k = nnuIdArr[(l+r) / 2].getId();
+
+ do {
+ while (nnuIdArr[i].getId() < k) i++;
+ while (k < nnuIdArr[j].getId()) j--;
+ if (i<=j) {
+ NnuId tmp = nnuIdArr[i];
+ nnuIdArr[i] = nnuIdArr[j];
+ nnuIdArr[j] = tmp;
+
+ i++;
+ j--;
+ }
+ } while (i<=j);
+
+ if (l<j) quicksort(nnuIdArr, l,j);
+ if (l<r) quicksort(nnuIdArr, i,r);
+ }
+
+
+
+ // This method assumes that nnuIdArr0 and nnuIdArr1 are sorted.
+ final static NnuId[] delete( NnuId nnuIdArr0[], NnuId nnuIdArr1[] ) {
+
+ int i, index, len;
+ int curStart =0, newStart =0;
+ boolean found = false;
+
+ int size = nnuIdArr0.length - nnuIdArr1.length;
+
+ if(size > 0) {
+ NnuId newNnuIdArr[] = new NnuId[size];
+
+ for(i=0; i<nnuIdArr1.length; i++) {
+ index = equals(nnuIdArr0, nnuIdArr1[i], 0, nnuIdArr0.length);
+
+ if(index >= 0) {
+ found = true;
+ if(index == curStart) {
+ curStart++;
+ }
+ else {
+ len = index - curStart;
+ System.arraycopy(nnuIdArr0, curStart,
+ newNnuIdArr, newStart, len);
+
+ curStart = index+1;
+ newStart = newStart + len;
+ }
+ }
+ else {
+ found = false;
+ System.out.println("Can't Find matching nnuId.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ }
+
+ if((found == true) && (curStart < nnuIdArr0.length)) {
+ len = nnuIdArr0.length - curStart;
+ System.arraycopy(nnuIdArr0, curStart, newNnuIdArr, newStart, len);
+ }
+
+ return newNnuIdArr;
+ }
+ else if( size == 0) {
+ // Remove all.
+ }
+ else {
+ // We are in trouble !!!
+ }
+
+ return null;
+
+ }
+
+
+ // This method assumes that nnuIdArr0 and nnuIdArr1 are sorted.
+ final static NnuId[] merge( NnuId nnuIdArr0[], NnuId nnuIdArr1[] ) {
+
+ int index[] = new int[1];
+ int indexPlus1, blkSize, i, j;
+
+ int size = nnuIdArr0.length + nnuIdArr1.length;
+
+ NnuId newNnuIdArr[] = new NnuId[size];
+
+ // Copy the nnuIdArr0 data into the newly created newNnuIdArr.
+ System.arraycopy(nnuIdArr0, 0, newNnuIdArr, 0, nnuIdArr0.length);
+
+ for(i=nnuIdArr0.length, j=0; i<size; i++, j++) {
+ // True or false, it doesn't matter.
+ equals((NnuId[])newNnuIdArr, nnuIdArr1[j], index, 0, i);
+
+ if(index[0] == i) { // Append to last.
+ newNnuIdArr[i] = nnuIdArr1[j];
+ }
+ else { // Insert in between array elements.
+ indexPlus1 = index[0] + 1;
+ blkSize = i - index[0];
+
+ // Shift the later portion of array elements by one position.
+ // This is the make room for the new data entry.
+ System.arraycopy(newNnuIdArr, index[0], newNnuIdArr,
+ indexPlus1, blkSize);
+
+ newNnuIdArr[index[0]] = nnuIdArr1[j];
+ }
+
+ }
+
+ return newNnuIdArr;
+
+ }
+
+
+ final static void replace(NnuId oldObj, NnuId newObj, NnuId nnuIdArr[]) {
+
+
+ int[] index = new int[1];
+ int lenLess1 = nnuIdArr.length - 1;
+ int blkSize;
+
+ // delete old from nnuIdArr.
+ index[0] = equals(nnuIdArr, oldObj, 0, nnuIdArr.length);
+ if(index[0] == lenLess1) {
+ nnuIdArr[index[0]] = null;
+ }
+ else if(index[0] >= 0) {
+ blkSize = lenLess1 - index[0];
+ System.arraycopy(nnuIdArr, index[0]+1,
+ nnuIdArr, index[0], blkSize);
+ nnuIdArr[lenLess1] = null;
+ }
+ else {
+ System.out.println("Can't Find matching nnuId.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+
+ // insert new to nnuIdArr.
+ equals(nnuIdArr, newObj, index, 0, lenLess1);
+
+ if(index[0] == lenLess1) { // Append to last.
+ nnuIdArr[index[0]] = newObj;
+ }
+ else { // Insert in between array elements.
+ blkSize = lenLess1 - index[0];
+
+ // Shift the later portion of array elements by one position.
+ // This is the make room for the new data entry.
+ System.arraycopy(nnuIdArr, index[0], nnuIdArr,
+ index[0]+1, blkSize);
+
+ nnuIdArr[index[0]] = newObj;
+ }
+
+
+ }
+
+
+ final static void printIds(NnuId nnuIdArr[]) {
+ for(int i=0; i<nnuIdArr.length; i++) {
+ System.out.println("[" + i +"] is " + nnuIdArr[i].getId());
+ }
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Node.java b/src/classes/share/javax/media/j3d/Node.java
new file mode 100644
index 0000000..6488a15
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Node.java
@@ -0,0 +1,733 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.lang.reflect.Constructor;
+
+/**
+ * The Node class provides an abstract class for all Group and Leaf Nodes.
+ * It provides a common framework for constructing a Java 3D scene graph,
+ * specifically bounding volumes.
+ * <p>
+ * NOTE: Applications should <i>not</i> extend this class directly.
+ */
+public abstract class Node extends SceneGraphObject {
+
+ /**
+ * Specifies that this Node will be reported in the pick
+ * SceneGraphPath if a pick occurs. This capability is only
+ * specifiable for Group nodes; it is ignored for leaf nodes.
+ * The default for Group nodes is false. All interior nodes not
+ * needed for uniqueness in a SceneGraphPath that don't have
+ * ENABLE_PICK_REPORTING set to true will not be reported in the
+ * SceneGraphPath.
+ * @see SceneGraphPath
+ */
+ public static final int
+ ENABLE_PICK_REPORTING = CapabilityBits.NODE_ENABLE_PICK_REPORTING;
+
+ /**
+ * Specifies that this Node will be reported in the collision
+ * SceneGraphPath if a collision occurs. This capability is only
+ * specifiable for Group nodes; it is ignored for leaf nodes.
+ * The default for Group nodes is false. All interior nodes not
+ * needed for uniqueness in a SceneGraphPath that don't have
+ * ENABLE_COLLISION_REPORTING set to true will not be reported
+ * in the SceneGraphPath.
+ * @see SceneGraphPath
+ */
+ public static final int
+ ENABLE_COLLISION_REPORTING = CapabilityBits.NODE_ENABLE_COLLISION_REPORTING;
+
+ /**
+ * Specifies that this Node allows read access to its bounds
+ * information.
+ */
+ public static final int
+ ALLOW_BOUNDS_READ = CapabilityBits.NODE_ALLOW_BOUNDS_READ;
+
+ /**
+ * Specifies that this Node allows write access to its bounds
+ * information.
+ */
+ public static final int
+ ALLOW_BOUNDS_WRITE = CapabilityBits.NODE_ALLOW_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this Node allows reading its pickability state.
+ */
+ public static final int
+ ALLOW_PICKABLE_READ = CapabilityBits.NODE_ALLOW_PICKABLE_READ;
+
+ /**
+ * Specifies that this Node allows write access its pickability state.
+ */
+ public static final int
+ ALLOW_PICKABLE_WRITE = CapabilityBits.NODE_ALLOW_PICKABLE_WRITE;
+
+ /**
+ * Specifies that this Node allows reading its collidability state.
+ */
+ public static final int
+ ALLOW_COLLIDABLE_READ = CapabilityBits.NODE_ALLOW_COLLIDABLE_READ;
+
+ /**
+ * Specifies that this Node allows write access its collidability state.
+ */
+ public static final int
+ ALLOW_COLLIDABLE_WRITE = CapabilityBits.NODE_ALLOW_COLLIDABLE_WRITE;
+
+ /**
+ * Specifies that this Node allows read access to its bounds
+ * auto compute information.
+ */
+ public static final int
+ ALLOW_AUTO_COMPUTE_BOUNDS_READ = CapabilityBits.NODE_ALLOW_AUTO_COMPUTE_BOUNDS_READ;
+
+ /**
+ * Specifies that this Node allows write access to its bounds
+ * auto compute information.
+ */
+ public static final int
+ ALLOW_AUTO_COMPUTE_BOUNDS_WRITE = CapabilityBits.NODE_ALLOW_AUTO_COMPUTE_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this Node allows read access to its local
+ * coordinates to virtual world (Vworld) coordinates transform.
+ */
+ public static final int
+ ALLOW_LOCAL_TO_VWORLD_READ = CapabilityBits.NODE_ALLOW_LOCAL_TO_VWORLD_READ;
+
+ // for checking for cycles
+ private boolean visited = false;
+
+
+ /**
+ * Constructs a Node object with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * pickable : true<br>
+ * collidable : true<br>
+ * bounds auto compute : true<br>
+ * bounds : N/A (automatically computed)<br>
+ * </ul>
+ */
+ public Node() {
+ }
+
+ /**
+ * Retrieves the parent of this Node. This method is only valid
+ * during the construction of the scene graph.
+ * @return the parent of this node, or null if this node has no parent
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ */
+ public Node getParent() {
+ if (isLiveOrCompiled())
+ throw new RestrictedAccessException(J3dI18N.getString("Node0"));
+
+ NodeRetained nr = ((NodeRetained)this.retained).getParent();
+ return (nr == null ? null : (Node) nr.getSource());
+ }
+
+ /**
+ * Sets the geometric bounds of a node.
+ * @param bounds the bounding object for a node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setBounds(Bounds bounds) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node1"));
+
+ ((NodeRetained)this.retained).setBounds(bounds);
+ }
+
+ /**
+ * Returns the bounding object of a node.
+ * @return the node's bounding object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ */
+ public Bounds getBounds() {
+
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_BOUNDS_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Node2"));
+ }
+ }
+ else {
+ // this will throw a SceneGraphCycleException if there is
+ // a cycle
+ checkForCycle();
+ }
+
+ return ((NodeRetained)this.retained).getBounds();
+ }
+
+ /**
+ * Returns the collidable value; this value determines whether this node
+ * and it's children, if a group node, can be considered for collision
+ * purposes; if it is set to false, then neither this node nor any
+ * children nodes will be traversed for collision purposes; the default
+ * value is true. The collidable setting is the way that an
+ * application can perform collision culling.
+ * @return the present collidable value for this node
+ */
+ public boolean getCollidable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLLIDABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node16"));
+
+ return ((NodeRetained)retained).getCollidable();
+ }
+
+ /**
+ * Sets the collidable value; determines whether this node and any of its
+ * children, if a group node, can be considered for collision purposes.
+ * @param collidable the new collidable value for this node
+ */
+ public void setCollidable( boolean collidable ) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_COLLIDABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node4"));
+
+ ((NodeRetained)retained).setCollidable(collidable);
+ }
+
+ /**
+ * Turns the automatic calcuation of geometric bounds of a node on/off.
+ * @param autoCompute indicates if the node's bounding object is
+ * automatically computed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setBoundsAutoCompute(boolean autoCompute) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_AUTO_COMPUTE_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node5"));
+
+ ((NodeRetained)this.retained).setBoundsAutoCompute(autoCompute);
+ }
+
+ /**
+ * Gets the value indicating if the automatic calcuation of geometric bounds of a node is on/off.
+ * @return the node's auto compute flag for the geometric bounding object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getBoundsAutoCompute() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_AUTO_COMPUTE_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node6"));
+
+ return ((NodeRetained)this.retained).getBoundsAutoCompute();
+ }
+
+ /**
+ * Retrieves the local coordinates to virtual world coordinates
+ * transform for this node in the scene graph. This is the composite
+ * of all transforms in the scene graph from the root down to
+ * <code>this</code> node. It is only valid
+ * for nodes that are part of a live scene graph.
+ * @param t the object that will receive the local coordinates to
+ * Vworld coordinates transform.
+ * @exception RestrictedAccessException if the node is <em>not</em>
+ * part of a live scene graph
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this node is part of live scene graph
+ * @exception IllegalSharingException if the node is a descendant
+ * of a SharedGroup node.
+ */
+ public void getLocalToVworld(Transform3D t) {
+ if (!isLive())
+ throw new RestrictedAccessException(J3dI18N.getString("Node7"));
+
+ if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
+
+ ((NodeRetained)this.retained).getLocalToVworld(t);
+ }
+
+ /**
+ * Retrieves the local coordinates to virtual world coordinates
+ * transform for the particular path in the scene graph ending with
+ * this node. This is the composite
+ * of all transforms in the scene graph from the root down to
+ * <code>this</code> node via the specified Link nodes. It is
+ * only valid for nodes that are part of a live scene graph.
+ * @param path the specific path from the node to the Locale
+ * @param t the object that will receive the local coordinates to
+ * Vworld coordinates transform.
+ * @exception RestrictedAccessException if the node is <em>not</em>
+ * part of a live scene graph
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this node is part of live scene graph
+ * @exception IllegalArgumentException if the specified path does
+ * not contain a valid Locale, or if the last node in the path is
+ * different from this node
+ */
+ public void getLocalToVworld(SceneGraphPath path, Transform3D t) {
+ if (!isLive())
+ throw new RestrictedAccessException(J3dI18N.getString("Node7"));
+
+ if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
+
+ ((NodeRetained)this.retained).getLocalToVworld(path,t);
+ }
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code>
+ * and then <code>cloneTree</code>
+ * is called for each child node. For Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ * @return a reference to the cloned sub-graph.
+ * @exception DanglingReferenceException When a dangling reference is
+ * discovered during the cloneTree operation.
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneTree() {
+ return cloneTree(new NodeReferenceTable(), false, false);
+ }
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code>
+ * and then <code>cloneTree</code> is called for each child node.
+ * For Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ * @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> determines whether data is
+ * duplicated or copied.
+ * @return a reference to the cloned scene graph.
+ * @exception DanglingReferenceException When a dangling reference is
+ * discovered during the cloneTree operation.
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneTree(boolean forceDuplicate) {
+ return cloneTree(new NodeReferenceTable(), forceDuplicate, false);
+ }
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code> and
+ * then <code>cloneTree</code> is called for each child node. For
+ * Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ *
+ * @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> determines whether data is
+ * duplicated or copied.
+ *
+ * @param allowDanglingReferences when set to <code>true</code> allows
+ * the <code>cloneTree</code>
+ * method to complete even whan a dangling reference is discovered. When
+ * this parameter is <code>false</code> a
+ * <code>DanglingReferenceException</code> is generated as
+ * soon as cloneTree detects this situation.
+ *
+ * @return a reference to the cloned scene graph.
+ *
+ * @exception DanglingReferenceException When a dangling reference is
+ * discovered during the cloneTree operation and the
+ * <code>allowDanglingReference</code> parameter is </code>false</code>.
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ *
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneTree(boolean forceDuplicate,
+ boolean allowDanglingReferences) {
+ return cloneTree(new NodeReferenceTable(),
+ forceDuplicate, allowDanglingReferences);
+ }
+
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code>
+ * and then <code>cloneTree</code>
+ * is called for each child node. For Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ * @param referenceTable table that stores the mapping between
+ * original and cloned nodes. All previous values in the
+ * referenceTable will be cleared before the clone is made.
+ * @return a reference to the cloned sub-graph.
+ * @exception DanglingReferenceException When a dangling reference is
+ * discovered during the cloneTree operation.
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ * @see NodeComponent#setDuplicateOnCloneTree
+ * @since Java 3D 1.2
+ */
+ public Node cloneTree(NodeReferenceTable referenceTable) {
+ return cloneTree(referenceTable, false, false);
+ }
+
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code>
+ * and then <code>cloneTree</code> is called for each child node.
+ * For Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ * @param referenceTable table that stores the mapping between
+ * original and cloned nodes. All previous values in the
+ * referenceTable will be cleared before the clone is made.
+ * @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> determines whether data is
+ * duplicated or copied.
+ * @return a reference to the cloned scene graph.
+ * @exception DanglingReferenceException When a dangling reference is
+ * discovered during the cloneTree operation.
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ * @see NodeComponent#setDuplicateOnCloneTree
+ * @since Java 3D 1.2
+ */
+ public Node cloneTree(NodeReferenceTable referenceTable,
+ boolean forceDuplicate) {
+ return cloneTree(referenceTable, forceDuplicate, false);
+ }
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code>
+ * and then <code>cloneTree</code> is called for each child node.
+ * For Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ * @param referenceTable table that stores the mapping between
+ * original and cloned nodes. All previous values in the
+ * referenceTable will be cleared before the clone is made.
+ * @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> determines whether data is
+ * duplicated or copied.
+ *
+ * @param allowDanglingReferences when set to <code>true</code> allows
+ * the <code>cloneTree</code>
+ * method to complete even whan a dangling reference is discovered. When
+ * this parameter is <code>false</code> a
+ * <code>DanglingReferenceException</code> is generated as
+ * soon as cloneTree detects this situation.
+ *
+ * @return a reference to the cloned scene graph.
+ * @exception DanglingReferenceException When a dangling reference is
+ * discovered during the cloneTree operation.
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ * @see NodeComponent#setDuplicateOnCloneTree
+ * @since Java 3D 1.2
+ */
+ public Node cloneTree(NodeReferenceTable referenceTable,
+ boolean forceDuplicate,
+ boolean allowDanglingReferences) {
+
+ if (!isLiveOrCompiled()) {
+ // this will throw a SceneGraphCycleException if there is
+ // a cycle
+ checkForCycle();
+ }
+
+ referenceTable.set(allowDanglingReferences, new Hashtable());
+ Node n = cloneTree(forceDuplicate, referenceTable.objectHashtable);
+
+ // go through hash table looking for Leaf nodes.
+ // call updateNodeReferences for each.
+ Enumeration e = referenceTable.objectHashtable.elements();
+
+ while (e.hasMoreElements()) {
+ SceneGraphObject o = (SceneGraphObject) e.nextElement();
+ o.updateNodeReferences(referenceTable);
+ }
+ return n;
+ }
+
+ /**
+ * Duplicates all the nodes of the specified sub-graph. For Group Nodes
+ * the group node is duplicated via a call to <code>cloneNode</code> and
+ * then <code>cloneTree</code> is called for each child node. For
+ * Leaf Nodes, component
+ * data can either be duplicated or be made a reference to the original
+ * data. Leaf Node cloneTree behavior is determined by the
+ * <code>duplicateOnCloneTree</code> flag found in every Leaf Node's
+ * component data class and by the <code>forceDuplicate</code> paramter.
+ *
+ * @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> determines whether data is
+ * duplicated or copied.
+ *
+ * @param nodeHashtable a hashtable used to map orignal node references to
+ * their cloned counterpart.
+ *
+ * @return a reference to the cloned scene graph.
+ *
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ Node cloneTree(boolean forceDuplicate, Hashtable nodeHashtable) {
+ Node l;
+ this.nodeHashtable = nodeHashtable;
+ try {
+ l = cloneNode(forceDuplicate);
+ } catch (RuntimeException e) {
+ this.nodeHashtable = null;
+ throw e;
+ }
+ // must reset to null so that we can tell whether the call is from user
+ // or cloneTree
+ this.nodeHashtable = null;
+ nodeHashtable.put(this, l);
+ return l;
+ }
+
+
+ /**
+ * 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>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @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 live
+ * or compiled scene graph
+ *
+ * @see Node#cloneTree
+ * @see Node#duplicateNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ throw new RuntimeException(J3dI18N.getString("Node12"));
+ }
+
+
+ /**
+ * 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ *
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 Group#cloneNode
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode,
+ boolean forceDuplicate) {
+ duplicateAttributes(originalNode, forceDuplicate);
+ }
+
+ /**
+ * Copies all node information from <code>originalNode</code> into
+ * the current node. This method is called from subclass of
+ * <code>duplicateNode</code> method which is, in turn, called by the
+ * <code>cloneNode</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 Group#cloneNode
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ final void checkDuplicateNode(Node originalNode,
+ boolean forceDuplicate) {
+ if (originalNode.nodeHashtable != null) {
+ duplicateAttributes(originalNode, forceDuplicate);
+ } else {
+ // user call cloneNode() or duplicateNode() directly
+ // instead of via cloneTree()
+ originalNode.nodeHashtable = new Hashtable();
+ duplicateAttributes(originalNode, forceDuplicate);
+ originalNode.nodeHashtable = null;
+ }
+ }
+
+
+ /**
+ * 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>
+ *
+ * @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 originalNode object is part of a live
+ * or compiled scenegraph.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+
+ if (originalNode.isLiveOrCompiled()) {
+ throw new RestrictedAccessException(J3dI18N.getString("Node13"));
+ }
+ super.duplicateSceneGraphObject(originalNode);
+ NodeRetained attr = (NodeRetained) originalNode.retained;
+ NodeRetained rt = (NodeRetained) retained;
+
+ rt.setPickable(attr.getPickable());
+ rt.setCollidable(attr.getCollidable());
+ }
+
+
+ /**
+ * When set to <code>true</code> this <code>Node</code> can be Picked.
+ * Setting to false indicates that this node and it's children
+ * are ALL unpickable.
+ *
+ * @param pickable Indicates if this node should be pickable or not
+ */
+ public void setPickable( boolean pickable ) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PICKABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node14"));
+
+ ((NodeRetained)retained).setPickable(pickable);
+ }
+
+ /**
+ * Returns true if this <code>Node</code> is pickable,
+ * false if it is not pickable.
+ */
+ public boolean getPickable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PICKABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Node3"));
+
+ return ((NodeRetained)retained).getPickable();
+ }
+
+ /**
+ * checks for cycles in the scene graph
+ */
+ void checkForCycle() {
+ if (visited) {
+ throw new SceneGraphCycleException(J3dI18N.getString("Node15"));
+ }
+ visited = true;
+ Node parent = getParent();
+ if (parent != null) {
+ parent.checkForCycle();
+ }
+ visited = false;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/NodeComponent.java b/src/classes/share/javax/media/j3d/NodeComponent.java
new file mode 100644
index 0000000..bd3779c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NodeComponent.java
@@ -0,0 +1,264 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.Hashtable;
+
+/**
+ * NodeComponent is a common superclass for all scene graph node
+ * component objects such as: Geometry, Appearance, Material, Texture, etc.
+ */
+public abstract class NodeComponent extends SceneGraphObject {
+
+ // This is use for cloneTree only, set to false after the operation
+ boolean forceDuplicate = false;
+ /**
+ * Constructs a NodeComponent object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * duplicate on clone tree : false<br>
+ * </ul>
+ */
+ public NodeComponent() {
+ }
+
+ /**
+ * Sets this node's duplicateOnCloneTree value. The
+ * <i>duplicateOnCloneTree</i> value is used to determine if NodeComponent
+ * objects are to be duplicated or referenced during a
+ * <code>cloneTree</code> operation. A value of <code>true</code> means
+ * that this NodeComponent object should be duplicated, while a value
+ * of <code>false</code> indicates that this NodeComponent object's
+ * reference will be copied into the newly cloned object. This value
+ * can be overriden via the <code>forceDuplicate</code> parameter of
+ * the <code>cloneTree</code> method.
+ * @param duplicate the value to set.
+ * @see Node#cloneTree
+ */
+ public void setDuplicateOnCloneTree(boolean duplicate) {
+ ((NodeComponentRetained)retained).setDuplicateOnCloneTree(duplicate);
+ }
+
+ /**
+ * Returns this node's duplicateOnCloneTree value. The
+ * <i>duplicateOnCloneTree</i> value is used to determine if NodeComponent
+ * objects are to be duplicated or referenced during a
+ * <code>cloneTree</code> operation. A value of <code>true</code> means
+ * that this NodeComponent object should be duplicated, while a value
+ * of <code>false</code> indicates that this NodeComponent object's
+ * reference will be copied into the newly cloned object. This value
+ * can be overriden via the <code>forceDuplicate</code> parameter of
+ * the <code>cloneTree</code> method.
+ * @return the value of this node's duplicateOnCloneTree
+ * @see Node#cloneTree
+ */
+ public boolean getDuplicateOnCloneTree() {
+ return ((NodeComponentRetained)retained).getDuplicateOnCloneTree();
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>cloneNodeComponent(boolean forceDuplicate)</code>
+ */
+ public NodeComponent cloneNodeComponent() {
+ throw new RuntimeException(J3dI18N.getString("NodeComponent0"));
+ }
+
+
+ /**
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>duplicateNodeComponent(NodeComponent
+ * originalNodeComponent, boolean forceDuplicate)</code>
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+ duplicateAttributes(originalNodeComponent,
+ originalNodeComponent.forceDuplicate);
+ }
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node component. This method is called from subclass of
+ * <code>duplicateNodeComponent</code> method which is, in turn, called by the
+ * <code>cloneNodeComponent</code> method.
+ *
+ * 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 originalNodeComponent the original node component to duplicate.
+ */
+ final void checkDuplicateNodeComponent(
+ NodeComponent originalNodeComponent) {
+
+ if (originalNodeComponent.nodeHashtable != null) {
+ duplicateAttributes(originalNodeComponent,
+ originalNodeComponent.forceDuplicate);
+ } else {
+ // user call cloneNodeComponent() or duplicateNodeComponent()
+ // directly instead of via cloneTree()
+ originalNodeComponent.nodeHashtable = new Hashtable();
+ duplicateAttributes(originalNodeComponent,
+ originalNodeComponent.forceDuplicate);
+ originalNodeComponent.nodeHashtable = null;
+ }
+ }
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code>
+ * into the current node. This method is called from the
+ * <code>cloneNodeComponent</code> method which is, in turn, called
+ * by the <code>cloneNode</code> method.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @param originalNodeComponent the 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 forceDuplicate is set and
+ * this object is part of a compiled scenegraph
+ *
+ * @see NodeComponent#cloneNodeComponent
+ * @see Node#cloneNode
+ * @see Node#cloneTree
+ *
+ * @since Java 3D 1.2
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ originalNodeComponent.forceDuplicate = forceDuplicate;
+ try {
+ duplicateNodeComponent(originalNodeComponent);
+ } catch (RuntimeException e) {
+ originalNodeComponent.forceDuplicate = false;
+ throw e;
+ }
+ originalNodeComponent.forceDuplicate = false;
+ }
+
+ /**
+ * Used to create a new instance of a NodeComponent object. This
+ * routine is called by <code>cloneNode</code> to duplicate the
+ * current node. <br>
+ *
+ * <code>cloneNodeComponent</code> should be overridden by any user
+ * subclassed <i>NodeComponent</i> objects. All subclasses must have their
+ * <code>cloneNodeComponent</code>
+ * method consist of the following lines:
+ * <P><blockquote><pre>
+ * public NodeComponent cloneNodeComponent(boolean forceDuplicate) {
+ * UserNodeComponent unc = new UserNodeComponent();
+ * unc.duplicateNodeComponent(this, forceDuplicate);
+ * return unc;
+ * }
+ * </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.
+ *
+ * @exception RestrictedAccessException if forceDuplicate is set and
+ * this object is part of a compiled scenegraph
+ *
+ * @see NodeComponent#duplicateNodeComponent
+ * @see Node#cloneNode
+ * @see Node#cloneTree
+ *
+ * @since Java 3D 1.2
+ */
+ public NodeComponent cloneNodeComponent(boolean forceDuplicate) {
+ // For backward compatibility !
+ //
+ // If user did not overwrite this procedure, it will fall back
+ // to call cloneNodeComponent()
+ // So for core API,
+ // don't implement cloneNodeComponent(boolean forceDuplicate)
+ // otherwise this prcedure will not call and the user
+ // cloneNodeComponent() will not invoke.
+ NodeComponent nc;
+ this.forceDuplicate = forceDuplicate;
+ try {
+ nc = cloneNodeComponent();
+ } catch (RuntimeException e) {
+ this.forceDuplicate = false;
+ throw e;
+ }
+ this.forceDuplicate = false;
+ return nc;
+ }
+
+
+ /**
+ * Copies all NodeComponent 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.
+ *
+ * @see Group#cloneNode
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNode,
+ boolean forceDuplicate) {
+
+ if (forceDuplicate && originalNode.isCompiled()) {
+ throw new RestrictedAccessException(
+ J3dI18N.getString("NodeComponent1"));
+ }
+
+ super.duplicateSceneGraphObject(originalNode);
+ setDuplicateOnCloneTree(originalNode.getDuplicateOnCloneTree());
+ }
+
+ /**
+ * Creates the retained mode NodeComponentRetained object that this
+ * NodeComponent object will point to.
+ */
+ void createRetained() {
+ this.retained = new NodeComponentRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * This function is called from getNodeComponent() to see if any of
+ * the sub-NodeComponents duplicateOnCloneTree flag is true.
+ * If it is the case, current NodeComponent needs to
+ * duplicate also even though current duplicateOnCloneTree flag is false.
+ * This should be overwrite by NodeComponent which contains sub-NodeComponent.
+ */
+ boolean duplicateChild() {
+ return getDuplicateOnCloneTree();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/NodeComponentRetained.java b/src/classes/share/javax/media/j3d/NodeComponentRetained.java
new file mode 100644
index 0000000..a7e6b8a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NodeComponentRetained.java
@@ -0,0 +1,241 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * Retained version of NodeComponent
+ */
+
+class NodeComponentRetained extends SceneGraphObjectRetained {
+
+ // duplicate or make a reference when cloneTree() is called
+ // on this object.
+ boolean duplicateOnCloneTree = false;
+
+ // This keeps track of how many times this NodeComponent is referenced in
+ // the Scene Graph
+ int refCount = 0; // this is used in setLive
+ int refCnt = 0; // this is used in compile
+
+ // This is true when this appearance is referenced in an immediate mode context
+ boolean inImmCtx = false;
+
+ // A list of NodeRetained Objects that refer, directly or indirectly, to this
+ // NodeComponentRetained
+ ArrayList users = new ArrayList(1);
+
+ // Mirror object of this node compoenent object
+ NodeComponentRetained mirror = null;
+
+ // Sole User FrequencyBit
+ // In the case of Appearance, its a bitmask of all components
+ int changedFrequent = 0;
+ int compChanged = 0;
+
+ // Increment the refcount. If this is the first, mark it as live.
+ void doSetLive(boolean inBackgroundGroup, int refCount) {
+ int oldRefCount = this.refCount;
+ this.refCount += refCount;
+ if (oldRefCount <= 0) {
+ super.doSetLive(inBackgroundGroup);
+
+ // Create and init a mirror object if not already there
+ // The two procedures is combined since it is redunctant to
+ // call initMirrorObject() if mirror == this (static object).
+ createMirrorObject();
+ }
+ }
+
+ void setLive(boolean inBackgroundGroup, int refCount) {
+ int oldRefCount = this.refCount;
+ doSetLive(inBackgroundGroup, refCount);
+ if (oldRefCount <= 0) {
+ super.markAsLive();
+ }
+ }
+
+
+
+ // Decrement the refcount. If this is the last, mark it as not live.
+ void clearLive(int refCount) {
+ this.refCount -= refCount;
+
+ if (this.refCount <= 0) {
+ super.clearLive();
+ }
+ }
+
+ // increment the compile reference count
+ synchronized void incRefCnt() {
+ refCnt++;
+ }
+
+ // decrement the compile reference count
+ synchronized void decRefCnt() {
+ refCnt--;
+ }
+
+ // remove mirror shape from the list of users
+ void removeAMirrorUser(Shape3DRetained ms) {
+ synchronized(mirror.users) {
+ mirror.users.remove(ms);
+ }
+ }
+
+ // Add a mirror shape to the list of users
+ void addAMirrorUser(Shape3DRetained ms) {
+ synchronized(mirror.users) {
+ mirror.users.add(ms);
+ }
+ }
+
+ // Copy the list of useres passed in into this
+ void copyMirrorUsers(NodeComponentRetained node) {
+ synchronized(mirror.users) {
+ synchronized(node.mirror.users) {
+ int size = node.mirror.users.size();
+ for (int i=0; i<size ; i++) {
+ mirror.users.add(node.mirror.users.get(i));
+ }
+ }
+ }
+ }
+
+
+ // Remove the users of "node" from "this" node compoenent
+ void removeMirrorUsers(NodeComponentRetained node) {
+
+ synchronized(mirror.users) {
+ synchronized(node.mirror.users) {
+ for (int i=node.mirror.users.size()-1; i>=0; i--) {
+ mirror.users.remove(mirror.users.indexOf(node.mirror.users.get(i)));
+ }
+ }
+ }
+ }
+
+ // Add a user to the list of users
+ synchronized void removeUser(NodeRetained node) {
+ if (node.source.isLive())
+ users.remove(users.indexOf(node));
+ }
+
+ // Add a user to the list of users
+ synchronized void addUser(NodeRetained node) {
+ if (node.source.isLive())
+ users.add(node);
+ }
+
+
+ // Add a user to the list of users
+ synchronized void notifyUsers() {
+
+ if (source == null || !source.isLive()) {
+ return;
+ }
+
+ for (int i=users.size()-1; i >=0; i--) {
+ ((NodeRetained)users.get(i)).notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+ /**
+ * Sets this node's duplicateOnCloneTree value. The
+ * <i>duplicateOnCloneTree</i> value is used to determine if NodeComponent
+ * objects are to be duplicated or referenced during a
+ * <code>cloneTree</code> operation. A value of <code>true</code> means
+ * that this NodeComponent object should be duplicated, while a value
+ * of <code>false</code> indicates that this NodeComponent object's
+ * reference will be copied into the newly cloned object. This value
+ * can be overriden via the <code>forceDuplicate</code> parameter of
+ * the <code>cloneTree</code> method.
+ * @param duplicate the value to set.
+ * @see Node#cloneTree
+ */
+ void setDuplicateOnCloneTree(boolean duplicate) {
+ duplicateOnCloneTree = duplicate;
+ }
+
+ /**
+ * Returns this node's duplicateOnCloneTree value. The
+ * <i>duplicateOnCloneTree</i> value is used to determine if NodeComponent
+ * objects are to be duplicated or referenced during a
+ * <code>cloneTree</code> operation. A value of <code>true</code> means
+ * that this NodeComponent object should be duplicated, while a value
+ * of <code>false</code> indicates that this NodeComponent object's
+ * reference will be copied into the newly cloned object. This value
+ * can be overriden via the <code>forceDuplicate</code> parameter of
+ * the <code>cloneTree</code> method.
+ * @return the value of this node's duplicateOnCloneTree
+ * @see Node#cloneTree
+ */
+ boolean getDuplicateOnCloneTree() {
+ return duplicateOnCloneTree;
+ }
+
+
+ void initMirrorObject() {
+ }
+
+ void updateMirrorObject(int component, Object obj) {
+ }
+
+ void createMirrorObject() {
+ // Overridden by appearance and other classes
+ initMirrorObject();
+ mirror = null;
+ }
+
+ // Evaluate state based on the following extensions
+ void evaluateExtensions(int extensions) {
+ }
+
+
+
+
+ void setFrequencyChangeMask(int bit, int mask) {
+ // Record only the inf->frequent change
+ if (source.getCapabilityIsFrequent(bit))
+ changedFrequent |= mask;
+ else if (!source.isLive()) {
+ changedFrequent &= ~mask;
+ }
+
+ }
+
+ protected Object clone() {
+ NodeComponentRetained ncr = (NodeComponentRetained)super.clone();
+ ncr.changedFrequent = changedFrequent;
+ return ncr;
+ }
+
+ protected void set(NodeComponentRetained nc) {
+ changedFrequent = nc.changedFrequent;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/NodeComponentUpdate.java b/src/classes/share/javax/media/j3d/NodeComponentUpdate.java
new file mode 100644
index 0000000..b0aba77
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NodeComponentUpdate.java
@@ -0,0 +1,26 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A Node Component Update interface. Any object that can be put in the
+ * node component updateCheck list must implement this interface.
+ */
+
+interface NodeComponentUpdate {
+
+ /**
+ * The actual update function.
+ */
+ abstract void updateNodeComponentCheck();
+}
diff --git a/src/classes/share/javax/media/j3d/NodeData.java b/src/classes/share/javax/media/j3d/NodeData.java
new file mode 100644
index 0000000..930d7a3
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NodeData.java
@@ -0,0 +1,21 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+class NodeData {
+ // per path node data
+ // TODO: replace per path mirror objects with node data
+ // TODO: move other basic node's data here
+ SwitchState switchState = null;
+}
diff --git a/src/classes/share/javax/media/j3d/NodeReferenceTable.java b/src/classes/share/javax/media/j3d/NodeReferenceTable.java
new file mode 100644
index 0000000..b425f2e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NodeReferenceTable.java
@@ -0,0 +1,121 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+
+/**
+ * The NodeReferenceTable object is used by a leaf node's
+ * <code>updateNodeReferences</code> method called by the
+ * <code>cloneTree</code> method.
+ * The NodeReferenceTable maps nodes from the original subgraph
+ * to the new nodes in the cloned subgraph. This information
+ * can then be used to update any cloned leaf node references
+ * to reference nodes in the cloned subgraph.
+ * <P>
+ * During a <code>cloneTree</code> call, after all nodes have been duplicated,
+ * each SceneGraphObject's <code>updateNodeReferences</code> method is called.
+ * This method takes a NodeReferenceTable object as a parameter. The
+ * SceneGraphObject's <code>updateNodeReferences</code> method can then use the
+ * <code>getNewObjectReference</code> method from this object to get updated
+ * references to objects that have been duplicated in the new cloneTree
+ * sub-graph. If a match is found, a
+ * reference to the corresponding SceneGraphObject in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown by the <code>cloneTree</code>
+ * method or a reference to the original
+ * SceneGraphObject is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * @see SceneGraphObject#updateNodeReferences
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+public class NodeReferenceTable extends Object {
+
+ Hashtable objectHashtable;
+ boolean allowDanglingReferences;
+
+ /**
+ * Constructs an empty NodeReferenceTable.
+ * @since Java 3D 1.2
+ */
+ public NodeReferenceTable() {
+ }
+
+ // Constructor - this is NOT public!
+ NodeReferenceTable(boolean allowDanglingReferences,
+ Hashtable objectHashtable) {
+ set(allowDanglingReferences, objectHashtable);
+ }
+
+ void set(boolean allowDanglingReferences,
+ Hashtable objectHashtable) {
+ this.objectHashtable = objectHashtable;
+ this.allowDanglingReferences = allowDanglingReferences;
+ }
+
+
+ /**
+ * This method is used in conjunction with the <code>cloneTree</code>
+ * method. It can be used by the <code>updateNodeReferences</code>
+ * method to see if a SceneGraphObject that is being referenced has been duplicated
+ * in the new cloneTree sub-graph.
+ * <P>
+ * A SceneGraphObject's <code>updateNodeReferences</code> method would use this
+ * method by calling it with the reference to the old (existed before
+ * the cloneTree operation) object. If the object has been duplicated
+ * in the cloneTree sub-graph, the corresponding object in the cloned
+ * sub-graph is returned. If no corresponding reference is found, either
+ * a DanglingReferenceException is thrown or a reference to the original
+ * SceneGraphObject is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ *
+ * @param oldReference the reference to the object in
+ * the original sub-graph.
+ *
+ * @return A reference to the corresponding object in the cloned
+ * sub-graph. If no corresponding object exists, either a
+ * DanglingReferenceException will be generated by the
+ * <code>cloneTree</code> method or a reference to the original object
+ * is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ *
+ * @see SceneGraphObject#updateNodeReferences
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public final SceneGraphObject
+ getNewObjectReference(SceneGraphObject oldReference) {
+
+ // look up original SceneGraphObject in hash table
+ SceneGraphObject newObject =
+ (SceneGraphObject)objectHashtable.get(oldReference);
+
+ if (newObject != null) {
+ // found new reference
+ return newObject;
+ }
+
+ // dangling reference found!
+ if (allowDanglingReferences == true) {
+ return oldReference;
+ }
+
+ // dangling references not allowed
+ throw new DanglingReferenceException();
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/NodeRetained.java b/src/classes/share/javax/media/j3d/NodeRetained.java
new file mode 100644
index 0000000..3ddf7eb
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NodeRetained.java
@@ -0,0 +1,914 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.ArrayList;
+
+/**
+ * The Node class provides an abstract class for all Group and Leaf
+ * Nodes. It provides a common framework for constructing a Java 3D
+ * scene graph, including bounding volumes and parent pointers.
+ */
+abstract class NodeRetained extends SceneGraphObjectRetained implements NnuId {
+
+ // All the node types in the scene graph
+ static final int BACKGROUND = 1;
+ static final int CLIP = 2;
+ static final int LINEARFOG = 3;
+ static final int EXPONENTIALFOG = 4;
+ static final int AMBIENTLIGHT = 5;
+ static final int DIRECTIONALLIGHT = 6;
+ static final int POINTLIGHT = 7;
+ static final int SPOTLIGHT = 8;
+ static final int LINK = 9;
+ static final int MORPH = 10;
+ static final int SHAPE = 11;
+ static final int BACKGROUNDSOUND = 12;
+ static final int POINTSOUND = 13;
+ static final int CONESOUND = 14;
+ static final int SOUNDSCAPE = 15;
+ static final int VIEWPLATFORM = 16;
+ static final int BEHAVIOR = 17;
+
+ static final int SWITCH = 18;
+ static final int BRANCHGROUP = 19;
+ static final int ORDEREDGROUP = 20;
+ static final int DECALGROUP = 21;
+ static final int SHAREDGROUP = 22;
+ static final int GROUP = 23;
+ static final int TRANSFORMGROUP = 24;
+ static final int BOUNDINGLEAF = 25;
+ static final int MODELCLIP = 26;
+ static final int ALTERNATEAPPEARANCE= 27;
+ static final int ORIENTEDSHAPE3D = 28;
+ static final int VIEWSPECIFICGROUP = 29;
+ static final int NUMNODES = 29;
+
+ // traverse flags
+ static final int CONTAINS_VIEWPLATFORM = 0x1;
+
+
+ /**
+ * The universe that we are in
+ */
+ VirtualUniverse universe = null;
+
+ /**
+ * The locale that this node is attatched to. This is only non-null
+ * if this instance is directly linked into a locale.
+ */
+ Locale locale = null;
+
+ /**
+ * The node's parent.
+ */
+ NodeRetained parent = null;
+
+ /**
+ * The node's internal identifier.
+ */
+ String nodeId = null;
+
+ /**
+ * An int that represents the nodes type. Used for quick if tests
+ * in the traverser.
+ */
+ int nodeType;
+
+ // This keeps track of how many times this Node is refernced, refCount > 1
+ // if node is in a shared group
+ int refCount = 0;
+
+ /**
+ * This is the index for the child, as seen by its parent.
+ */
+ int childIndex = -1;
+
+ /**
+ * This boolean is true when the node is in a sharedGroup
+ */
+ boolean inSharedGroup = false;
+
+ /**
+ * This indicates if the node is pickable. If this node is not
+ * pickable then neither are any children
+ */
+ boolean pickable = true;
+
+ /**
+ * The collidable setting; see getCollidable and setCollidable.
+ */
+ boolean collidable = true;
+
+ // A list of localToVworld transforms. If inSharedGroup is false,
+ // then only localToVworld[0][] is valid.
+ // Note: this contains reference to the actual transforms in the
+ // TransformGroupRetained
+ Transform3D localToVworld[][] = null;
+ int localToVworldIndex[][] = null;
+
+ static final int LAST_LOCAL_TO_VWORLD = 0;
+ static final int CURRENT_LOCAL_TO_VWORLD = 1;
+
+ // A parallel array to localToVworld. This is the keys for
+ // localToVworld transforms in shared groups.
+ HashKey localToVworldKeys[] = null;
+
+ /**
+ * This boolean is true when the geometric bounds for the node is
+ * automatically updated
+ */
+ boolean boundsAutoCompute = true;
+
+ // "effective" bounds in local coordinate if boundsAutoCompute == F,
+ // used for internal operations, not used if boundsAutoCompute == T
+ Bounds localBounds;
+
+ // Bounds set by the API
+ Bounds apiBounds;
+
+ /**
+ * Each element, p, of branchGroupPaths is a list of BranchGroup from
+ * root of the tree to this.
+ * For BranchGroup under a non-shared group this size of
+ * branchGroupPaths is always 1. Otherwise, the size is equal to
+ * the number of possible paths to reach this node.
+ * This variable is used to cached BranchGroup for fast picking.
+ * For non BranchGroupRetained class this is a reference to
+ * the previous BranchGroupRetained branchGroupPaths.
+ */
+ ArrayList branchGroupPaths = new ArrayList(1);
+
+ // background node whose geometry branch contains this node
+ BackgroundRetained geometryBackground = null;
+
+ // closest parent which is a TransformGroupRetained or sharedGroupRetained
+ GroupRetained parentTransformLink = null;
+
+ // closest parent which is a SwitchRetained or sharedGroupRetained
+ GroupRetained parentSwitchLink = null;
+
+ // static transform if a parent transform group is merged during compile.
+ TransformGroupRetained staticTransform = null;
+
+ // orderedId assigned by OrderedGroup parent
+ Integer orderedId = null;
+
+ // Id use for quick search.
+ int nnuId;
+
+ NodeRetained() {
+ // Get a not necessary unique Id.
+ nnuId = NnuIdManager.getId();
+
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setUpper(-1.0, -1.0, -1.0);
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ }
+
+
+ public int getId() {
+ return nnuId;
+ }
+
+ public int equal(NnuId obj) {
+ int keyId = obj.getId();
+ if(nnuId < keyId) {
+ return -1;
+ }
+ else if(nnuId > keyId) {
+ return 1;
+ }
+ else { // Found it!
+ return 0;
+ }
+ }
+
+ Bounds getLocalBounds(Bounds bounds) {
+ return (Bounds)bounds.clone();
+ }
+
+ /**
+ * Sets the geometric bounds of a node.
+ * @param bounds the bounding object for the node
+ */
+ void setBounds(Bounds bounds) {
+ apiBounds = bounds;
+ if (source.isLive()) {
+ if (!boundsAutoCompute) {
+ if (bounds != null) {
+ localBounds = getLocalBounds(bounds);
+ if (staticTransform != null) {
+ localBounds.transform(staticTransform.transform);
+ }
+ } else {
+ if(localBounds != null) {
+ localBounds.set((Bounds)null);
+ }
+ else {
+ localBounds = new BoundingBox((Bounds)null);
+ }
+ }
+ }
+ } else {
+ if (bounds != null) {
+ localBounds = getLocalBounds(bounds);
+ if (staticTransform != null) {
+ localBounds.transform(staticTransform.transform);
+ }
+ } else {
+ if(localBounds != null) {
+ localBounds.set((Bounds)null);
+ }
+ else {
+ localBounds = new BoundingBox((Bounds)null);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the bounding object of a node.
+ * @return the node's bounding object
+ */
+ Bounds getEffectiveBounds() {
+ Bounds b = null;
+ if (localBounds != null && !localBounds.isEmpty()) {
+ b = (Bounds) localBounds.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+ Bounds getBounds() {
+ return apiBounds;
+ }
+
+ /**
+ * ONLY needed for SHAPE, MORPH, and LINK node type.
+ * Compute the combine bounds of bounds and its localBounds.
+ */
+ void computeCombineBounds(Bounds bounds) {
+ // Do nothing except for Group, Shape3D, Morph, and Link node.
+ }
+
+
+ /**
+ * Sets the automatic calcuation of geometric bounds of a node.
+ * @param autoCompute is a boolean value indicating if automatic calcuation
+ * of bounds
+ */
+ void setBoundsAutoCompute(boolean autoCompute) {
+ this.boundsAutoCompute = autoCompute;
+ }
+
+ /**
+ * Gets the auto Compute flag for the geometric bounds.
+ * @return the node's auto Compute flag for the geometric bounding object
+ */
+ boolean getBoundsAutoCompute() {
+ return boundsAutoCompute;
+ }
+
+ /**
+ * Replaces the specified parent by a new parent.
+ * @param parent the new parent
+ */
+ void setParent(NodeRetained parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Returns the parent of the node.
+ * @return the parent.
+ */
+ NodeRetained getParent() {
+ return (NodeRetained)parent;
+ }
+
+ // Transform the input bound by the current LocalToVWorld
+ void transformBounds(SceneGraphPath path, Bounds bound) {
+ if (!((NodeRetained) path.item.retained).inSharedGroup) {
+ bound.transform(getCurrentLocalToVworld());
+ } else {
+ HashKey key = new HashKey("");
+ path.getHashKey(key);
+ bound.transform(getCurrentLocalToVworld(key));
+ }
+ }
+
+
+ // Note : key will get modified in this method.
+ private void computeLocalToVworld( NodeRetained caller, NodeRetained nodeR,
+ HashKey key, Transform3D l2Vw) {
+ int i;
+
+ // To handle localToVworld under a SG.
+ if(nodeR instanceof SharedGroupRetained) {
+ // Get the immediate parent's id and remove last id from key.
+ String nodeId = key.getLastNodeId();
+
+ SharedGroupRetained sgRetained = (SharedGroupRetained) nodeR;
+
+ // Search for the right parent.
+ for(i=0; i<sgRetained.parents.size(); i++) {
+
+ if(nodeId.equals((String)(((NodeRetained)
+ (sgRetained.parents.elementAt(i))).nodeId))) {
+ // Found the right link. Now traverse upward.
+
+ computeLocalToVworld(caller,
+ (NodeRetained)(sgRetained.parents.elementAt(i)),
+ key, l2Vw);
+ return;
+ }
+ }
+ // Problem !
+ throw new RuntimeException(J3dI18N.getString("NodeRetained4"));
+ }
+ else {
+
+ NodeRetained nodeParentR =(NodeRetained)nodeR.getParent();
+
+ if(nodeParentR == null) {
+ // Base case. It has to be a BG attached to a locale.
+ if(((BranchGroupRetained)(nodeR)).locale != null) {
+ l2Vw.setIdentity();
+ }
+ else {
+ throw new RuntimeException(J3dI18N.getString("NodeRetained5"));
+ }
+ }
+ else {
+ computeLocalToVworld(caller, (NodeRetained)nodeParentR, key, l2Vw);
+
+ }
+
+ }
+
+ if((nodeR instanceof TransformGroupRetained) && (nodeR != caller)) {
+ Transform3D t1 = VirtualUniverse.mc.getTransform3D(null);
+ ((TransformGroupRetained)(nodeR)).transform.getWithLock(t1);
+ l2Vw.mul(t1);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t1);
+ } else if ((nodeR == caller) && (staticTransform != null)) {
+ l2Vw.mul(staticTransform.transform);
+ }
+
+ return;
+ }
+
+
+ /**
+ * Get the localToVworld transform for a node.
+ */
+ void getLocalToVworld(Transform3D t) {
+ if (inSharedGroup) {
+ throw new IllegalSharingException(J3dI18N.getString("NodeRetained0"));
+ }
+
+ // Lock the object while writing into t.
+ if (localToVworld == null) {
+ t.setIdentity();
+ } else {
+ computeLocalToVworld(this, this, null, t);
+ }
+ }
+
+
+ /**
+ * Get the localToVworld transform for a node.
+ */
+ void getLocalToVworld(SceneGraphPath path, Transform3D t) {
+ HashKey key = new HashKey("");
+
+ if (inSharedGroup == false) {
+ throw new IllegalSharingException(J3dI18N.getString("NodeRetained1"));
+ }
+ path.validate(key);
+ computeLocalToVworld(this, this, key, t);
+
+ }
+
+ /**
+ * Get the localToVworld transform for a node
+ */
+ void getLocalToVworld(Transform3D t, HashKey key) {
+ HashKey newKey = new HashKey(key);
+ computeLocalToVworld(this, this, newKey, t);
+ }
+
+
+ /**
+ * Get the current localToVworld transform for a node
+ */
+ Transform3D getCurrentLocalToVworld() {
+
+ if (localToVworld != null) {
+ return localToVworld[0][localToVworldIndex[0][CURRENT_LOCAL_TO_VWORLD]];
+ } else {
+ return new Transform3D();
+ }
+ }
+
+ // this method expects that localToVworld is not null
+
+ Transform3D getCurrentLocalToVworld(int index) {
+ return localToVworld[index][localToVworldIndex[index][CURRENT_LOCAL_TO_VWORLD]];
+ }
+
+ Transform3D getCurrentLocalToVworld(HashKey key) {
+
+ if (localToVworld != null) {
+ if (!inSharedGroup) {
+ return localToVworld[0][localToVworldIndex[0][CURRENT_LOCAL_TO_VWORLD]];
+ } else {
+ int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ if(i>= 0) {
+ return localToVworld[i][localToVworldIndex[i][CURRENT_LOCAL_TO_VWORLD]];
+ }
+ }
+ }
+ return new Transform3D();
+ }
+
+ /**
+ * Get the last localToVworld transform for a node
+ */
+ Transform3D getLastLocalToVworld() {
+
+ if (localToVworld != null) {
+ return localToVworld[0][localToVworldIndex[0][LAST_LOCAL_TO_VWORLD]];
+ } else {
+ return new Transform3D();
+ }
+ }
+
+ Transform3D getLastLocalToVworld(int index) {
+ return localToVworld[index][localToVworldIndex[index][LAST_LOCAL_TO_VWORLD]];
+ }
+
+ Transform3D getLastLocalToVworld(HashKey key) {
+
+ if (localToVworld != null) {
+ if (!inSharedGroup) {
+ return localToVworld[0][localToVworldIndex[0][LAST_LOCAL_TO_VWORLD]];
+ } else {
+ int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ if(i>= 0) {
+ return localToVworld[i][localToVworldIndex[i][LAST_LOCAL_TO_VWORLD]];
+ }
+ }
+ }
+ return new Transform3D();
+ }
+
+ // Do nothing for NodeRetained.
+ void setAuxData(SetLiveState s, int index, int hkIndex) {
+
+ }
+
+ void setNodeData(SetLiveState s) {
+ localToVworld = s.localToVworld;
+ localToVworldIndex = s.localToVworldIndex;
+ localToVworldKeys = s.localToVworldKeys;
+
+ // reference to the last branchGroupPaths
+ branchGroupPaths = s.parentBranchGroupPaths;
+
+ parentTransformLink = s.parentTransformLink;
+ parentSwitchLink = s.parentSwitchLink;
+ }
+
+
+ // set pickable, recursively update cache result
+ void setPickable(boolean pickable) {
+ if (this.pickable == pickable)
+ return;
+
+ this.pickable = pickable;
+
+ if (source.isLive()) {
+ synchronized(universe.sceneGraphLock) {
+ boolean pick[];
+ if (!inSharedGroup) {
+ pick = new boolean[1];
+ } else {
+ pick = new boolean[localToVworldKeys.length];
+ }
+
+ findPickableFlags(pick);
+ updatePickable(localToVworldKeys, pick);
+ }
+ }
+ }
+
+ void updatePickable(HashKey pickKeys[], boolean pick[]) {
+ for (int i=0; i < pick.length; i++) {
+ if (!pickable) {
+ pick[i] = false;
+ }
+ }
+ }
+
+ // get pickable
+ boolean getPickable() {
+ return pickable;
+ }
+
+
+ // set collidable, recursively update cache result
+ void setCollidable(boolean collidable) {
+ if (this.collidable == collidable)
+ return;
+
+ this.collidable = collidable;
+
+ if (source.isLive()) {
+ synchronized(universe.sceneGraphLock) {
+ boolean collide[];
+ if (!inSharedGroup) {
+ collide = new boolean[1];
+ } else {
+ collide = new boolean[localToVworldKeys.length];
+ }
+
+ findCollidableFlags(collide);
+ updateCollidable(localToVworldKeys, collide);
+ }
+ }
+ }
+
+
+ // get collidable
+ boolean getCollidable() {
+ return collidable;
+ }
+
+
+ void updateCollidable(HashKey keys[], boolean collide[]) {
+ for (int i=0; i < collide.length; i++) {
+ if (!collidable) {
+ collide[i] = false;
+ }
+ }
+ }
+
+ /**
+ * For the default, just pass up to parent
+ */
+ void notifySceneGraphChanged(boolean globalTraverse){}
+
+ void recombineAbove() {}
+
+ synchronized void updateLocalToVworld() {}
+
+
+ void setLive(SetLiveState s) {
+ int oldrefCount = refCount;
+
+ doSetLive(s);
+ if (oldrefCount <= 0)
+ super.markAsLive();
+ }
+
+ // The default set of setLive actions.
+ void doSetLive(SetLiveState s) {
+ int i;
+ int oldrefCount = refCount;
+
+ refCount += s.refCount;
+ if(!(locale == null || universe == s.universe))
+ throw new IllegalSharingException(J3dI18N.getString("NodeRetained3"));
+ if(s.locale == null)
+ System.out.println("NodeRetained.setLive() locale is null");
+
+
+ locale = s.locale;
+ inSharedGroup = s.inSharedGroup;
+
+ if (oldrefCount <= 0) {
+ if (listIdx == null) {
+ universe = s.universe;
+ } else {
+ // sync with getIdxUsed()
+ if (s.universe != universe) {
+ synchronized (this) {
+ universe = s.universe;
+ incIdxUsed();
+ }
+ }
+ }
+ }
+ s.universe.numNodes++;
+
+ // pickable & collidable array have the same length
+ for (i=0; i < s.pickable.length; i++) {
+ if (!pickable) {
+ s.pickable[i] = false;
+ }
+ if (!collidable) {
+ s.collidable[i] = false;
+ }
+ }
+
+
+ if (oldrefCount <= 0)
+ super.doSetLive(s);
+
+ if (inBackgroundGroup) {
+ geometryBackground = s.geometryBackground;
+ }
+
+ setNodeData(s);
+ }
+
+
+ /**
+ * remove the localToVworld transform for this node.
+ */
+ void removeNodeData(SetLiveState s) {
+
+ if (refCount <= 0) {
+ localToVworld = null;
+ localToVworldIndex = null;
+ localToVworldKeys = null;
+ // restore to default and avoid calling clear()
+ // that may clear parent reference branchGroupPaths
+ branchGroupPaths = new ArrayList(1);
+ parentTransformLink = null;
+ parentSwitchLink = null;
+ }
+ else {
+ // Set it back to its parent localToVworld data. This is b/c the parent has
+ // changed it localToVworld data arrays.
+ localToVworld = s.localToVworld;
+ localToVworldIndex = s.localToVworldIndex;
+ localToVworldKeys = s.localToVworldKeys;
+
+ // Reference of parent branchGroupPaths will not change
+
+ // no need to reset parentSwitchLink or parentTransformLink
+ // because there are not per path data
+ }
+
+ }
+
+ // The default set of clearLive actions
+ void clearLive(SetLiveState s) {
+
+ refCount-=s.refCount;
+
+ if (refCount <= 0) {
+ super.clearLive();
+
+ // don't remove the nodeId unless there are no more references
+ if (nodeId != null) {
+ universe.nodeIdFreeList.addElement(nodeId);
+ nodeId = null;
+ }
+ }
+
+ universe.numNodes--;
+
+
+ removeNodeData(s);
+
+ if(refCount <= 0) {
+ locale = null;
+ geometryBackground = null;
+ }
+ }
+
+ // search up the parent to determine if this node is pickable
+ void findPickableFlags(boolean pick[]) {
+ NodeRetained nodeR = this;
+
+
+ if (!inSharedGroup) {
+ pick[0] = true;
+ nodeR = nodeR.parent;
+ while (nodeR != null) {
+ if (!nodeR.pickable) {
+ pick[0] = false;
+ break;
+ }
+ nodeR = nodeR.parent;
+ }
+ } else {
+ HashKey key;
+ for (int i=0; i < pick.length; i++) {
+ nodeR = this;
+ pick[i] = true;
+ key = new HashKey(localToVworldKeys[i]);
+
+ do {
+ if (nodeR instanceof SharedGroupRetained) {
+ String nodeId = key.getLastNodeId();
+ Vector parents = ((SharedGroupRetained) nodeR).parents;
+ int sz = parents.size();
+ NodeRetained prevNodeR = nodeR;
+ for(int j=0; j< sz; j++) {
+ NodeRetained linkR = (NodeRetained) parents.elementAt(j);
+ if (linkR.nodeId.equals(nodeId)) {
+ nodeR = linkR;
+ break;
+ }
+ }
+ if (prevNodeR == nodeR) {
+ // branch is already detach
+ return;
+ }
+ } else {
+ nodeR = nodeR.parent;
+ }
+ if (nodeR == null)
+ break;
+ if (!nodeR.pickable) {
+ pick[i] = false;
+ break;
+ }
+ } while (true);
+ }
+ }
+ }
+
+
+ // search up the parent to determine if this node is collidable
+ void findCollidableFlags(boolean collide[]) {
+ NodeRetained nodeR = this;
+
+ if (!inSharedGroup) {
+ collide[0] = true;
+ nodeR = nodeR.parent;
+ while (nodeR != null) {
+ if (!nodeR.collidable) {
+ collide[0] = false;
+ break;
+ }
+ nodeR = nodeR.parent;
+ }
+ } else {
+ HashKey key;
+ for (int i=0; i < collide.length; i++) {
+ nodeR = this;
+ collide[i] = true;
+ key = new HashKey(localToVworldKeys[i]);
+
+ do {
+ if (nodeR instanceof SharedGroupRetained) {
+ String nodeId = key.getLastNodeId();
+ Vector parents = ((SharedGroupRetained) nodeR).parents;
+ int sz = parents.size();
+ NodeRetained prevNodeR = nodeR;
+ for(int j=0; j< sz; j++) {
+ NodeRetained linkR = (NodeRetained) parents.elementAt(j);
+ if (linkR.nodeId.equals(nodeId)) {
+ nodeR = linkR;
+ break;
+ }
+ }
+ if (nodeR == prevNodeR) {
+ return;
+ }
+ } else {
+ nodeR = nodeR.parent;
+ }
+ if (nodeR == null)
+ break;
+ if (!nodeR.collidable) {
+ collide[i] = false;
+ break;
+ }
+ } while (true);
+ }
+ }
+ }
+
+ void findTransformLevels(int transformLevels[]) {
+ NodeRetained nodeR = this;
+ TransformGroupRetained tg;
+
+ if (!inSharedGroup) {
+ transformLevels[0] = -1;
+ while (nodeR != null) {
+ if (nodeR.nodeType == NodeRetained.TRANSFORMGROUP) {
+ tg = (TransformGroupRetained)nodeR;
+ transformLevels[0] = tg.transformLevels[0];
+ break;
+ }
+ nodeR = nodeR.parent;
+ }
+ } else {
+ HashKey key;
+ int i,j;
+ for (i=0; i < transformLevels.length; i++) {
+ nodeR = this;
+ transformLevels[i] = -1;
+ key = new HashKey(localToVworldKeys[i]);
+
+ do {
+ if (nodeR == null)
+ break;
+ else if (nodeR instanceof SharedGroupRetained) {
+ // note that key is truncated after getLastNodeId
+ String nodeId = key.getLastNodeId();
+ Vector parents = ((SharedGroupRetained) nodeR).parents;
+ int sz = parents.size();
+ NodeRetained prevNodeR = nodeR;
+ for (j=0; j< sz; j++) {
+ NodeRetained linkR =
+ (NodeRetained) parents.elementAt(j);
+ if (linkR.nodeId.equals(nodeId)) {
+ nodeR = linkR;
+ break;
+ }
+ }
+ if (prevNodeR == nodeR) {
+ // branch is already detach
+ return;
+ }
+ }
+ else if (nodeR.nodeType == NodeRetained.TRANSFORMGROUP) {
+ tg = (TransformGroupRetained)nodeR;
+ if (tg.inSharedGroup) {
+
+ j = key.equals(tg.localToVworldKeys, 0,
+ tg.localToVworldKeys.length);
+
+ transformLevels[i] = tg.transformLevels[j];
+ } else {
+ transformLevels[i] = tg.transformLevels[0];
+ }
+ break;
+ }
+
+ nodeR = nodeR.parent;
+ } while (true);
+ }
+ }
+ }
+
+
+ boolean isStatic() {
+ if (source.getCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ) ||
+ source.getCapability(Node.ENABLE_PICK_REPORTING) ||
+ source.getCapability(Node.ENABLE_COLLISION_REPORTING) ||
+ source.getCapability(Node.ALLOW_BOUNDS_READ) ||
+ source.getCapability(Node.ALLOW_BOUNDS_WRITE) ||
+ source.getCapability(Node.ALLOW_PICKABLE_READ) ||
+ source.getCapability(Node.ALLOW_PICKABLE_WRITE) ||
+ source.getCapability(Node.ALLOW_COLLIDABLE_READ) ||
+ source.getCapability(Node.ALLOW_COLLIDABLE_WRITE) ||
+ source.getCapability(Node.ALLOW_AUTO_COMPUTE_BOUNDS_READ) ||
+ source.getCapability(Node.ALLOW_AUTO_COMPUTE_BOUNDS_WRITE)) {
+ return false;
+ }
+ return true;
+ }
+
+ void merge(CompileState compState) {
+ staticTransform = compState.staticTransform;
+ if (compState.parentGroup != null) {
+ compState.parentGroup.compiledChildrenList.add(this);
+ }
+ parent = compState.parentGroup;
+ if (staticTransform != null) {
+ mergeTransform(staticTransform);
+ }
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ if (localBounds != null) {
+ localBounds.transform(xform.transform);
+ }
+ }
+ int[] processViewSpecificInfo(int mode, HashKey k, View v, ArrayList vsgList, int[] keyList,
+ ArrayList leafList) {
+ return keyList;
+
+ }
+
+ VirtualUniverse getVirtualUniverse() {
+ return universe;
+ }
+
+ void searchGeometryAtoms(UnorderList list) {}
+}
+
diff --git a/src/classes/share/javax/media/j3d/ObjectUpdate.java b/src/classes/share/javax/media/j3d/ObjectUpdate.java
new file mode 100644
index 0000000..eb109b6
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ObjectUpdate.java
@@ -0,0 +1,26 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/*
+ * A Object Update interface. Any object that can be put in the ObjectUpdate list
+ * must implement this interface.
+ */
+
+interface ObjectUpdate {
+
+ /**
+ * The actual update function.
+ */
+ abstract void updateObject();
+}
diff --git a/src/classes/share/javax/media/j3d/OrderedBin.java b/src/classes/share/javax/media/j3d/OrderedBin.java
new file mode 100644
index 0000000..b38d4b1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrderedBin.java
@@ -0,0 +1,109 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * An OrderedBin contains an array of OrderedCollection, each represents
+ * a child of the OrderedGroup
+ */
+class OrderedBin extends Object {
+ // ArrayList of orderedCollection, one for each child of the orderedGroup
+ ArrayList orderedCollections = new ArrayList();
+
+ // orderedGroup source
+ OrderedGroupRetained source;
+ OrderedChildInfo childInfoList= null;
+ OrderedChildInfo lastChildInfo = null;
+
+ boolean onUpdateList = false;
+
+ // Value of already existing orderedCollection
+ ArrayList setOCForCI = new ArrayList();
+ ArrayList valueOfSetOCForCI = new ArrayList();
+
+ // Value of orderedCollection based on oi, these arrays
+ // have size > 0 only during update_view;
+ ArrayList setOCForOI = new ArrayList();
+ ArrayList valueOfSetOCForOI = new ArrayList();
+
+ OrderedBin(int nchildren, OrderedGroupRetained src){
+ int i;
+ for (i=0; i< nchildren; i++) {
+ orderedCollections.add(null);
+ }
+ source = src;
+ }
+
+ void addRemoveOrderedCollection() {
+ int i, index;
+
+ // Add the setValues first, since they reflect already existing
+ // orderedCollection
+ for (i = 0; i < setOCForCI.size(); i++) {
+ index = ((Integer)setOCForCI.get(i)).intValue();
+ OrderedCollection oc = (OrderedCollection)valueOfSetOCForCI.get(i);
+ orderedCollections.set(index, oc);
+ }
+
+ setOCForCI.clear();
+ valueOfSetOCForCI.clear();
+
+ while (childInfoList != null) {
+ if (childInfoList.type == OrderedChildInfo.ADD) {
+ orderedCollections.add(childInfoList.childId, childInfoList.value);
+ }
+ else if (childInfoList.type == OrderedChildInfo.REMOVE) {
+ orderedCollections.remove(childInfoList.childId);
+ }
+ childInfoList = childInfoList.next;
+ }
+
+ // Now update the sets based on oi, since the og.orderedChildIdTable reflects
+ // the childIds for the next frame, use the table to set the oc at the
+ // correct place
+ for (i = 0; i < setOCForOI.size(); i++) {
+ index = ((Integer)setOCForOI.get(i)).intValue();
+ OrderedCollection oc = (OrderedCollection)valueOfSetOCForOI.get(i);
+ int ci = source.orderedChildIdTable[index];
+ orderedCollections.set(ci, oc);
+ }
+ setOCForOI.clear();
+ valueOfSetOCForOI.clear();
+
+ onUpdateList = false;
+ lastChildInfo = null;
+
+
+ }
+ void addChildInfo(OrderedChildInfo cinfo) {
+ // Add this cinfo at the end
+ if (childInfoList == null) {
+ childInfoList = cinfo;
+ lastChildInfo = cinfo;
+ }
+ else {
+ // Add at the end
+ cinfo.prev = lastChildInfo;
+ lastChildInfo.next = cinfo;
+ cinfo.next = null;
+ // Update this to be the last child
+ lastChildInfo = cinfo;
+ }
+
+ }
+
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/OrderedChildInfo.java b/src/classes/share/javax/media/j3d/OrderedChildInfo.java
new file mode 100644
index 0000000..f07dfe2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrderedChildInfo.java
@@ -0,0 +1,64 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * List of orderedGroup children that needs to be added/removed for
+ * the next frame. Note that the order in which they are removed and
+ * added should be maintained after the renderer is done to get the
+ * correct order of rendering.
+ */
+class OrderedChildInfo extends Object {
+
+ static int ADD = 0x1;
+ static int REMOVE = 0x2;
+
+
+ /**
+ * Type of operation, could be add/remove or set
+ */
+ int type;
+
+ /**
+ * Ordered index at which this operation takes place
+ */
+ int orderedId;
+
+ /**
+ * Child index at which this operation takes place
+ */
+ int childId;
+
+ /**
+ * Value of the orderedCollection, only relavent for
+ * add and set
+ */
+ OrderedCollection value;
+
+
+ // Maintains the order in which the ordered children
+ // were added and removed
+ OrderedChildInfo next;
+ OrderedChildInfo prev;
+
+ OrderedChildInfo(int t, int cid, int oid, OrderedCollection val) {
+ type = t;
+ orderedId = oid;
+ childId = cid;
+ value = val;
+ prev = null;
+ next = null;
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/OrderedCollection.java b/src/classes/share/javax/media/j3d/OrderedCollection.java
new file mode 100644
index 0000000..5b35d2d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrderedCollection.java
@@ -0,0 +1,58 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+
+/**
+ * An OrderCollections contains a LightBin and an ArrayList of
+ * of all top level OrderedGroups under this OrderCollection
+ */
+class OrderedCollection extends Object implements ObjectUpdate{
+
+ LightBin lightBin = null;
+
+ // a list of top level orderedBins under this orderedCollection
+ ArrayList childOrderedBins = new ArrayList();
+
+ // LightBin used for next frame
+ LightBin nextFrameLightBin = null;
+
+ // LightBins to be added for this frame
+ LightBin addLightBins = null;
+
+ boolean onUpdateList = false;
+
+ public void updateObject() {
+ int i;
+ LightBin lb;
+ lightBin = nextFrameLightBin;
+ if (addLightBins != null) {
+ if (lightBin != null) {
+ addLightBins.prev = lightBin;
+ lightBin.next = addLightBins;
+ }
+ else {
+ lightBin = addLightBins;
+ nextFrameLightBin = lightBin;
+ }
+ }
+ addLightBins = null;
+ onUpdateList = false;
+ }
+
+
+
+}
+
diff --git a/src/classes/share/javax/media/j3d/OrderedGroup.java b/src/classes/share/javax/media/j3d/OrderedGroup.java
new file mode 100644
index 0000000..dcbc85d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrderedGroup.java
@@ -0,0 +1,458 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Arrays;
+
+/**
+ * The OrderedGroup node is a Group that ensures its children render
+ * in a specified order. In addition to the list of children
+ * inherited from the base Group class, OrderedGroup defines an
+ * integer array of child indices that specify the order in which its
+ * children are rendered. This provides a level of indirection in
+ * determining the rendering order of its children. By default, the
+ * child index order array is null, and the children are rendered in
+ * increasing index order.
+ *
+ * <p>
+ * When the child index order array is non-null, it must be the same
+ * length as the number of children. Every entry in the array must
+ * have a unique value between 0 and <code>numChildren-1</code> (i.e.,
+ * there must be no duplicate values and no missing indices). The
+ * order that the child indices appear in the child index order array
+ * determines the order in which the children are rendered. The child
+ * at <code>childIndexOrder[0]</code> is rendered first, followed by
+ * <code>childIndexOrder[1]</code>, and so on, with the child at
+ * <code>childIndexOrder[numChildren-1]</code> rendered
+ * last.
+ *
+ * <p>
+ * The child index order array only affects rendering. List
+ * operations that refer to a child by index, such as getChild(index),
+ * will not be altered by the entries in this array. They will get,
+ * enumerate, add, remove, etc., the children based on the actual
+ * index in the group node. However, some of the list operations,
+ * such as addChild, removeChild, etc., will update the child index
+ * order array as a result of their operation. For example,
+ * removeChild will remove the entry in the child index order array
+ * corresponding to the removed child's index and adjust the remaining
+ * entries accordingly. See the documentation for these methods for
+ * more details.
+ */
+
+public class OrderedGroup extends Group {
+
+ private boolean checkArr[] = null;
+
+ /**
+ * Specifies that this OrderedGroup node
+ * allows reading its child index order information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_CHILD_INDEX_ORDER_READ =
+ CapabilityBits.ORDERED_GROUP_ALLOW_CHILD_INDEX_ORDER_READ;
+
+ /**
+ * Specifies that this OrderedGroup node
+ * allows writing its child index order information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_CHILD_INDEX_ORDER_WRITE =
+ CapabilityBits.ORDERED_GROUP_ALLOW_CHILD_INDEX_ORDER_WRITE;
+
+
+ /**
+ * Constructs and initializes a new OrderedGroup node object.
+ * The childIndexOrder array is initialized to null, meaning
+ * that its children are rendered in increasing index order.
+ */
+ public OrderedGroup() {
+ }
+
+
+ /**
+ * Sets the childIndexOrder array. If the specified array is
+ * null, this node's childIndexOrder array is set to null. Its
+ * children will then be rendered in increasing index order. If
+ * the specified array is not null, the entire array is copied to
+ * this node's childIndexOrder array. In this case, the length of
+ * the array must be equal to the number of children in this
+ * group, and every entry in the array must have a unique value
+ * between 0 and <code>numChildren-1</code> (i.e., there must be
+ * no duplicate values and no missing indices).
+ *
+ * @param childIndexOrder the array that is copied into this
+ * node's child index order array; this can be null
+ *
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any of the following are true:
+ * <ul>
+ * <li><code>childIndexOrder.length != numChildren</code>;</li>
+ * <li><code>childIndexOrder[</code><i>i</i><code>] < 0</code>,
+ * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
+ * <li><code>childIndexOrder[</code><i>i</i><code>] >= numChildren</code>,
+ * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
+ * <li><code>childIndexOrder[</code><i>i</i><code>] ==
+ * childIndexOrder[</code><i>j</i><code>]</code>,
+ * for <i>i</i>,<i>j</i> in <code>[0, numChildren-1]</code>,
+ * <i>i</i> <code>!=</code> <i>j</i>;</li>
+ * </ul>
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setChildIndexOrder(int[] childIndexOrder) {
+ verifyChildIndexOrderArray(childIndexOrder, 0);
+
+ ((OrderedGroupRetained)retained).setChildIndexOrder(childIndexOrder);
+ }
+
+
+ /**
+ * Retrieves the current childIndexOrder array.
+ *
+ * @return a copy of this node's childIndexOrder array; this
+ * can be null.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int[] getChildIndexOrder() {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHILD_INDEX_ORDER_READ))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("OrderedGroup5"));
+
+ return ((OrderedGroupRetained)this.retained).getChildIndexOrder();
+ }
+
+ /**
+ * Appends the specified child node to this group node's list of
+ * children, and sets the child index order array to the specified
+ * array. If the specified array is null, this node's
+ * childIndexOrder array is set to null. Its children will then
+ * be rendered in increasing index order. If the specified array
+ * is not null, the entire array is copied to this node's
+ * childIndexOrder array. In this case, the length of the array
+ * must be equal to the number of children in this group after the
+ * new child has been added, and every entry in the array must
+ * have a unique value between 0 and <code>numChildren-1</code>
+ * (i.e., there must be no duplicate values and no missing
+ * indices).
+ *
+ * @param child the child to add to this node's list of children
+ *
+ * @param childIndexOrder the array that is copied into this
+ * node's child index order array; this can be null
+ *
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ *
+ * @exception RestrictedAccessException if this group node is part
+ * of live
+ * or compiled scene graph and the child node being added is not
+ * a BranchGroup node
+ *
+ * @exception MultipleParentException if <code>child</code> has already
+ * been added as a child of another group node.
+ *
+ * @exception IllegalArgumentException if the specified array is
+ * non-null and any of the following are true:
+ * <ul>
+ * <li><code>childIndexOrder.length != numChildren</code>;</li>
+ * <li><code>childIndexOrder[</code><i>i</i><code>] < 0</code>,
+ * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
+ * <li><code>childIndexOrder[</code><i>i</i><code>] >= numChildren</code>,
+ * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
+ * <li><code>childIndexOrder[</code><i>i</i><code>] ==
+ * childIndexOrder[</code><i>j</i><code>]</code>,
+ * for <i>i</i>,<i>j</i> in <code>[0, numChildren-1]</code>,
+ * <i>i</i> <code>!=</code> <i>j</i>;</li>
+ * </ul>
+ *
+ * @since Java 3D 1.3
+ */
+ public void addChild(Node child, int[] childIndexOrder) {
+
+ verifyAddStates(child);
+ verifyChildIndexOrderArray(childIndexOrder, 1);
+
+ ((OrderedGroupRetained)retained).addChild(child, childIndexOrder);
+
+ }
+
+
+ // Overridden methods from Group
+
+ /**
+ * Appends the specified child node to this group node's list of children.
+ *
+ * <p>
+ * If the current child index order array is non-null, the array
+ * is increased in size by one element, and a new element
+ * containing the index of the new child is added to the end of
+ * the array. Thus, this new child will be rendered last.
+ *
+ * @param child the child to add to this node's list of children
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part
+ * of live
+ * or compiled scene graph and the child node being added is not
+ * a BranchGroup node
+ * @exception MultipleParentException if <code>child</code> has already
+ * been added as a child of another group node.
+ *
+ * @since Java 3D 1.3
+ */
+ public void addChild(Node child) {
+ // Just call super -- the extra work is done by the retained class
+ super.addChild(child);
+ }
+
+ /**
+ * Inserts the specified child node in this group node's list of
+ * children at the specified index.
+ * This method is only supported when the child index order array
+ * is null.
+ *
+ * @param child the new child
+ * @param index at which location to insert. The <code>index</code>
+ * must be a value
+ * greater than or equal to 0 and less than or equal to
+ * <code>numChildren()</code>.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live
+ * or compiled scene graph and the child node being inserted is not
+ * a BranchGroup node
+ * @exception MultipleParentException if <code>child</code> has already
+ * been added as a child of another group node.
+ * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
+ * @exception IllegalStateException if the childIndexOrder array is
+ * not null.
+ *
+ * @since Java 3D 1.3
+ */
+ public void insertChild(Node child, int index) {
+ if (((OrderedGroupRetained)retained).userChildIndexOrder != null) {
+ throw new IllegalStateException(J3dI18N.getString("OrderedGroup6"));
+ }
+
+ // Just call super -- the extra work is done by the retained class
+ super.insertChild(child, index);
+ }
+
+ /**
+ * Removes the child node at the specified index from this group node's
+ * list of children.
+ *
+ * <p>
+ * If the current child index order array is non-null, the element
+ * containing the removed child's index will be removed from the
+ * child index order array, and the array will be reduced in size
+ * by one element. If the child removed was not the last child in
+ * the Group, the values of the child index order array will be
+ * updated to reflect the indices that were renumbered. More
+ * formally, each child whose index in the Group node was greater
+ * than the removed element (before removal) will have its index
+ * decremented by one.
+ *
+ * @param index which child to remove. The <code>index</code>
+ * must be a value
+ * greater than or equal to 0 and less than <code>numChildren()</code>.
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live or compiled scene graph and the child node being removed is not
+ * a BranchGroup node
+ * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeChild(int index) {
+ // Just call super -- the extra work is done by the retained class
+ super.removeChild(index);
+ }
+
+
+ /**
+ * Moves the specified branch group node from its existing location to
+ * the end of this group node's list of children.
+ *
+ * <p>
+ * If the current child index order array is non-null, the array
+ * is increased in size by one element, and a new element
+ * containing the index of the new child is added to the end of
+ * the array. Thus, this new child will be rendered last.
+ *
+ * @param branchGroup the branch group node to move to this node's list
+ * of children
+ * @exception CapabilityNotSetException if the appropriate capability is
+ * not set and this group node is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void moveTo(BranchGroup branchGroup) {
+ // Just call super -- the extra work is done by the retained class
+ super.moveTo(branchGroup);
+ }
+
+ /**
+ * Removes the specified child node from this group node's
+ * list of children.
+ * If the specified object is not in the list, the list is not modified.
+ *
+ * <p>
+ * If the current child index order array is non-null, the element
+ * containing the removed child's index will be removed from the
+ * child index order array, and the array will be reduced in size
+ * by one element. If the child removed was not the last child in
+ * the Group, the values of the child index order array will be
+ * updated to reflect the indices that were renumbered. More
+ * formally, each child whose index in the Group node was greater
+ * than the removed element (before removal) will have its index
+ * decremented by one.
+ *
+ * @param child the child node to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live or compiled scene graph and the child node being removed is not
+ * a BranchGroup node
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeChild(Node child) {
+ // Just call super -- the extra work is done by the retained class
+ super.removeChild(child);
+ }
+
+ /**
+ * Removes all children from this Group node.
+ *
+ * <p>
+ * If the current child index order array is non-null, then it is set to
+ * a zero-length array (the empty set). Note that a zero-length
+ * child index order array is not the same as a null array in that
+ * as new elements are added, the child index order array will grow
+ * and will be used instead of the Group's natural child order.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception RestrictedAccessException if this group node is part of
+ * live or compiled scene graph and any of the children being removed are
+ * not BranchGroup nodes
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllChildren() {
+ // Just call super -- the extra work is done by the retained class
+ super.removeAllChildren();
+ }
+
+
+ /**
+ * Creates the retained mode OrderedGroupRetained object that this
+ * OrderedGroup component object will point to.
+ */
+ void createRetained() {
+ this.retained = new OrderedGroupRetained();
+ this.retained.setSource(this);
+ }
+
+ void verifyAddStates(Node child) {
+
+ if (child instanceof SharedGroup) {
+ throw new IllegalArgumentException(J3dI18N.getString("Group2"));
+ }
+
+ if (isLiveOrCompiled()) {
+ if (! (child instanceof BranchGroup))
+ throw new RestrictedAccessException(J3dI18N.getString("Group12"));
+
+ if(!this.getCapability(ALLOW_CHILDREN_EXTEND))
+ throw new CapabilityNotSetException(J3dI18N.getString("Group16"));
+ }
+ }
+
+ void verifyChildIndexOrderArray(int[] cIOArr, int plus) {
+
+ if (isLiveOrCompiled()) {
+
+ if(!this.getCapability(ALLOW_CHILD_INDEX_ORDER_WRITE))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("OrderedGroup4"));
+ }
+
+ if(cIOArr != null) {
+
+ if(cIOArr.length != (((GroupRetained)retained).children.size() + plus)) {
+ throw new
+ IllegalArgumentException(J3dI18N.getString("OrderedGroup0"));
+ }
+
+ if((checkArr == null) || (checkArr.length != cIOArr.length)) {
+ checkArr = new boolean[cIOArr.length];
+ }
+
+ Arrays.fill(checkArr, false);
+
+ for(int i=0; i<cIOArr.length; i++) {
+ if(cIOArr[i] < 0) {
+ throw new
+ IllegalArgumentException(J3dI18N.getString("OrderedGroup1"));
+ }
+ else if(cIOArr[i] >= cIOArr.length) {
+ throw new
+ IllegalArgumentException(J3dI18N.getString("OrderedGroup2"));
+ }
+ else if(checkArr[cIOArr[i]]) {
+ throw new
+ IllegalArgumentException(J3dI18N.getString("OrderedGroup3"));
+ }
+ else {
+ checkArr[cIOArr[i]] = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * 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) {
+ OrderedGroup og = new OrderedGroup();
+ og.duplicateNode(this, forceDuplicate);
+ return og;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/OrderedGroupRetained.java b/src/classes/share/javax/media/j3d/OrderedGroupRetained.java
new file mode 100644
index 0000000..8ee1f8f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrderedGroupRetained.java
@@ -0,0 +1,502 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * The OrderedGroup is a group node that ensures its children rendered
+ * in index increasing order.
+ */
+
+class OrderedGroupRetained extends GroupRetained {
+ // mapping of ordered child id to child index
+ int orderedChildIdTable[];
+
+ // This is a counter for ordered child id
+ private int orderedChildIdCount = 0;
+
+ // This is a vector of free orderedChildId
+ private ArrayList orderedChildIdFreeList = new ArrayList();
+
+ // One OrderedBin per view
+ OrderedBin[] orderedBin = new OrderedBin[0];
+
+ // child id of the newly added child
+ Integer newChildId;
+
+ // ChildCount used by renderBin to initialize the
+ // orderedCollection in each orderedBin (per view)
+ int childCount = 0;
+
+ // per children ordered path data
+ ArrayList childrenOrderedPaths = new ArrayList(1);
+
+
+ // child index order - set by the user.
+ int[] userChildIndexOrder = null;
+
+ // child index order - use by j3d internal.
+ int[] childIndexOrder = null;
+
+
+ OrderedGroupRetained() {
+ this.nodeType = NodeRetained.ORDEREDGROUP;
+ }
+
+
+ void setChildIndexOrder(int[] cIOArr) {
+ if(cIOArr != null) {
+ if((userChildIndexOrder == null) ||
+ (userChildIndexOrder.length != cIOArr.length)) {
+ userChildIndexOrder = new int[cIOArr.length];
+ }
+
+ System.arraycopy(cIOArr, 0, userChildIndexOrder,
+ 0, userChildIndexOrder.length);
+ }
+ else {
+ userChildIndexOrder = null;
+ }
+
+ if (source.isLive()) {
+ int[] newArr = new int[cIOArr.length];
+ System.arraycopy(cIOArr, 0, newArr,
+ 0, newArr.length);
+ J3dMessage m;
+ m = VirtualUniverse.mc.getMessage();
+ m.threads = J3dThread.UPDATE_RENDER;
+ m.type = J3dMessage.ORDERED_GROUP_TABLE_CHANGED;
+ m.universe = universe;
+ m.args[3] = this;
+ m.args[4] = newArr;
+ VirtualUniverse.mc.processMessage(m);
+ }
+ }
+
+ int[] getChildIndexOrder() {
+ if (userChildIndexOrder == null) {
+ return null;
+ }
+
+ int[] newArr = new int[userChildIndexOrder.length];
+ System.arraycopy(userChildIndexOrder, 0,
+ newArr, 0, userChildIndexOrder.length);
+ return newArr;
+
+ }
+
+ Integer getOrderedChildId() {
+ Integer orderedChildId;
+ synchronized(orderedChildIdFreeList) {
+ if (orderedChildIdFreeList.size() == 0) {
+ orderedChildId = new Integer(orderedChildIdCount);
+ orderedChildIdCount++;
+ } else {
+ orderedChildId = (Integer)orderedChildIdFreeList.remove(0);
+ }
+ }
+ return(orderedChildId);
+ }
+
+ void freeOrderedChildId(int id) {
+ synchronized(orderedChildIdFreeList) {
+ orderedChildIdFreeList.add(new Integer(id));
+ }
+ }
+
+ int getOrderedChildCount() {
+ int count;
+
+ synchronized (orderedChildIdFreeList) {
+ count = orderedChildIdCount;
+ }
+ return count;
+ }
+
+ void addChild(Node child) {
+ if(userChildIndexOrder != null) {
+ doAddChildIndexEntry();
+ }
+
+ // GroupRetained.addChild have to check for case of non-null child index order
+ // array and handle it.
+ super.addChild(child);
+
+ }
+
+ void addChild(Node child, int[] cIOArr) {
+ if(cIOArr != null) {
+ userChildIndexOrder = new int[cIOArr.length];
+
+ System.arraycopy(cIOArr, 0, userChildIndexOrder,
+ 0, userChildIndexOrder.length);
+ }
+ else {
+ userChildIndexOrder = null;
+ }
+
+ // GroupRetained.addChild have to check for case of non-null child
+ // index order array and handle it.
+ super.addChild(child);
+
+ }
+
+ void moveTo(BranchGroup bg) {
+ if(userChildIndexOrder != null) {
+ doAddChildIndexEntry();
+ }
+
+ // GroupRetained.moveto have to check for case of non-null child
+ // index order array and handle it.
+ super.moveTo(bg);
+ }
+
+
+ void doRemoveChildIndexEntry(int index) {
+
+ int[] newArr = new int[userChildIndexOrder.length - 1];
+
+ for(int i=0, j=0; i<userChildIndexOrder.length; i++) {
+ if(userChildIndexOrder[i] > index) {
+ newArr[j] = userChildIndexOrder[i] - 1;
+ j++;
+ }
+ else if(userChildIndexOrder[i] < index) {
+ newArr[j] = userChildIndexOrder[i];
+ j++;
+ }
+ }
+
+ userChildIndexOrder = newArr;
+
+ }
+
+ void doAddChildIndexEntry() {
+ int[] newArr = new int[userChildIndexOrder.length + 1];
+
+ System.arraycopy(userChildIndexOrder, 0, newArr,
+ 0, userChildIndexOrder.length);
+
+ newArr[userChildIndexOrder.length] = userChildIndexOrder.length;
+
+ userChildIndexOrder = newArr;
+ }
+
+ /**
+ * Compiles the children of the OrderedGroup, preventing shape merging at
+ * this level or above
+ */
+ void compile(CompileState compState) {
+
+ super.compile(compState);
+
+ // don't remove this group node
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numOrderedGroups++;
+ }
+ }
+
+ void setOrderedBin(OrderedBin ob, int index) {
+ synchronized (orderedBin) {
+ orderedBin[index] = ob;
+ }
+ }
+
+
+ // Get the orderedBin for this view index
+ OrderedBin getOrderedBin(int index) {
+ OrderedBin ob;
+ synchronized (orderedBin) {
+ if (index >= orderedBin.length) {
+ OrderedBin[] newList = new OrderedBin[index+1];
+ for (int i= 0; i < orderedBin.length; i++) {
+ newList[i] = orderedBin[i];
+ }
+ newList[index] = null;
+ orderedBin = newList;
+ }
+ }
+ return (orderedBin[index]);
+ }
+
+ void updateChildIdTableInserted(int childId, int orderedId) {
+ int size = 0;
+ int i;
+
+ //System.out.println("updateChildIdTableInserted childId " + childId + " orderedId " + orderedId + " " + this);
+ if (orderedChildIdTable != null) {
+ size = orderedChildIdTable.length;
+ for (i=0; i<size; i++) {
+ if (orderedChildIdTable[i] != -1) {
+ if (orderedChildIdTable[i] >= childId) {
+ orderedChildIdTable[i]++; // shift upward
+ }
+ }
+ }
+ }
+ if (orderedId >= size) {
+ int newTable[];
+ newTable = new int[orderedId+1];
+ if (size > 0) {
+ System.arraycopy(orderedChildIdTable,0,newTable,0,
+ orderedChildIdTable.length);
+ }
+ else {
+ for (i = 0; i < newTable.length; i++) {
+ newTable[i] = -1;
+ }
+ }
+ orderedChildIdTable = newTable;
+ }
+ orderedChildIdTable[orderedId] = childId;
+ //printTable(orderedChildIdTable);
+
+ }
+
+ void updateChildIdTableRemoved(int childId ) {
+ // If the orderedGroup itself has been clearLived, then the ids
+ // have been returned, if only some of the children of the
+ // OGs is removed, then removed the specific entries
+ // from the table
+ if (orderedChildIdTable == null)
+ return;
+
+ for (int i=0; i<orderedChildIdTable.length; i++) {
+ if (orderedChildIdTable[i] != -1) {
+ if (orderedChildIdTable[i] > childId) {
+ orderedChildIdTable[i]--; // shift downward
+ }
+ else if (orderedChildIdTable[i] == childId) {
+ orderedChildIdTable[i] = -1;
+ //System.out.println("og.updateChildIdTableRemoved freeId " + i);
+ freeOrderedChildId(i);
+ }
+ }
+ }
+
+ }
+
+ void setAuxData(SetLiveState s, int index, int hkIndex) {
+ OrderedPath setLiveStateOrderedPath, newOrderedPath;
+ ArrayList childOrderedPaths;
+ NodeRetained child;
+
+ setLiveStateOrderedPath = (OrderedPath) s.orderedPaths.get(hkIndex);
+ for (int i=0; i<children.size(); i++) {
+ child = (NodeRetained)children.get(i);
+ if (refCount == s.refCount) {
+ // only need to do it once if in shared group when the first
+ // instances is to be added
+ child.orderedId = getOrderedChildId();
+ }
+
+ newOrderedPath = setLiveStateOrderedPath.clonePath();
+ newOrderedPath.addElementToPath(this, child.orderedId);
+ childOrderedPaths = (ArrayList)childrenOrderedPaths.get(i);
+ childOrderedPaths.add(hkIndex, newOrderedPath);
+ }
+ }
+
+
+ void setLive(SetLiveState s) {
+ super.setLive(s);
+ s.orderedPaths = orderedPaths;
+ if((userChildIndexOrder != null) && (refCount == 1)) {
+
+ // Don't send a message for initial set live.
+ int[]newArr = new int[userChildIndexOrder.length];
+ System.arraycopy(userChildIndexOrder, 0, newArr,
+ 0, userChildIndexOrder.length);
+ childIndexOrder = newArr;
+ }
+ }
+
+ void clearLive(SetLiveState s) {
+ super.clearLive(s);
+ // This is used to clear the childIdTable and set the orderedBin
+ // for all views to be null
+
+ if (refCount == 0) {
+ s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT;
+ // only need to do it once if in shared group
+ s.nodeList.add(this);
+ s.ogCIOList.add(this);
+ s.ogCIOTableList.add(null);
+ userChildIndexOrder = null;
+ }
+ s.orderedPaths = orderedPaths;
+ }
+
+ void setNodeData(SetLiveState s) {
+ super.setNodeData(s);
+ if (!inSharedGroup) {
+ setAuxData(s, 0, 0);
+ } else {
+ // For inSharedGroup case.
+ int j, hkIndex;
+
+ for(j=0; j<s.keys.length; j++) {
+ hkIndex = s.keys[j].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+
+ if(hkIndex >= 0) {
+ setAuxData(s, j, hkIndex);
+
+ } else {
+ System.out.println("Can't Find matching hashKey in setNodeData.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ }
+ }
+ // Note s.orderedPaths is to be updated in GroupRetained.setLive
+ // for each of its children
+ }
+
+ void removeNodeData(SetLiveState s) {
+
+ if((inSharedGroup) && (s.keys.length != localToVworld.length)) {
+ int i, index;
+ ArrayList childOrderedPaths;
+
+ // Must be in reverse, to preserve right indexing.
+ for (i = s.keys.length-1; i >= 0; i--) {
+ index = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(index >= 0) {
+ for (int j=0; j<children.size(); j++) {
+ childOrderedPaths = (ArrayList)childrenOrderedPaths.get(j);
+ childOrderedPaths.remove(index);
+ }
+ }
+ }
+ // Note s.orderedPaths is to be updated in GroupRetained.clearLive
+ // for each of its children
+ }
+ super.removeNodeData(s);
+ }
+
+
+ // This node has been cleared, so
+ void clearDerivedDataStructures() {
+ int i;
+
+ //System.out.println("og clearDerivedDataStructures " + this);
+ // Clear the orderedBin and childId table for all views
+ // since this orderedGroup has been clearLived!
+ for (i = 0; i < orderedBin.length; i++) {
+ if (orderedBin[i] != null) {
+ orderedBin[i].source = null;
+ orderedBin[i] = null;
+ }
+ }
+ if (orderedChildIdTable != null) {
+ for (i=0; i<orderedChildIdTable.length; i++) {
+ if (orderedChildIdTable[i] != -1) {
+ orderedChildIdTable[i] = -1;
+ //System.out.println("og.clearDerivedDataStructures freeId " + i);
+ freeOrderedChildId(i);
+ }
+ }
+ orderedChildIdTable = null;
+ }
+ }
+
+ void incrChildCount() {
+ childCount++;
+ }
+
+
+ void decrChildCount() {
+ childCount--;
+ }
+
+ void printTable(int[] table) {
+ for (int i=0; i<table.length; i++) {
+ System.out.print(" " + table[i]);
+ }
+ System.out.println("");
+ }
+
+ void insertChildrenData(int index) {
+ childrenOrderedPaths.add(index, new ArrayList(1));
+ }
+
+ void appendChildrenData() {
+ childrenOrderedPaths.add(new ArrayList(1));
+ }
+
+ void doRemoveChild(int index, J3dMessage messages[], int messageIndex) {
+
+ if(userChildIndexOrder != null) {
+ doRemoveChildIndexEntry(index);
+ }
+
+ super.doRemoveChild(index, messages, messageIndex);
+
+ }
+
+ void removeChildrenData(int index) {
+ childrenOrderedPaths.remove(index);
+ }
+
+ void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) {
+ if (refCount == s.refCount) {
+ s.ogList.add(this);
+ s.ogChildIdList.add(new Integer(childIndex));
+ s.ogOrderedIdList.add(child.orderedId);
+ }
+ s.orderedPaths = (ArrayList)childrenOrderedPaths.get(childIndex);
+ if(child!=null)
+ child.setLive(s);
+ }
+
+ void childCheckSetLive(NodeRetained child, int childIndex,
+ SetLiveState s, NodeRetained linkNode) {
+ OrderedPath childOrderedPath;
+ ArrayList childOrderedPaths;
+
+ if (linkNode != null) {
+ int ci = children.indexOf(linkNode);
+ childOrderedPaths = (ArrayList)childrenOrderedPaths.get(ci);
+ } else {
+ child.orderedId = getOrderedChildId();
+ // set this regardless of refCount
+ s.ogList.add(this);
+ s.ogChildIdList.add(new Integer(childIndex));
+ s.ogOrderedIdList.add(child.orderedId);
+
+ if(userChildIndexOrder != null) {
+ s.ogCIOList.add(this);
+ int[] newArr = new int[userChildIndexOrder.length];
+ System.arraycopy(userChildIndexOrder, 0, newArr,
+ 0, userChildIndexOrder.length);
+
+ s.ogCIOTableList.add(newArr);
+ }
+
+ childOrderedPaths = (ArrayList)childrenOrderedPaths.get(childIndex);
+
+ for(int i=0; i< orderedPaths.size();i++){
+ childOrderedPath =
+ ((OrderedPath)orderedPaths.get(i)).clonePath();
+ childOrderedPath.addElementToPath(this, child.orderedId);
+ childOrderedPaths.add(childOrderedPath);
+ }
+ }
+ s.orderedPaths = childOrderedPaths;
+ child.setLive(s);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/OrderedPath.java b/src/classes/share/javax/media/j3d/OrderedPath.java
new file mode 100644
index 0000000..8da3fe8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrderedPath.java
@@ -0,0 +1,40 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+class OrderedPath extends Object {
+ ArrayList pathElements = new ArrayList(1);
+
+
+ void addElementToPath(OrderedGroupRetained og, Integer orderedId) {
+ pathElements.add(new OrderedPathElement(og, orderedId));
+ }
+
+ OrderedPath clonePath() {
+ OrderedPath path = new OrderedPath();
+ path.pathElements = (ArrayList)pathElements.clone();
+ return path;
+ }
+
+ void printPath() {
+ System.out.println("orderedPath: [");
+ OrderedPathElement ope;
+ for (int i=0; i<pathElements.size(); i++) {
+ ope = (OrderedPathElement)pathElements.get(i);
+ System.out.println("(" + ope.orderedGroup + "," + ope.childId);
+ }
+ System.out.println("]");
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/OrderedPathElement.java b/src/classes/share/javax/media/j3d/OrderedPathElement.java
new file mode 100644
index 0000000..cce1d77
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrderedPathElement.java
@@ -0,0 +1,25 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+class OrderedPathElement extends Object {
+ OrderedGroupRetained orderedGroup;
+ Integer childId;
+
+ OrderedPathElement(OrderedGroupRetained og, Integer orderedId) {
+ orderedGroup = og;
+ childId = orderedId;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/OrientedShape3D.java b/src/classes/share/javax/media/j3d/OrientedShape3D.java
new file mode 100644
index 0000000..edfbe3a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrientedShape3D.java
@@ -0,0 +1,645 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The OrientedShape3D leaf node is a Shape3D node that is oriented
+ * along a specified axis or about a specified point. It defines an
+ * alignment mode and a rotation point or axis. This will cause
+ * the local +<i>z</i> axis of the object to point at the viewer's eye
+ * position. This is done regardless of the transforms above this
+ * OrientedShape3D node in the scene graph. It optionally defines a
+ * scale value along with a constant scale enable flag that causes
+ * this node to be scale invariant, subject only to its scale.
+ *
+ * <p>
+ * OrientedShape3D is similar in functionality to the Billboard
+ * behavior, but OrientedShape3D nodes will orient themselves
+ * correctly for each view, and they can be used within a SharedGroup.
+ *
+ * <p>
+ * If the alignment mode is ROTATE_ABOUT_AXIS, then the rotation will be
+ * around the specified axis. If the alignment mode is
+ * ROTATE_ABOUT_POINT, then the rotation will be about the specified
+ * point, with an additional rotation to align the +<i>y</i> axis of the
+ * TransformGroup with the +<i>y</i> axis in the View.
+ *
+ * <p>
+ * If the constant scale enable flag is set, the object will be drawn
+ * the same size in absolute screen coordinates (meters), regardless
+ * of the following: any transforms above this OrientedShape3D node in
+ * the scene graph, the view scale, the window scale, or the effects
+ * of perspective correction. This is done by scaling the geometry
+ * about the local origin of this node, such that 1 unit in local
+ * coordinates is equal to the number of meters specified by this
+ * node's scale value. If the constant scale enable flag is set to
+ * false, then the scale is not used. The default scale is 1.0
+ * meters.
+ *
+ * <p>
+ * OrientedShape3D nodes are ideal for drawing screen-aligned text or
+ * for drawing roughly-symmetrical objects. A typical use might
+ * consist of a quadrilateral that contains a texture of a tree.
+ *
+ * @see Billboard
+ *
+ * @since Java 3D 1.2
+ */
+
+public class OrientedShape3D extends Shape3D {
+
+ /**
+ * Specifies that rotation should be about the specified axis.
+ * @see #setAlignmentMode
+ */
+ public static final int ROTATE_ABOUT_AXIS = 0;
+
+ /**
+ * Specifies that rotation should be about the specified point and
+ * that the children's Y-axis should match the view object's Y-axis.
+ * @see #setAlignmentMode
+ */
+ public static final int ROTATE_ABOUT_POINT = 1;
+
+ /**
+ * Specifies that no rotation is done. The OrientedShape3D will
+ * not be aligned to the view.
+ * @see #setAlignmentMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ROTATE_NONE = 2;
+
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows reading its alignment mode information.
+ */
+ public static final int ALLOW_MODE_READ =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_MODE_READ;
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows writing its alignment mode information.
+ */
+ public static final int ALLOW_MODE_WRITE =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_MODE_WRITE;
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows reading its alignment axis information.
+ */
+ public static final int ALLOW_AXIS_READ =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_AXIS_READ;
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows writing its alignment axis information.
+ */
+ public static final int ALLOW_AXIS_WRITE =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_AXIS_WRITE;
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows reading its rotation point information.
+ */
+ public static final int ALLOW_POINT_READ =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_POINT_READ;
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows writing its rotation point information.
+ */
+ public static final int ALLOW_POINT_WRITE =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_POINT_WRITE;
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows reading its scale and constant scale enable information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_SCALE_READ =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_SCALE_READ;
+
+ /**
+ * Specifies that this OrientedShape3D node
+ * allows writing its scale and constant scale enable information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_SCALE_WRITE =
+ CapabilityBits.ORIENTED_SHAPE3D_ALLOW_SCALE_WRITE;
+
+
+ /**
+ * Constructs an OrientedShape3D node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * alignment mode : ROTATE_ABOUT_AXIS<br>
+ * alignment axis : Y-axis (0,1,0)<br>
+ * rotation point : (0,0,1)<br>
+ * constant scale enable : false<br>
+ * scale : 1.0<br>
+ *</ul>
+ */
+ public OrientedShape3D() {
+ super();
+ }
+
+
+ /**
+ * Constructs an OrientedShape3D node with the specified geometry
+ * component, appearance component, mode, and axis.
+ * The specified axis must not be parallel to the <i>Z</i>
+ * axis--(0,0,<i>z</i>) for any value of <i>z</i>. It is not
+ * possible for the +<i>Z</i> axis to point at the viewer's eye
+ * position by rotating about itself. The target transform will
+ * be set to the identity if the axis is (0,0,<i>z</i>).
+ *
+ * @param geometry the geometry component with which to initialize
+ * this shape node
+ * @param appearance the appearance component of the shape node
+ * @param mode alignment mode, one of: ROTATE_ABOUT_AXIS,
+ * ROTATE_ABOUT_POINT, or ROTATE_NONE
+ * @param axis the ray about which the OrientedShape3D rotates
+ */
+ public OrientedShape3D(Geometry geometry,
+ Appearance appearance,
+ int mode,
+ Vector3f axis) {
+
+ super(geometry, appearance);
+ ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
+ ((OrientedShape3DRetained)retained).initAlignmentAxis(axis);
+ }
+
+ /**
+ * Constructs an OrientedShape3D node with the specified geometry
+ * component, appearance component, mode, and rotation point.
+ *
+ * @param geometry the geometry component with which to initialize
+ * this shape node
+ * @param appearance the appearance component of the shape node
+ * @param mode alignment mode, one of: ROTATE_ABOUT_AXIS,
+ * ROTATE_ABOUT_POINT, or ROTATE_NONE
+ * @param point the position about which the OrientedShape3D rotates
+ */
+ public OrientedShape3D(Geometry geometry,
+ Appearance appearance,
+ int mode,
+ Point3f point) {
+
+ super(geometry, appearance);
+ ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
+ ((OrientedShape3DRetained)retained).initRotationPoint(point);
+
+ }
+
+
+ /**
+ * Constructs an OrientedShape3D node with the specified geometry
+ * component, appearance component, mode, axis, constant scale
+ * enable flag, and scale
+ * The specified axis must not be parallel to the <i>Z</i>
+ * axis--(0,0,<i>z</i>) for any value of <i>z</i>. It is not
+ * possible for the +<i>Z</i> axis to point at the viewer's eye
+ * position by rotating about itself. The target transform will
+ * be set to the identity if the axis is (0,0,<i>z</i>).
+ *
+ * @param geometry the geometry component with which to initialize
+ * this shape node
+ * @param appearance the appearance component of the shape node
+ * @param mode alignment mode, one of: ROTATE_ABOUT_AXIS,
+ * ROTATE_ABOUT_POINT, or ROTATE_NONE
+ * @param axis the ray about which the OrientedShape3D rotates
+ * @param constantScaleEnable a flag indicating whether to enable
+ * constant scale
+ * @param scale scale value used when constant scale is enabled
+ *
+ * @since Java 3D 1.3
+ */
+ public OrientedShape3D(Geometry geometry,
+ Appearance appearance,
+ int mode,
+ Vector3f axis,
+ boolean constantScaleEnable,
+ double scale) {
+
+ super(geometry, appearance);
+
+ ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
+ ((OrientedShape3DRetained)retained).initAlignmentAxis(axis);
+ ((OrientedShape3DRetained)retained).
+ initConstantScaleEnable(constantScaleEnable);
+ ((OrientedShape3DRetained)retained).initScale(scale);
+ }
+
+ /**
+ * Constructs an OrientedShape3D node with the specified geometry
+ * component, appearance component, mode, and rotation point.
+ *
+ * @param geometry the geometry component with which to initialize
+ * this shape node
+ * @param appearance the appearance component of the shape node
+ * @param mode alignment mode, one of: ROTATE_ABOUT_AXIS,
+ * ROTATE_ABOUT_POINT, or ROTATE_NONE
+ * @param point the position about which the OrientedShape3D rotates
+ * @param constantScaleEnable a flag indicating whether to enable
+ * constant scale
+ * @param scale scale value used when constant scale is enabled
+ *
+ * @since Java 3D 1.3
+ */
+ public OrientedShape3D(Geometry geometry,
+ Appearance appearance,
+ int mode,
+ Point3f point,
+ boolean constantScaleEnable,
+ double scale) {
+
+ super(geometry, appearance);
+
+ ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
+ ((OrientedShape3DRetained)retained).initRotationPoint(point);
+ ((OrientedShape3DRetained)retained).
+ initConstantScaleEnable(constantScaleEnable);
+ ((OrientedShape3DRetained)retained).initScale(scale);
+ }
+
+
+ /**
+ * Creates the retained mode OrientedShape3DRetained object that this
+ * OrientedShape3D object will point to.
+ */
+ void createRetained() {
+ retained = new OrientedShape3DRetained();
+ retained.setSource(this);
+ }
+
+
+ /**
+ * Sets the alignment mode.
+ *
+ * @param mode alignment mode, one of: ROTATE_ABOUT_AXIS,
+ * ROTATE_ABOUT_POINT, or ROTATE_NONE
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAlignmentMode(int mode) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D0"));
+ if (isLive())
+ ((OrientedShape3DRetained)retained).setAlignmentMode(mode);
+ else
+ ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
+ }
+
+
+ /**
+ * Retrieves the alignment mode.
+ *
+ * @return one of: ROTATE_ABOUT_AXIS, ROTATE_ABOUT_POINT,
+ * or ROTATE_NONE
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getAlignmentMode() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D1"));
+ return((OrientedShape3DRetained)retained).getAlignmentMode();
+ }
+
+
+ /**
+ * Sets the new alignment axis. This is the ray about which this
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_AXIS.
+ * The specified axis must not be parallel to the <i>Z</i>
+ * axis--(0,0,<i>z</i>) for any value of <i>z</i>. It is not
+ * possible for the +<i>Z</i> axis to point at the viewer's eye
+ * position by rotating about itself. The target transform will
+ * be set to the identity if the axis is (0,0,<i>z</i>).
+ *
+ * @param axis the new alignment axis
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAlignmentAxis(Vector3f axis) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_AXIS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D2"));
+ if (isLive())
+ ((OrientedShape3DRetained)retained).setAlignmentAxis(axis);
+ else
+ ((OrientedShape3DRetained)retained).initAlignmentAxis(axis);
+ }
+
+
+ /**
+ * Sets the new alignment axis. This is the ray about which this
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_AXIS.
+ * The specified axis must not be parallel to the <i>Z</i>
+ * axis--(0,0,<i>z</i>) for any value of <i>z</i>. It is not
+ * possible for the +<i>Z</i> axis to point at the viewer's eye
+ * position by rotating about itself. The target transform will
+ * be set to the identity if the axis is (0,0,<i>z</i>).
+ *
+ * @param x the x component of the alignment axis
+ * @param y the y component of the alignment axis
+ * @param z the z component of the alignment axis
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAlignmentAxis(float x, float y, float z) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_AXIS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D2"));
+ if (isLive())
+ ((OrientedShape3DRetained)retained).setAlignmentAxis(x,y,z);
+ else
+ ((OrientedShape3DRetained)retained).initAlignmentAxis(x,y,z);
+ }
+
+
+ /**
+ * Retrieves the alignment axis of this OrientedShape3D node,
+ * and copies it into the specified vector.
+ *
+ * @param axis the vector that will contain the alignment axis
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getAlignmentAxis(Vector3f axis) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_AXIS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D3"));
+ ((OrientedShape3DRetained)retained).getAlignmentAxis(axis);
+ }
+
+ /**
+ * Sets the new rotation point. This is the point about which the
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_POINT.
+ *
+ * @param point the new rotation point
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setRotationPoint(Point3f point) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_POINT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D4"));
+ if (isLive())
+ ((OrientedShape3DRetained)retained).setRotationPoint(point);
+ else
+ ((OrientedShape3DRetained)retained).initRotationPoint(point);
+ }
+
+
+ /**
+ * Sets the new rotation point. This is the point about which the
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_POINT.
+ *
+ * @param x the x component of the rotation point
+ * @param y the y component of the rotation point
+ * @param z the z component of the rotation point
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setRotationPoint(float x, float y, float z) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_POINT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D4"));
+ if (isLive())
+ ((OrientedShape3DRetained)retained).setRotationPoint(x,y,z);
+ else
+ ((OrientedShape3DRetained)retained).initRotationPoint(x,y,z);
+ }
+
+
+ /**
+ * Retrieves the rotation point of this OrientedShape3D node,
+ * and copies it into the specified vector.
+ *
+ * @param point the point that will contain the rotation point
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getRotationPoint(Point3f point) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_POINT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D5"));
+ ((OrientedShape3DRetained)retained).getRotationPoint(point);
+ }
+
+
+ /**
+ * Sets the constant scale enable flag.
+ *
+ * @param constantScaleEnable a flag indicating whether to enable
+ * constant scale
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setConstantScaleEnable(boolean constantScaleEnable) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SCALE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D6"));
+
+ if (isLive())
+ ((OrientedShape3DRetained)retained).
+ setConstantScaleEnable(constantScaleEnable);
+ else
+ ((OrientedShape3DRetained)retained).
+ initConstantScaleEnable(constantScaleEnable);
+ }
+
+
+ /**
+ * Retrieves the constant scale enable flag.
+ *
+ * @return the current constant scale enable flag
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public boolean getConstantScaleEnable() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SCALE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D7"));
+
+ return ((OrientedShape3DRetained)retained).getConstantScaleEnable();
+ }
+
+
+ /**
+ * Sets the scale for this OrientedShape3D. This scale is used when
+ * the constant scale enable flag is set to true.
+ *
+ * @param scale the scale value
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setScale(double scale) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SCALE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D8"));
+
+ if (isLive())
+ ((OrientedShape3DRetained)retained).setScale(scale);
+ else
+ ((OrientedShape3DRetained)retained).initScale(scale);
+ }
+
+
+ /**
+ * Retrieves the scale value for this OrientedShape3D.
+ *
+ * @return the current scale value
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public double getScale() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_SCALE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("OrientedShape3D9"));
+
+ return ((OrientedShape3DRetained)retained).getScale();
+ }
+
+
+ /**
+ * 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) {
+ OrientedShape3D s = new OrientedShape3D();
+ 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>Shape3D</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+
+
+
+ /**
+ * Copies all Shape3D 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNode, forceDuplicate);
+ OrientedShape3DRetained attr = (OrientedShape3DRetained)
+ originalNode.retained;
+ OrientedShape3DRetained rt = (OrientedShape3DRetained) retained;
+
+ rt.setAlignmentMode(attr.getAlignmentMode());
+ Vector3f axis = new Vector3f();
+ attr.getAlignmentAxis(axis);
+ rt.setAlignmentAxis(axis);
+ Point3f point = new Point3f();
+ attr.getRotationPoint(point);
+ rt.setRotationPoint(point);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/OrientedShape3DRenderMethod.java b/src/classes/share/javax/media/j3d/OrientedShape3DRenderMethod.java
new file mode 100644
index 0000000..bfffdd3
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrientedShape3DRenderMethod.java
@@ -0,0 +1,111 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The OrientedShape3DRenderMethod provides a render method to render
+ * OrientedShape3D nodes.
+ * The RenderMethod interface is used to create various ways to render
+ * different geometries.
+ */
+
+class OrientedShape3DRenderMethod implements RenderMethod {
+
+ public boolean render(RenderMolecule rm, Canvas3D cv, int pass,
+ RenderAtomListInfo ra, int dirtyBits) {
+ boolean useAlpha;
+ boolean isNonUniformScale;
+ Transform3D trans=null;
+
+ useAlpha = rm.useAlpha;
+
+ GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry();
+ geo.setVertexFormat((rm.useAlpha &&
+ ((geo.vertexFormat & GeometryArray.COLOR) != 0)),
+ rm.textureBin.attributeBin.ignoreVertexColors, cv.ctx);
+
+ if (rm.doInfinite) {
+ cv.updateState(pass, dirtyBits);
+ while (ra != null) {
+ trans = ra.infLocalToVworld;
+ isNonUniformScale = !trans.isCongruent();
+
+ cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat, trans);
+ ra.geometry().execute(cv, ra.renderAtom, isNonUniformScale,
+ (useAlpha && ra.geometry().noAlpha),
+ rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.ignoreVertexColors,
+ pass);
+ ra = ra.next;
+ }
+ return true;
+ }
+
+ boolean isVisible = false; // True if any of the RAs is visible.
+ while (ra != null) {
+ if (cv.ra == ra.renderAtom) {
+ if (cv.raIsVisible) {
+ cv.updateState(pass, dirtyBits);
+ trans = ra.localToVworld;
+ isNonUniformScale = !trans.isCongruent();
+
+ cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat, trans);
+ ra.geometry().execute(cv, ra.renderAtom, isNonUniformScale,
+ (useAlpha && ra.geometry().noAlpha),
+ rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.
+ ignoreVertexColors,
+ pass);
+ isVisible = true;
+ }
+ }
+ else {
+ if (ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) {
+ cv.updateState(pass, dirtyBits);
+ cv.raIsVisible = true;
+ trans = ra.localToVworld;
+ isNonUniformScale = !trans.isCongruent();
+
+ cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat, trans);
+ ra.geometry().execute(cv, ra.renderAtom, isNonUniformScale,
+ (useAlpha && ra.geometry().noAlpha),
+ rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.
+ ignoreVertexColors,
+ pass);
+ isVisible = true;
+ }
+ else {
+ cv.raIsVisible = false;
+ }
+ cv.ra = ra.renderAtom;
+
+ }
+ ra = ra.next;
+
+ }
+
+
+ geo.disableGlobalAlpha(cv.ctx,
+ (rm.useAlpha &&
+ ((geo.vertexFormat & GeometryArray.COLOR) != 0)),
+ rm.textureBin.attributeBin.ignoreVertexColors);
+ return isVisible;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/OrientedShape3DRetained.java b/src/classes/share/javax/media/j3d/OrientedShape3DRetained.java
new file mode 100644
index 0000000..a8fbd3a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/OrientedShape3DRetained.java
@@ -0,0 +1,592 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+class OrientedShape3DRetained extends Shape3DRetained {
+
+ static final int ALIGNMENT_CHANGED = LAST_DEFINED_BIT << 1;
+ static final int AXIS_CHANGED = LAST_DEFINED_BIT << 2;
+ static final int ROTATION_CHANGED = LAST_DEFINED_BIT << 3;
+ static final int CONSTANT_SCALE_CHANGED = LAST_DEFINED_BIT << 4;
+ static final int SCALE_FACTOR_CHANGED = LAST_DEFINED_BIT << 5;
+
+
+ int mode = OrientedShape3D.ROTATE_ABOUT_AXIS;
+
+ // Axis about which to rotate.
+ Vector3f axis = new Vector3f(0.0f, 1.0f, 0.0f);
+ Point3f rotationPoint = new Point3f(0.0f, 0.0f, 1.0f);
+ private Vector3d nAxis = new Vector3d(0.0, 1.0, 0.0); // normalized axis
+
+ // reused temporaries
+ private Point3d viewPosition = new Point3d();
+ private Point3d yUpPoint = new Point3d();
+
+ private Vector3d eyeVec = new Vector3d();
+ private Vector3d yUp = new Vector3d();
+ private Vector3d zAxis = new Vector3d();
+ private Vector3d yAxis = new Vector3d();
+ private Vector3d vector = new Vector3d();
+
+ private AxisAngle4d aa = new AxisAngle4d();
+
+ private Transform3D xform = new Transform3D(); // used several times
+ private Transform3D zRotate = new Transform3D();
+
+ // For scale invariant mode
+ boolean constantScale = false;
+ double scaleFactor = 1.0;
+
+ // Frequently used variables for scale invariant computation
+ // Left and right Vworld to Clip coordinates transforms
+ private Transform3D left_xform = new Transform3D();
+ private Transform3D right_xform = new Transform3D();
+
+ // Transform for scaling the OrientedShape3D to correct for
+ // perspective foreshortening
+ Transform3D scaleXform = new Transform3D();
+
+ // Variables for converting between clip to local world coords
+ private Vector4d im_vec[] = {new Vector4d(), new Vector4d()};
+ private Vector4d lvec = new Vector4d();
+
+ boolean orientedTransformDirty = true;
+
+ Transform3D[] orientedTransforms = new Transform3D[1];
+ static final double EPSILON = 1.0e-6;
+
+
+ /**
+ * Constructs a OrientedShape3D node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * alignment mode : ROTATE_ABOUT_AXIS<br>
+ * alignment axis : Y-axis (0,1,0)<br>
+ * rotation point : (0,0,1)<br>
+ *</ul>
+ */
+ public OrientedShape3DRetained() {
+ super();
+ this.nodeType = NodeRetained.ORIENTEDSHAPE3D;
+ }
+
+ // initializes alignment mode
+ void initAlignmentMode(int mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Sets the alignment mode.
+ * @param mode one of: ROTATE_ABOUT_AXIS or ROTATE_ABOUT_POINT
+ */
+ void setAlignmentMode(int mode) {
+ if (this.mode != mode) {
+ initAlignmentMode(mode);
+ sendChangedMessage(ALIGNMENT_CHANGED, new Integer(mode));
+ }
+ }
+
+ /**
+ * Retrieves the alignment mode.
+ * @return one of: ROTATE_ABOUT_AXIS or ROTATE_ABOUT_POINT
+ */
+ int getAlignmentMode() {
+ return(mode);
+ }
+
+ // initializes alignment axis
+ void initAlignmentAxis(Vector3f axis) {
+ initAlignmentAxis(axis.x, axis.y, axis.z);
+ }
+
+ // initializes alignment axis
+ void initAlignmentAxis(float x, float y, float z) {
+ this.axis.set(x,y,z);
+ double invMag;
+ invMag = 1.0/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
+ nAxis.x = (double)axis.x*invMag;
+ nAxis.y = (double)axis.y*invMag;
+ nAxis.z = (double)axis.z*invMag;
+ }
+
+ /**
+ * Sets the new alignment axis. This is the ray about which this
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_AXIS.
+ * @param axis the new alignment axis
+ */
+ void setAlignmentAxis(Vector3f axis) {
+ setAlignmentAxis(axis.x, axis.y, axis.z);
+ }
+
+ /**
+ * Sets the new alignment axis. This is the ray about which this
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_AXIS.
+ * @param x the x component of the alignment axis
+ * @param y the y component of the alignment axis
+ * @param z the z component of the alignment axis
+ */
+ void setAlignmentAxis(float x, float y, float z) {
+ initAlignmentAxis(x,y,z);
+
+ if (mode == OrientedShape3D.ROTATE_ABOUT_AXIS) {
+ sendChangedMessage(AXIS_CHANGED, new Vector3f(x,y,z));
+ }
+ }
+
+ /**
+ * Retrieves the alignment axis of this OrientedShape3D node,
+ * and copies it into the specified vector.
+ * @param axis the vector that will contain the alignment axis
+ */
+ void getAlignmentAxis(Vector3f axis) {
+ axis.set(this.axis);
+ }
+
+ // initializes rotation point
+ void initRotationPoint(Point3f point) {
+ rotationPoint.set(point);
+ }
+
+ // initializes rotation point
+ void initRotationPoint(float x, float y, float z) {
+ rotationPoint.set(x,y,z);
+ }
+
+ /**
+ * Sets the new rotation point. This is the point about which the
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_POINT.
+ * @param point the new rotation point
+ */
+ void setRotationPoint(Point3f point) {
+ setRotationPoint(point.x, point.y, point.z);
+ }
+
+ /**
+ * Sets the new rotation point. This is the point about which the
+ * OrientedShape3D rotates when the mode is ROTATE_ABOUT_POINT.
+ * @param x the x component of the rotation point
+ * @param y the y component of the rotation point
+ * @param z the z component of the rotation point
+ */
+ void setRotationPoint(float x, float y, float z) {
+ initRotationPoint(x,y,z);
+
+ if (mode == OrientedShape3D.ROTATE_ABOUT_POINT) {
+ sendChangedMessage(ROTATION_CHANGED, new Point3f(x,y,z));
+ }
+ }
+
+ /**
+ * Retrieves the rotation point of this OrientedShape3D node,
+ * and copies it into the specified vector.
+ * @param axis the point that will contain the rotation point
+ */
+ void getRotationPoint(Point3f point) {
+ point.set(rotationPoint);
+ }
+
+ void setConstantScaleEnable(boolean enable) {
+ if(constantScale != enable) {
+ initConstantScaleEnable(enable);
+ sendChangedMessage(CONSTANT_SCALE_CHANGED, new Boolean(enable));
+ }
+ }
+
+ boolean getConstantScaleEnable() {
+ return constantScale;
+ }
+
+ void initConstantScaleEnable(boolean cons_scale) {
+ constantScale = cons_scale;
+ }
+
+ void setScale(double scale) {
+ initScale(scale);
+ if(constantScale)
+ sendChangedMessage(SCALE_FACTOR_CHANGED, new Double(scale));
+ }
+
+ void initScale(double scale) {
+ scaleFactor = scale;
+ }
+
+ double getScale() {
+ return scaleFactor;
+ }
+
+ void sendChangedMessage(int component, Object attr) {
+ J3dMessage changeMessage = VirtualUniverse.mc.getMessage();
+ changeMessage.type = J3dMessage.ORIENTEDSHAPE3D_CHANGED;
+ changeMessage.threads = targetThreads ;
+ changeMessage.universe = universe;
+ changeMessage.args[0] = getGeomAtomsArray(mirrorShape3D);
+ changeMessage.args[1] = new Integer(component);
+ changeMessage.args[2] = attr;
+ OrientedShape3DRetained[] o3dArr =
+ new OrientedShape3DRetained[mirrorShape3D.size()];
+ mirrorShape3D.toArray(o3dArr);
+ changeMessage.args[3] = o3dArr;
+ changeMessage.args[4] = this;
+ VirtualUniverse.mc.processMessage(changeMessage);
+ }
+
+ void updateImmediateMirrorObject(Object[] args) {
+ int component = ((Integer)args[1]).intValue();
+ if ((component & (ALIGNMENT_CHANGED |
+ AXIS_CHANGED |
+ ROTATION_CHANGED |
+ CONSTANT_SCALE_CHANGED |
+ SCALE_FACTOR_CHANGED)) != 0) {
+ OrientedShape3DRetained[] msArr = (OrientedShape3DRetained[])args[3];
+ Object obj = args[2];
+ if ((component & ALIGNMENT_CHANGED) != 0) {
+ int mode = ((Integer)obj).intValue();
+ for (int i=0; i< msArr.length; i++) {
+ msArr[i].initAlignmentMode(mode);
+ }
+ }
+ else if ((component & AXIS_CHANGED) != 0) {
+ Vector3f axis =(Vector3f) obj;
+ for (int i=0; i< msArr.length; i++) {
+ msArr[i].initAlignmentAxis(axis);
+ }
+ }
+ else if ((component & ROTATION_CHANGED) != 0) {
+ Point3f point =(Point3f) obj;
+ for (int i=0; i< msArr.length; i++) {
+ msArr[i].initRotationPoint(point);
+ }
+ }
+ else if((component & CONSTANT_SCALE_CHANGED) != 0) {
+ boolean bool = ((Boolean)obj).booleanValue();
+ for (int i=0; i< msArr.length; i++) {
+ msArr[i].initConstantScaleEnable(bool);
+ }
+ }
+ else if((component & SCALE_FACTOR_CHANGED) != 0) {
+ double scale = ((Double)obj).doubleValue();
+ for (int i=0; i< msArr.length; i++) {
+ msArr[i].initScale(scale);
+ }
+ }
+ }
+ else {
+ super.updateImmediateMirrorObject(args);
+ }
+ }
+
+
+ Transform3D getOrientedTransform(int viewIndex) {
+ synchronized(orientedTransforms) {
+ if (viewIndex >= orientedTransforms.length) {
+ Transform3D xform = new Transform3D();
+ Transform3D[] newList = new Transform3D[viewIndex+1];
+ for (int i = 0; i < orientedTransforms.length; i++) {
+ newList[i] = orientedTransforms[i];
+ }
+ newList[viewIndex] = xform;
+ orientedTransforms = newList;
+ }
+ else {
+ if (orientedTransforms[viewIndex] == null) {
+ orientedTransforms[viewIndex] = new Transform3D();
+ }
+ }
+ }
+ return orientedTransforms[viewIndex];
+ }
+
+ // called on the parent object
+ // Should be synchronized so that the user thread does not modify the
+ // OrientedShape3D params while computing the transform
+ synchronized void updateOrientedTransform(Canvas3D canvas, int viewIndex) {
+ double angle = 0.0;
+ double sign;
+ boolean status;
+
+ Transform3D orientedxform = getOrientedTransform(viewIndex);
+ // get viewplatforms's location in virutal world
+ if (mode == OrientedShape3D.ROTATE_ABOUT_AXIS) { // rotate about axis
+ canvas.getCenterEyeInImagePlate(viewPosition);
+ canvas.getImagePlateToVworld(xform); // xform is imagePlateToLocal
+ xform.transform(viewPosition);
+
+ // get billboard's transform
+ xform.set(getCurrentLocalToVworld());
+ xform.invert(); // xform is now vWorldToLocal
+
+ // transform the eye position into the billboard's coordinate system
+ xform.transform(viewPosition);
+
+
+ // eyeVec is a vector from the local origin to the eye pt in local
+ eyeVec.set(viewPosition);
+ eyeVec.normalize();
+
+ // project the eye into the rotation plane
+ status = projectToPlane(eyeVec, nAxis);
+
+ if (status) {
+ // project the z axis into the rotation plane
+ zAxis.x = 0.0;
+ zAxis.y = 0.0;
+ zAxis.z = 1.0;
+ status = projectToPlane(zAxis, nAxis);
+ }
+ if (status) {
+
+ // compute the sign of the angle by checking if the cross product
+ // of the two vectors is in the same direction as the normal axis
+ vector.cross(eyeVec, zAxis);
+ if (vector.dot(nAxis) > 0.0) {
+ sign = 1.0;
+ } else {
+ sign = -1.0;
+ }
+
+ // compute the angle between the projected eye vector and the
+ // projected z
+
+ double dot = eyeVec.dot(zAxis);
+ if (dot > 1.0f) {
+ dot = 1.0f;
+ } else if (dot < -1.0f) {
+ dot = -1.0f;
+ }
+
+ angle = sign*Math.acos(dot);
+
+ // use -angle because xform is to *undo* rotation by angle
+ aa.x = nAxis.x;
+ aa.y = nAxis.y;
+ aa.z = nAxis.z;
+ aa.angle = -angle;
+ orientedxform.set(aa);
+ }
+ else {
+ orientedxform.setIdentity();
+ }
+
+ } else { // rotate about point
+ // Need to rotate Z axis to point to eye, and Y axis to be
+ // parallel to view platform Y axis, rotating around rotation pt
+
+ // get the eye point
+ canvas.getCenterEyeInImagePlate(viewPosition);
+
+ // derive the yUp point
+ yUpPoint.set(viewPosition);
+ yUpPoint.y += 0.01; // one cm in Physical space
+
+ // transform the points to the Billboard's space
+ canvas.getImagePlateToVworld(xform); // xform is ImagePlateToVworld
+ xform.transform(viewPosition);
+ xform.transform(yUpPoint);
+
+ // get billboard's transform
+ xform.set(getCurrentLocalToVworld());
+ xform.invert(); // xform is vWorldToLocal
+
+ // transfom points to local coord sys
+ xform.transform(viewPosition);
+ xform.transform(yUpPoint);
+
+ // Make a vector from viewPostion to 0,0,0 in the BB coord sys
+ eyeVec.set(viewPosition);
+ eyeVec.normalize();
+
+ // create a yUp vector
+ yUp.set(yUpPoint);
+ yUp.sub(viewPosition);
+ yUp.normalize();
+
+
+ // find the plane to rotate z
+ zAxis.x = 0.0;
+ zAxis.y = 0.0;
+ zAxis.z = 1.0;
+
+ // rotation axis is cross product of eyeVec and zAxis
+ vector.cross(eyeVec, zAxis); // vector is cross product
+
+ // if cross product is non-zero, vector is rotation axis and
+ // rotation angle is acos(eyeVec.dot(zAxis)));
+ double length = vector.length();
+ if (length > 0.0001) {
+ double dot = eyeVec.dot(zAxis);
+ if (dot > 1.0f) {
+ dot = 1.0f;
+ } else if (dot < -1.0f) {
+ dot = -1.0f;
+ }
+ angle = Math.acos(dot);
+ aa.x = vector.x;
+ aa.y = vector.y;
+ aa.z = vector.z;
+ aa.angle = -angle;
+ zRotate.set(aa);
+ } else {
+ // no rotation needed, set to identity (scale = 1.0)
+ zRotate.set(1.0);
+ }
+
+ // Transform the yAxis by zRotate
+ yAxis.x = 0.0;
+ yAxis.y = 1.0;
+ yAxis.z = 0.0;
+ zRotate.transform(yAxis);
+
+ // project the yAxis onto the plane perp to the eyeVec
+ status = projectToPlane(yAxis, eyeVec);
+
+
+ if (status) {
+ // project the yUp onto the plane perp to the eyeVec
+ status = projectToPlane(yUp, eyeVec);
+ }
+
+ if (status) {
+ // rotation angle is acos(yUp.dot(yAxis));
+ double dot = yUp.dot(yAxis);
+
+ // Fix numerical error, otherwise acos return NULL
+ if (dot > 1.0f) {
+ dot = 1.0f;
+ } else if (dot < -1.0f) {
+ dot = -1.0f;
+ }
+
+ angle = Math.acos(dot);
+
+ // check the sign by looking a the cross product vs the eyeVec
+ vector.cross(yUp, yAxis); // vector is cross product
+ if (eyeVec.dot(vector) < 0) {
+ angle *= -1;
+ }
+ aa.x = eyeVec.x;
+ aa.y = eyeVec.y;
+ aa.z = eyeVec.z;
+ aa.angle = -angle;
+ xform.set(aa); // xform is now yRotate
+
+ // rotate around the rotation point
+ vector.x = rotationPoint.x;
+ vector.y = rotationPoint.y;
+ vector.z = rotationPoint.z; // vector to translate to RP
+ orientedxform.set(vector); // translate to RP
+ orientedxform.mul(xform); // yRotate
+ orientedxform.mul(zRotate); // zRotate
+ vector.scale(-1.0); // vector to translate back
+ xform.set(vector); // xform to translate back
+ orientedxform.mul(xform); // translate back
+ }
+ else {
+ orientedxform.setIdentity();
+ }
+
+ }
+ //Scale invariant computation
+ if(constantScale) {
+ // Back Xform a unit vector to local world coords
+ canvas.getInverseVworldProjection(left_xform, right_xform);
+
+ // the two endpts of the vector have to be transformed
+ // individually because the Xform is not affine
+ im_vec[0].set(0.0, 0.0, 0.0, 1.0);
+ im_vec[1].set(1.0, 0.0, 0.0, 1.0);
+ left_xform.transform(im_vec[0]);
+ left_xform.transform(im_vec[1]);
+
+ left_xform.set(getCurrentLocalToVworld());
+ left_xform.invert();
+ left_xform.transform(im_vec[0]);
+ left_xform.transform(im_vec[1]);
+ lvec.set(im_vec[1]);
+ lvec.sub(im_vec[0]);
+
+ // We simply need the direction of this vector
+ lvec.normalize();
+ im_vec[0].set(0.0, 0.0, 0.0, 1.0);
+ im_vec[1].set(lvec);
+ im_vec[1].w = 1.0;
+
+ // Forward Xfrom to clip coords
+ left_xform.set(getCurrentLocalToVworld());
+ left_xform.transform(im_vec[0]);
+ left_xform.transform(im_vec[1]);
+
+ canvas.getVworldProjection(left_xform, right_xform);
+ left_xform.transform(im_vec[0]);
+ left_xform.transform(im_vec[1]);
+
+ // Perspective division
+ im_vec[0].x /= im_vec[0].w;
+ im_vec[0].y /= im_vec[0].w;
+ im_vec[0].z /= im_vec[0].w;
+
+ im_vec[1].x /= im_vec[1].w;
+ im_vec[1].y /= im_vec[1].w;
+ im_vec[1].z /= im_vec[1].w;
+
+ lvec.set(im_vec[1]);
+ lvec.sub(im_vec[0]);
+
+ // Use the length of this vector to determine the scaling
+ // factor
+ double scale = 1/lvec.length();
+
+ // Convert to meters
+ scale *= scaleFactor*canvas.getPhysicalWidth()/2;
+
+ // Scale object so that it appears the same size
+ scaleXform.setScale(scale);
+ orientedxform.mul(scaleXform);
+ }
+
+ }
+
+
+ private boolean projectToPlane(Vector3d projVec, Vector3d planeVec) {
+ double dis = planeVec.dot(projVec);
+ projVec.x = projVec.x - planeVec.x*dis;
+ projVec.y = projVec.y - planeVec.y*dis;
+ projVec.z = projVec.z - planeVec.z*dis;
+
+ double length = projVec.length();
+ if (length < EPSILON) { // projVec is parallel to planeVec
+ return false;
+ }
+ projVec.scale(1 / length);
+ return true;
+ }
+
+ void compile(CompileState compState) {
+
+ super.compile(compState);
+
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+
+ // don't push the static transform to orientedShape3D
+ // because orientedShape3D is rendered using vertex array;
+ // it's not worth pushing the transform here
+
+ compState.keepTG = true;
+ }
+
+ void searchGeometryAtoms(UnorderList list) {
+ list.add(getGeomAtom(getMirrorShape(key)));
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PathInterpolator.java b/src/classes/share/javax/media/j3d/PathInterpolator.java
new file mode 100644
index 0000000..f775459
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PathInterpolator.java
@@ -0,0 +1,278 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+
+/**
+ * PathInterpolator behavior. This class defines the base class for
+ * all Path Interpolators. Subclasses have access to the
+ * computePathInterpolation() method, which computes the
+ * currentInterpolationValue given the current time and alpha.
+ * The method also computes the currentKnotIndex, which is based on
+ * the currentInterpolationValue.
+ * The currentInterpolationValue is calculated
+ * by linearly interpolating among a series of predefined knots
+ * (using the value generated by the specified Alpha object).
+ * The first knot must have a value of 0.0 and the last knot must have a
+ * value of 1.0. An intermediate knot with index k must have a value
+ * strictly greater than any knot with index less than k.
+ */
+
+public abstract class PathInterpolator extends TransformInterpolator {
+
+ // Array of knots
+ private float knots[];
+
+ /**
+ * This value is the ratio between knot values indicated by
+ * the currentKnotIndex variable. So if a subclass wanted to
+ * interpolate between knot values, it would use the currentKnotIndex
+ * to get the bounding knots for the "real" value, then use the
+ * currentInterpolationValue to interpolate between the knots.
+ * To calculate this variable, a subclass needs to call
+ * the <code>computePathInterpolation(alphaValue)</code> method from the subclass's
+ * computeTransform() method. Then this variable will hold a valid
+ * value which can be used in further calculations by the subclass.
+ */
+ protected float currentInterpolationValue;
+
+ /**
+ * This value is the index of the current base knot value, as
+ * determined by the alpha function. A subclass wishing to
+ * interpolate between bounding knots would use this index and
+ * the one following it, and would use the currentInterpolationValue
+ * variable as the ratio between these indices.
+ * To calculate this variable, a subclass needs to call
+ * the <code>computePathInterpolation(alphaValue)</code> method from the subclass's
+ * computeTransform() method. Then this variable will hold a valid
+ * value which can be used in further calculations by the subclass.
+ */
+ protected int currentKnotIndex;
+
+
+ /**
+ * Constructs a PathInterpolator node with a null alpha value and
+ * a null target of TransformGroup
+ *
+ * since Java 3D 1.3
+ */
+ PathInterpolator() {
+ }
+
+
+ /**
+ * Constructs a new PathInterpolator object that interpolates
+ * between the knot values in the knots array. The array of knots
+ * is copied into this PathInterpolator object.
+ * @param alpha the alpha object for this interpolator.
+ * @param knots an array of knot values that specify interpolation
+ * points.
+ *
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>PathInterpolator(Alpha, TransformGroup, float[]) </code>
+ */
+ public PathInterpolator(Alpha alpha, float[] knots) {
+ this(alpha, null, knots);
+ }
+
+ /**
+ * Constructs a new PathInterpolator object that interpolates
+ * between the knot values in the knots array. The array of knots
+ * is copied into this PathInterpolator object.
+ * @param alpha the alpha object for this interpolator.
+ * @param target the transformgroup node effected by this pathInterpolator
+ * @param knots an array of knot values that specify interpolation
+ * points.
+ *
+ * @since Java 3D 1.3
+ */
+
+ public PathInterpolator(Alpha alpha,TransformGroup target,
+ float[] knots) {
+ super(alpha, target);
+ setKnots(knots);
+ }
+
+
+ /**
+ * Constructs a new PathInterpolator object that interpolates
+ * between the knot values in the knots array. The array of knots
+ * is copied into this PathInterpolator object.
+ * @param alpha the alpha object for this interpolator.
+ * @param target the transform node effected by this positionInterpolator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * @param knots an array of knot values that specify interpolation
+ * points.
+ *
+ * @since Java 3D 1.3
+ */
+ public PathInterpolator(Alpha alpha,TransformGroup target, Transform3D axisOfTransform,
+ float[] knots) {
+ super(alpha, target, axisOfTransform);
+ setKnots(knots);
+ }
+
+ /**
+ * Retrieves the length of the knots array.
+ * @return the array length
+ */
+ public int getArrayLengths(){
+ return knots.length;
+ }
+
+
+ /**
+ * Sets the knot at the specified index for this interpolator.
+ * @param index the index to be changed
+ * @param knot the new knot value
+ */
+ public void setKnot(int index, float knot) {
+ this.knots[index] = knot;
+ }
+
+
+ /**
+ * Retrieves the knot at the specified index.
+ * @param index the index of the value requested
+ * @return the interpolator's knot value at the associated index
+ */
+ public float getKnot(int index) {
+ return this.knots[index];
+ }
+
+
+ /**
+ * Replaces the existing array of knot values with
+ * the specified array. The array of knots is copied into this
+ * interpolator object. Prior to calling this method,
+ * subclasses should verify that the lengths of the new knots array
+ * and subclass-specific parameter arrays are the same.
+ * @param knots a new array of knot values that specify
+ * interpolation points.
+ *
+ * @since Java 3D 1.2
+ */
+ protected void setKnots(float[] knots) {
+ if (knots[0] < -0.0001 || knots[0] > 0.0001) {
+ throw new IllegalArgumentException(J3dI18N.getString("PathInterpolator0"));
+ }
+
+ if ((knots[knots.length-1] - 1.0f) < -0.0001 || (knots[knots.length-1] - 1.0f) > 0.0001) {
+ throw new IllegalArgumentException(J3dI18N.getString("PathInterpolator1"));
+ }
+
+ this.knots = new float[knots.length];
+ for (int i = 0; i < knots.length; i++) {
+ if (i>0 && knots[i] < knots[i-1]) {
+ throw new IllegalArgumentException(J3dI18N.getString("PathInterpolator2"));
+ }
+ this.knots[i] = knots[i];
+ }
+ }
+
+
+ /**
+ * Copies the array of knots from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the knots.
+ * @param knots array that will receive the knots.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getKnots(float[] knots) {
+ for (int i = 0; i < this.knots.length; i++) {
+ knots[i] = this.knots[i];
+ }
+ }
+
+ /**
+ * Computes the base knot index and interpolation value
+ * given the specified value of alpha and the knots[] array. If
+ * the index is 0 and there should 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 currentInterpolationValue
+ * variable is set to the ratio of the alpha value between these
+ * two bounding knot points.
+ * @param alphaValue alpha value between 0.0 and 1.0
+ *
+ * @since Java 3D 1.3
+ */
+ protected void computePathInterpolation(float alphaValue ) {
+
+ int i;
+
+ for (i = 0; i < knots.length; i++) {
+ if ((i == 0 && alphaValue <= knots[i]) ||
+ (i > 0 && alphaValue >= knots[i-1] && alphaValue <= knots[i])) {
+
+ if (i==0) {
+ currentInterpolationValue = 0f;
+ currentKnotIndex = 0;
+ }
+ else {
+ currentInterpolationValue =
+ (alphaValue - knots[i-1])/(knots[i] - knots[i-1]);
+ currentKnotIndex = i - 1;
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>computePathInterpolation(float)</code>
+ */
+ protected void computePathInterpolation() {
+ float value = this.alpha.value();
+ computePathInterpolation(value);
+ }
+
+
+ /**
+ * Copies all PathInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ PathInterpolator pi = (PathInterpolator) originalNode;
+
+ int len = pi.getArrayLengths();
+
+ // No API available to set knots size
+ knots = new float[len];
+
+ for (int i = 0; i < len; i++)
+ setKnot(i, pi.getKnot(i));
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PhysicalBody.java b/src/classes/share/javax/media/j3d/PhysicalBody.java
new file mode 100644
index 0000000..1f08fb7
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PhysicalBody.java
@@ -0,0 +1,356 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+/**
+ * This object contains a specification of the user's head.
+ * Attributes of this object are defined in the head coordinate system.
+ * The orgin is defined to be halfway between the left and right eye
+ * in the plane of the face.
+ * The x-axis extends to the right (of the head looking out from the head).
+ * The y-axis extends up. The z-axis extends to the rear of the head.
+ *
+ * @see View
+ */
+
+public class PhysicalBody extends Object {
+ // The X offset for each eye is 1/2 of the inter-pupilary distance
+ // This constant specifies the default IPD.
+ private static final double HALF_IPD = 0.033;
+
+ // These offsets specify the default ear positions relative to the
+ // "center eye".
+ private static final double EAR_X = 0.080;
+ private static final double EAR_Y = -0.030;
+ private static final double EAR_Z = 0.095;
+
+ /**
+ * The user's left eye's position in head coordinates.
+ */
+ Point3d leftEyePosition = new Point3d(-HALF_IPD, 0.0, 0.0);
+
+ /**
+ * The user's right eye's position in head coordinates.
+ */
+ Point3d rightEyePosition = new Point3d(HALF_IPD, 0.0, 0.0);
+
+ /**
+ * The user's left ear's position in head coordinates.
+ */
+ Point3d leftEarPosition = new Point3d(-EAR_X, EAR_Y, EAR_Z);
+
+ /**
+ * The user's right ear's position in head coordinates.
+ */
+ Point3d rightEarPosition = new Point3d(EAR_X, EAR_Y, EAR_Z);
+
+ /**
+ * The user's nominal eye height as measured
+ * from the ground plane.
+ */
+ double nominalEyeHeightFromGround = 1.68;
+
+ /**
+ * The amount to offset the system's
+ * viewpoint from the user's current eye-point. This offset
+ * distance allows an "Over the shoulder" view of the scene
+ * as seen by the user.
+ *
+ * By default, we will use a Z value of 0.4572 meters (18 inches).
+ */
+ double nominalEyeOffsetFromNominalScreen = 0.4572;
+
+ // Head to head-tracker coordinate system transform.
+ // If head tracking is enabled, this transform is a calibration
+ // constant. If head tracking is not enabled, this transform is
+ // not used.
+ // This is used in both SCREEN_VIEW and HMD_VIEW modes.
+ Transform3D headToHeadTracker = new Transform3D();
+
+ // A list of View Objects that refer to this
+ ArrayList users = new ArrayList();
+
+ // Mask that indicates this PhysicalBody's view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int pbDirtyMask = (View.PB_EYE_POSITION_DIRTY
+ | View.PB_EAR_POSITION_DIRTY
+ | View.PB_NOMINAL_EYE_HEIGHT_FROM_GROUND_DIRTY
+ | View.PB_NOMINAL_EYE_OFFSET_FROM_NOMINAL_SCREEN_DIRTY);
+
+ /**
+ * Constructs a PhysicalBody object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * left eye position : (-0.033, 0.0, 0.0)<br>
+ * right eye position : (0.033, 0.0, 0.0)<br>
+ * left ear position : (-0.080, -0.030, 0.095)<br>
+ * right ear position : (0.080, -0.030, 0.095)<br>
+ * nominal eye height from ground : 1.68<br>
+ * nominal eye offset from nominal screen : 0.4572<br>
+ * head to head tracker transform : identity<br>
+ * </ul>
+ */
+ public PhysicalBody() {
+ // Just use the defaults
+ initHeadToHeadTracker();
+ }
+
+ // Add a user to the list of users
+ synchronized void removeUser(View view) {
+ int idx = users.indexOf(view);
+ if (idx >= 0) {
+ users.remove(idx);
+ }
+ }
+
+ // Add a user to the list of users
+ synchronized void addUser(View view) {
+ int idx = users.indexOf(view);
+ if (idx < 0) {
+ users.add(view);
+ }
+ }
+
+ // Add a user to the list of users
+ synchronized void notifyUsers() {
+ for (int i=users.size()-1; i>=0; i--) {
+ View view = (View)users.get(i);
+ // TODO: notifyUsers should have a parameter denoting field changed
+ if (view.soundScheduler != null) {
+ view.soundScheduler.setListenerFlag(
+ SoundScheduler.EAR_POSITIONS_CHANGED |
+ SoundScheduler.EYE_POSITIONS_CHANGED );
+ }
+ view.repaint();
+ }
+ }
+
+ /**
+ * Constructs and initializes a PhysicalBody object from the
+ * specified parameters.
+ * @param leftEyePosition the user's left eye position
+ * @param rightEyePosition the user's right eye position
+ */
+ public PhysicalBody(Point3d leftEyePosition, Point3d rightEyePosition) {
+ this.leftEyePosition.set(leftEyePosition);
+ this.rightEyePosition.set(rightEyePosition);
+ initHeadToHeadTracker();
+ }
+
+ /**
+ * Constructs and initializes a PhysicalBody object from the
+ * specified parameters.
+ * @param leftEyePosition the user's left eye position
+ * @param rightEyePosition the user's right eye position
+ * @param leftEarPosition the user's left ear position
+ * @param rightEarPosition the user's right ear position
+ */
+ public PhysicalBody(Point3d leftEyePosition,
+ Point3d rightEyePosition,
+ Point3d leftEarPosition,
+ Point3d rightEarPosition) {
+
+ this.leftEyePosition.set(leftEyePosition);
+ this.rightEyePosition.set(rightEyePosition);
+ this.leftEarPosition.set(leftEarPosition);
+ this.rightEarPosition.set(rightEarPosition);
+ initHeadToHeadTracker();
+ }
+
+ /**
+ * Returns a string representation of this PhysicalBody's values.
+ */
+
+ public String toString() {
+ return "eyePosition = (" + this.leftEyePosition + ", " +
+ this.rightEyePosition + ")\n" +
+ "earPosition = (" + this.leftEarPosition + ", " +
+ this.rightEarPosition + ")";
+ }
+
+ /**
+ * Retrieves the user head object's left eye position and places
+ * that value in the specified object.
+ * @param position the object that will receive the left-eye's position
+ * in head coordinates
+ */
+ public void getLeftEyePosition(Point3d position) {
+ position.set(this.leftEyePosition);
+ }
+
+ /**
+ * Sets the user head object's left eye position.
+ * @param position the left-eye's position in head coordinates
+ */
+ public void setLeftEyePosition(Point3d position) {
+ synchronized(this) {
+ this.leftEyePosition.set(position);
+ pbDirtyMask |= View.PB_EYE_POSITION_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the user head object's right eye position and places
+ * that value in the specified object.
+ * @param position the object that will receive the right-eye's position
+ * in head coordinates
+ */
+ public void getRightEyePosition(Point3d position) {
+ position.set(this.rightEyePosition);
+ }
+
+ /**
+ * Sets the user head object's right eye position.
+ * @param position the right-eye's position in head coordinates
+ */
+ public void setRightEyePosition(Point3d position) {
+ synchronized(this) {
+ this.rightEyePosition.set(position);
+ pbDirtyMask |= View.PB_EYE_POSITION_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the user head object's left ear position and places
+ * that value in the specified object.
+ * @param position the object that will receive the left-ear's position
+ * in head coordinates
+ */
+ public void getLeftEarPosition(Point3d position) {
+ position.set(this.leftEarPosition);
+ }
+
+ /**
+ * Sets the user head object's left ear position.
+ * @param position the left-ear's position in head coordinates
+ */
+ public void setLeftEarPosition(Point3d position) {
+ synchronized(this) {
+ this.leftEarPosition.set(position);
+ pbDirtyMask |= View.PB_EAR_POSITION_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the user head object's right ear position and places
+ * that value in the specified object.
+ * @param position the object that will receive the right-ear's position
+ * in head coordinates
+ */
+ public void getRightEarPosition(Point3d position) {
+ position.set(this.rightEarPosition);
+ }
+
+ /**
+ * Sets the user head object's right ear position.
+ * @param position the right-ear's position in head coordinates
+ */
+ public void setRightEarPosition(Point3d position) {
+ synchronized(this) {
+ this.rightEarPosition.set(position);
+ pbDirtyMask |= View.PB_EAR_POSITION_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Sets the nominal eye height from the ground plane.
+ * This parameter defines
+ * the distance from the origin of the user's head (the eyepoint) to
+ * the ground.
+ * It is used when the view attach policy is NOMINAL_FEET.
+ * @param height the nominal height of the eye above the ground plane
+ */
+ public void setNominalEyeHeightFromGround(double height) {
+ synchronized(this) {
+ nominalEyeHeightFromGround = height;
+ pbDirtyMask |= View.PB_NOMINAL_EYE_HEIGHT_FROM_GROUND_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the nominal eye height from the ground plane.
+ * @return the current nominal eye height above the ground plane
+ */
+ public double getNominalEyeHeightFromGround() {
+ return nominalEyeHeightFromGround;
+ }
+
+ /**
+ * Sets the nominal eye offset from the display screen.
+ * This parameter defines
+ * the distance from the origin of the user's head (the eyepoint), in it's
+ * nominal position, to
+ * the screen.
+ * It is used when the view attach policy is NOMINAL_HEAD or NOMINAL_FEET.
+ * This value is overridden to be the actual eyepoint when the window
+ * eyepoint policy is RELATIVE_TO_FIELD_OF_VIEW.
+ * @param offset the nominal offset from the eye to the screen
+ */
+ public void setNominalEyeOffsetFromNominalScreen(double offset) {
+ synchronized(this) {
+ nominalEyeOffsetFromNominalScreen = offset;
+ pbDirtyMask |= View.PB_NOMINAL_EYE_OFFSET_FROM_NOMINAL_SCREEN_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the nominal eye offset from the display screen.
+ * @return the current nominal offset from the eye to the display screen
+ */
+ public double getNominalEyeOffsetFromNominalScreen() {
+ return nominalEyeOffsetFromNominalScreen;
+ }
+
+ /**
+ * Sets the head to head-tracker coordinate system transform.
+ * If head tracking is enabled, this transform is a calibration
+ * constant. If head tracking is not enabled, this transform is
+ * not used.
+ * This is used in both SCREEN_VIEW and HMD_VIEW modes.
+ * @param t the new transform
+ * @exception BadTransformException if the transform is not rigid
+ */
+ public void setHeadToHeadTracker(Transform3D t) {
+ if (!t.isRigid()) {
+ throw new BadTransformException(J3dI18N.getString("PhysicalBody0"));
+ }
+ headToHeadTracker.setWithLock(t);
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the head to head-tracker coordinate system transform.
+ * @param t the object that will receive the transform
+ */
+ public void getHeadToHeadTracker(Transform3D t) {
+ t.set(headToHeadTracker);
+ }
+
+ // Initialize the head to head-tracker transform
+ private void initHeadToHeadTracker() {
+ // By default the center of the crystal eyes tracker is 20mm down
+ // and 35 mm closer to the screen from the origin of head coordinates
+ // (the center eye).
+ Vector3d v = new Vector3d(0.0, 0.020, 0.035);
+ headToHeadTracker.set(v);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PhysicalEnvironment.java b/src/classes/share/javax/media/j3d/PhysicalEnvironment.java
new file mode 100644
index 0000000..c3cf51c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PhysicalEnvironment.java
@@ -0,0 +1,513 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.*;
+import java.util.*;
+
+/**
+ * This object contains a specification of the physical environment in
+ * which the view will be generated. It is used to set up input
+ * devices (sensors) for head-tracking and other uses, and the audio
+ * output device. Sensors are indexed starting at zero.
+ *
+ * @see View
+ */
+
+public class PhysicalEnvironment extends Object {
+ /**
+ * The Sensor Index associated with the Head
+ */
+ int HeadIndex = 0;
+
+ // The Sensor index associated with the Right Hand
+ int RightHandIndex = 1;
+
+ // The Sensor index associated with the Left Hand
+ int LeftHandIndex = 2;
+
+ // The current Dominant Hand Sensor Index
+ int DominantHandIndex = 1;
+
+ // The current Non Dominant Hand Sensor Index
+ int NonDominantHandIndex = 2;
+
+ //
+ // Coexistence coordinate system to tracker-base coordinate
+ // system transform. If head tracking is enabled, this transform
+ // is a calibration constant. If head tracking is not enabled,
+ // this transform is not used.
+ // This is used in both SCREEN_VIEW and HMD_VIEW modes.
+ //
+ Transform3D coexistenceToTrackerBase = new Transform3D();
+
+ //
+ // Indicates whether the underlying hardware implementation
+ // supports tracking.
+ //
+ boolean trackingAvailable = false;
+
+ // The view associated with this physical environment
+ // View view;
+
+ //
+ // This variable specifies the policy Java 3D will use in placing
+ // the user's eye position relative to the user's head position
+ // (NOMINAL_SCREEN, NOMINAL_HEAD, or NOMINAL_FEET).
+ // It is used in the calibration process.
+ //
+ // TODO: this needs better explanation in the spec
+ int coexistenceCenterInPworldPolicy = View.NOMINAL_SCREEN;
+
+ // Mask that indicates this PhysicalEnv's view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int peDirtyMask = (View.PE_COE_TO_TRACKER_BASE_DIRTY
+ | View.PE_TRACKING_AVAILABLE_DIRTY
+ | View.PE_COE_CENTER_IN_PWORLD_POLICY_DIRTY);
+
+
+//// /**
+//// * The offset in the user's dominant-hand-tracker coordinates
+//// * to that hand's hot spot. This value is a calibration constant.
+//// */
+//// Vector3d dominantHandTrackerHotspotOffset;
+////
+//// /**
+//// * The offset in the user's non-dominant-hand-tracker coordinates
+//// * to that hand's hot spot. This value is a calibration constant.
+//// */
+//// Vector3d nondominantHandTrackerHotspotOffset;
+
+ //
+ // The number of sensor stored within the PhysicalEnvironment
+ //
+ int sensorCount;
+
+ //
+ // Array of sensors
+ //
+ Sensor[] sensors;
+
+ // Audio device associated with this PhysicalEnvironment
+ AudioDevice audioDevice = null;
+
+ boolean sensorListChanged = false;
+
+ Sensor[] sensorList = null;
+
+ // A list of View Objects that refer to this
+ ArrayList users = new ArrayList();
+
+ // Scheduler for input devices
+ InputDeviceScheduler inputsched;
+
+ // store all inputDevices
+ Vector devices = new Vector(1);
+
+ // Number of active view users
+ int activeViewRef = 0;
+
+ // Hashtable that maps a PhysicalEnvironment to its InputDeviceScheduler
+ static Hashtable physicalEnvMap = new Hashtable();
+
+ /**
+ * Constructs a PhysicalEnvironment object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * sensor count : 3<br>
+ * sensors : null (for all array elements)<br>
+ * head index : 0<br>
+ * right hand index : 1<br>
+ * left hand index : 2<br>
+ * dominant hand index : 1<br>
+ * nondominant hand index : 2<br>
+ * tracking available : false<br>
+ * audio device : null<br>
+ * input device list : empty<br>
+ * coexistence to tracker base transform : identity<br>
+ * coexistence center in pworld policy : View.NOMINAL_SCREEN<br>
+ * </ul>
+ */
+ public PhysicalEnvironment() {
+ this(3);
+ }
+
+ // Add a user to the list of users
+ synchronized void removeUser(View view) {
+ int idx = users.indexOf(view);
+ if (idx >= 0) {
+ users.remove(idx);
+ }
+ }
+
+ // Add a user to the list of users
+ synchronized void addUser(View view) {
+ int idx = users.indexOf(view);
+ if (idx < 0) {
+ users.add(view);
+ }
+ }
+
+ // Add a user to the list of users
+ synchronized void notifyUsers() {
+ for (int i=users.size()-1; i>=0; i--) {
+ View view = (View)users.get(i);
+ view.repaint();
+ }
+ }
+
+ /**
+ * Constructs and initializes a PhysicalEnvironment object with
+ * the specified number of sensors.
+ * @param sensorCount the number of sensors to create.
+ */
+ public PhysicalEnvironment(int sensorCount) {
+ this.sensorCount = sensorCount;
+ sensors = new Sensor[sensorCount];
+ for(int i=sensorCount-1; i>=0; i--) {
+ sensors[i] = null;
+ }
+ }
+
+
+
+ /**
+ * Returns copy of Sensor references. Returns null for zero
+ * sensors, so user of method must check for null. Also, any of
+ * these sensors could be null.
+ */
+ Sensor[] getSensorList() {
+ synchronized(sensors) {
+ if(sensorListChanged) { // note: this is a rare case
+ sensorList = new Sensor[sensors.length];
+ for(int i=0 ; i<sensors.length ; i++) {
+ sensorList[i] = sensors[i];
+ }
+ sensorListChanged = false;
+
+ }
+ return sensorList;
+ }
+ }
+
+
+ /**
+ * Sets the specified AudioDevice object as the device through
+ * which audio rendering for this PhysicalEnvironment will be
+ * performed.
+ * @param device audio device object to be associated with this
+ * PhysicalEnvironment
+ */
+ public void setAudioDevice(AudioDevice device) {
+ audioDevice = device;
+ }
+
+ /**
+ * Gets the audioDevice for this PhysicalEnvironment.
+ * @return audio device object associated with this PhysicalEnvironment
+ */
+ public AudioDevice getAudioDevice(){
+ return audioDevice;
+ }
+
+ /**
+ * Create an enumerator that produces all input devices.
+ * @return an enumerator of all available devices
+ */
+ public Enumeration getAllInputDevices() {
+ return devices.elements();
+ }
+
+ /**
+ * Add an input device to the list of input devices. User is
+ * responsible for initializing the device and setting the
+ * processing mode (streaming or polling).
+ * @param device the device to be added to the list of input devices
+ * @exception IllegalArgumentException if InputDevice.getProcessingMode()
+ * does not return one of BLOCKING, NON_BLOCKING, or DEMAND_DRIVEN.
+ */
+ public void addInputDevice(InputDevice device) {
+
+ int driver_type = device.getProcessingMode();
+
+ if ((driver_type == InputDevice.BLOCKING) ||
+ (driver_type == InputDevice.NON_BLOCKING) ||
+ (driver_type == InputDevice.DEMAND_DRIVEN)) {
+ synchronized (devices) {
+ devices.add(device);
+ if (inputsched != null) {
+ inputsched.addInputDevice(device);
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(J3dI18N.getString("PhysicalEnvironment0"));
+ }
+ }
+
+ /**
+ * Remove an input device from the list of input devices.
+ * User is responsible for closing out the device and releasing
+ * the device resources.
+ * @param device the device to be removed
+ */
+ public void removeInputDevice(InputDevice device) {
+ devices.remove(device);
+ synchronized (devices) {
+ if (inputsched != null) {
+ inputsched.removeInputDevice(device);
+ }
+ }
+ }
+
+ /**
+ * Sets the index of the head to the specified sensor index.
+ * @param index the new sensor index of the head
+ */
+ public void setHeadIndex(int index) {
+ HeadIndex = index;
+ synchronized(this) {
+ computeTrackingAvailable();
+ peDirtyMask |= View.PE_TRACKING_AVAILABLE_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Gets the sensor index of the head.
+ * @return the sensor index of the head
+ */
+ public int getHeadIndex() {
+ return HeadIndex;
+ }
+
+ /**
+ * Sets the index of the right hand to the specified sensor index.
+ * @param index the new sensor index of the right hand
+ */
+ public void setRightHandIndex(int index) {
+ RightHandIndex = index;
+ notifyUsers();
+ }
+
+ /**
+ * Gets the sensor index of the right hand.
+ * @return the sensor index of the right hand
+ */
+ public int getRightHandIndex() {
+ return RightHandIndex;
+ }
+
+ /**
+ * Sets the index of the left hand to the specified sensor index.
+ * @param index the new sensor index of the left hand
+ */
+ public void setLeftHandIndex(int index) {
+ LeftHandIndex = index;
+ notifyUsers();
+ }
+
+ /**
+ * Gets the sensor index of the left hand.
+ * @return the sensor index of the left hand
+ */
+ public int getLeftHandIndex() {
+ return LeftHandIndex;
+ }
+
+ /**
+ * Sets the index of the dominant hand to the specified sensor index.
+ * @param index the new sensor index of the dominant hand
+ */
+ public void setDominantHandIndex(int index) {
+ DominantHandIndex = index;
+ notifyUsers();
+ }
+
+ /**
+ * Gets the sensor index of the dominant hand.
+ * @return the sensor index of the dominant hand
+ */
+ public int getDominantHandIndex() {
+ return DominantHandIndex;
+ }
+
+ /**
+ * Sets the index of the non-dominant hand to the specified sensor index.
+ * @param index the new sensor index of the non dominant hand
+ */
+ public void setNonDominantHandIndex(int index) {
+ NonDominantHandIndex = index;
+ notifyUsers();
+ }
+
+ /**
+ * Gets the sensor index of the non-dominant hand.
+ * @return the sensor index of the non dominant hand
+ */
+ public int getNonDominantHandIndex() {
+ return NonDominantHandIndex;
+ }
+
+ /**
+ * Set the sensor specified by the index to sensor provided; sensors are
+ * indexed starting at 0. All sensors must be registered via this
+ * method.
+ * @param index the sensor's index
+ * @param sensor the new sensor
+ */
+ public void setSensor(int index, Sensor sensor) {
+ synchronized(sensors) {
+ sensors[index] = sensor;
+ sensorListChanged = true;
+ }
+ synchronized(this) {
+ computeTrackingAvailable();
+ peDirtyMask |= View.PE_TRACKING_AVAILABLE_DIRTY;
+ }
+
+ notifyUsers();
+ }
+
+ /**
+ * Gets the sensor specified by the index; sensors are indexed starting
+ * at 0.
+ * @param index the sensor's index
+ */
+ public Sensor getSensor(int index){
+ // not synchronized, since the only way to write to sensors is
+ // via a public API call, and user shouldn't call Sensor with
+ // two threads
+ return sensors[index];
+ }
+
+ /**
+ * Sets the coexistence coordinate system to tracker-base coordinate
+ * system transform. If head tracking is enabled, this transform
+ * is a calibration constant. If head tracking is not enabled,
+ * this transform is not used.
+ * This is used in both SCREEN_VIEW and HMD_VIEW modes.
+ * @param t the new transform
+ * @exception BadTransformException if the transform is not rigid
+ */
+ public void setCoexistenceToTrackerBase(Transform3D t) {
+ if (!t.isRigid()) {
+ throw new BadTransformException(J3dI18N.getString("PhysicalEnvironment1"));
+ }
+ synchronized(this) {
+ coexistenceToTrackerBase.setWithLock(t);
+ peDirtyMask |= View.PE_COE_TO_TRACKER_BASE_DIRTY;
+ }
+
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the coexistence coordinate system to tracker-base
+ * coordinate system transform and copies it into the specified
+ * Transform3D object.
+ * @param t the object that will receive the transform
+ */
+ public void getCoexistenceToTrackerBase(Transform3D t) {
+ t.set(coexistenceToTrackerBase);
+ }
+
+ /**
+ * Returns a status flag indicating whether or not tracking
+ * is available.
+ * @return a flag telling whether tracking is available
+ */
+ public boolean getTrackingAvailable() {
+ return this.trackingAvailable;
+ }
+
+ /**
+ * Sets the coexistence center in physical world policy.
+ * This setting determines how Java 3D places the
+ * user's eye point as a function of head position during the
+ * calibration process, one of View.NOMINAL_SCREEN,
+ * View.NOMINAL_HEAD, or View.NOMINAL_FEET.
+ * The default policy is View.NOMINAL_SCREEN.
+ * @param policy the new policy
+ */
+ public void setCoexistenceCenterInPworldPolicy(int policy) {
+ switch (policy) {
+ case View.NOMINAL_SCREEN:
+ case View.NOMINAL_HEAD:
+ case View.NOMINAL_FEET:
+ break;
+
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("PhysicalEnvironment2"));
+ }
+
+ synchronized(this) {
+ this.coexistenceCenterInPworldPolicy = policy;
+ peDirtyMask |= View.PE_COE_CENTER_IN_PWORLD_POLICY_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Returns the current coexistence center in physical world policy.
+ * @return one of: View.NOMINAL_SCREEN, View.NOMINAL_HEAD, or
+ * View.NOMINAL_FEET
+ */
+ public int getCoexistenceCenterInPworldPolicy() {
+ return this.coexistenceCenterInPworldPolicy;
+ }
+
+ /**
+ * Get the current sensor count.
+ * @return the number of sensor objects per PhysicalEnvironment object
+ */
+ public int getSensorCount() {
+ return sensorCount;
+ }
+
+ /**
+ * Set the number of sensor objects per PhysicalEnvironmnet. This is a
+ * calibration parameter that should be set before setting any sensors
+ * in the PhysicalEnvironment object. This call associates 'count'
+ * Sensors with this object, and they are indexed from 0 to count-1.
+ * @param count the new sensor count
+ */
+ public void setSensorCount(int count) {
+
+ Sensor[] tmp = new Sensor[count];
+ int i=0;
+
+ synchronized(sensors) {
+ int min = Math.min(count, sensorCount);
+ while(i < min) {
+ tmp[i] = sensors[i++];
+ }
+ while(i < count) {
+ tmp[i++] = null;
+ }
+ sensorCount = count;
+ sensorListChanged = true;
+ sensors = tmp;
+ }
+ notifyUsers();
+ }
+
+ // (re-)compute the tracking available flag
+ private void computeTrackingAvailable() {
+ synchronized(sensors) {
+ trackingAvailable = ((HeadIndex < sensors.length) &&
+ (sensors[HeadIndex] != null));
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/PickBounds.java b/src/classes/share/javax/media/j3d/PickBounds.java
new file mode 100644
index 0000000..6316749
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickBounds.java
@@ -0,0 +1,89 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * PickBounds is a finite pick shape defined with a Bounds object. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ */
+public final class PickBounds extends PickShape {
+
+ Bounds bounds;
+
+ /**
+ * Constructs an empty PickBounds. The bounds object is set to null.
+ */
+ public PickBounds() {
+ bounds = null;
+ }
+
+ /**
+ * Constructs a PickBounds from the specified bounds object.
+ * @param boundsObject the bounds of this PickBounds.
+ */
+ public PickBounds(Bounds boundsObject) {
+ bounds = boundsObject;
+ }
+
+
+ /**
+ * Sets the bounds object of this PickBounds to the specified object.
+ * @param boundsObject the new bounds of this PickBounds.
+ */
+ public void set(Bounds boundsObject) {
+ bounds = boundsObject;
+ }
+
+ /**
+ * Gets the bounds object from this PickBounds.
+ * @return the bounds.
+ */
+ public Bounds get() {
+ return bounds;
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+ return bounds.intersect(this.bounds, pickPos);
+ }
+
+ // Only use within J3D.
+ // Return a new PickBounds that is the transformed (t3d) of this pickBounds.
+ PickShape transform(Transform3D t3d) {
+ // If the bounds is a BoundingBox, then the transformed bounds will
+ // get bigger. So this is a potential bug, and we'll have to deal with
+ // if there is a complain.
+ Bounds newBds = (Bounds)bounds.clone();
+ newBds.transform(t3d);
+ PickBounds newPB = new PickBounds(newBds);
+
+ return newPB;
+ }
+
+ Point3d getStartPoint() {
+ return bounds.getCenter();
+ }
+
+ int getPickType() {
+ return (bounds != null ? bounds.getPickType() :
+ PickShape.PICKUNKNOWN);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickCone.java b/src/classes/share/javax/media/j3d/PickCone.java
new file mode 100644
index 0000000..b9940bd
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickCone.java
@@ -0,0 +1,89 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * PickCone is the abstract base class of all cone pick shapes.
+ *
+ * @since Java 3D 1.2
+ */
+public abstract class PickCone extends PickShape {
+
+ Point3d origin;
+ Vector3d direction;
+ double spreadAngle;
+
+ /**
+ * Constructs an empty PickCone.
+ * The origin and direction of the cone are
+ * initialized to (0,0,0). The spread angle is initialized
+ * to <code>PI/64</code>.
+ */
+ public PickCone() {
+ this.origin = new Point3d();
+ this.direction = new Vector3d();
+ this.spreadAngle = Math.PI / 64.0;
+ }
+
+ /**
+ * Gets the origin of this PickCone.
+ * @param origin the Point3d object into which the origin will be copied.
+ */
+ public void getOrigin(Point3d origin) {
+ origin.set(this.origin);
+ }
+
+ /**
+ * Gets the direction of this PickCone.
+ * @param direction the Vector3d object into which the direction
+ * will be copied.
+ */
+ public void getDirection(Vector3d direction) {
+ direction.set(this.direction);
+ }
+
+
+ /**
+ * Gets the spread angle of this PickCone.
+ * @return the spread angle.
+ */
+ public double getSpreadAngle() {
+ return spreadAngle;
+ }
+
+ /**
+ * Gets the radius of this PickCone at the specified distance.
+ * @param distance the distance from the origin at which we want
+ * the radius of the cone
+ * @return the radius at the specified distance
+ */
+ double getRadius(double distance) {
+ return distance * Math.tan (spreadAngle);
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ */
+ abstract boolean intersect(Bounds bounds, Point4d pickPos);
+
+ Point3d getStartPoint() {
+ return origin;
+ }
+
+ int getPickType() {
+ return PICKCONE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickConeRay.java b/src/classes/share/javax/media/j3d/PickConeRay.java
new file mode 100644
index 0000000..e420e59
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickConeRay.java
@@ -0,0 +1,266 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import com.sun.j3d.internal.Distance;
+
+/**
+ * PickConeRay is an infinite cone ray pick shape. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ *
+ * @since Java 3D 1.2
+ */
+public final class PickConeRay extends PickCone {
+
+ /**
+ * Constructs an empty PickConeRay.
+ * The origin and direction of the cone are
+ * initialized to (0,0,0). The spread angle is initialized
+ * to <code>PI/64</code> radians.
+ */
+ public PickConeRay() {
+ }
+
+ /**
+ * Constructs an infinite cone pick shape from the specified
+ * parameters.
+ * @param origin the origin of the cone
+ * @param direction the direction of the cone
+ * @param spreadAngle the spread angle of the cone in radians
+ */
+ public PickConeRay(Point3d origin, Vector3d direction, double spreadAngle) {
+ this.origin = new Point3d(origin);
+ this.direction = new Vector3d(direction);
+ this.spreadAngle = spreadAngle;
+ }
+
+ /**
+ * Sets the parameters of this PickCone to the specified values.
+ * @param origin the origin of the cone
+ * @param direction the direction of the cone
+ * @param spreadAngle the spread angle of the cone in radians
+ */
+ public void set(Point3d origin, Vector3d direction, double spreadAngle) {
+ this.origin.set(origin);
+ this.direction.set(direction);
+ this.spreadAngle = spreadAngle;
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ * @param bounds the bounds object to check
+ * @param pickPos the location of the point of intersection (not used for
+ * method. Provided for compatibility).
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+
+ Point4d iPnt = new Point4d();
+ Vector3d vector = new Vector3d();
+ double distance;
+ double radius;
+ Point3d rayPt = new Point3d();
+
+ //
+ // ================ BOUNDING SPHERE ================
+ //
+ if (bounds instanceof BoundingSphere) {
+ Point3d sphCenter = ((BoundingSphere)bounds).getCenter();
+ double sphRadius = ((BoundingSphere)bounds).getRadius();
+ double sqDist =
+ Distance.pointToRay (sphCenter, origin, direction, rayPt, null);
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+ if (sqDist <= (sphRadius+radius)*(sphRadius+radius)) {
+ return true;
+ }
+ return false; // we are too far to intersect
+ }
+ //
+ // ================ BOUNDING BOX ================
+ //
+ else if (bounds instanceof BoundingBox) {
+ // Calculate radius of BoundingBox
+ Point3d lower = new Point3d();
+ ((BoundingBox)bounds).getLower (lower);
+
+ Point3d center = ((BoundingBox)bounds).getCenter ();
+
+ // First, see if cone is too far away from BoundingBox
+ double sqDist =
+ Distance.pointToRay (center, origin, direction, rayPt, null);
+
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+
+ double temp = (center.x - lower.x + radius);
+ double boxRadiusSquared = temp*temp;
+ temp = (center.y - lower.y + radius);
+ boxRadiusSquared += temp*temp;
+ temp = (center.z - lower.z + radius);
+ boxRadiusSquared += temp*temp;
+
+
+ if (sqDist > boxRadiusSquared) {
+ return false; // we are too far to intersect
+ }
+ else if (sqDist < (radius*radius)) {
+ return true; // center is in cone
+ }
+
+ // Then, see if ray intersects
+ if (((BoundingBox)bounds).intersect (origin, direction, iPnt)) {
+ return true;
+ }
+
+ // Ray does not intersect, test for distance with each edge
+ Point3d upper = new Point3d();
+ ((BoundingBox)bounds).getUpper (upper);
+
+ Point3d[][] edges = {
+ // Top horizontal 4
+ {upper, new Point3d (lower.x, upper.y, upper.z)},
+ {new Point3d(lower.x, upper.y, upper.z), new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, lower.y, upper.z), new Point3d(upper.x, lower.y, upper.z)},
+ {new Point3d(upper.x, lower.y, upper.z), upper},
+ // Bottom horizontal 4
+ {lower, new Point3d(lower.x, upper.y, lower.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(upper.x, upper.y, lower.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, lower.y, lower.z)},
+ {new Point3d(upper.x, lower.y, lower.z), lower},
+ // Vertical 4
+ {lower, new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(lower.x, upper.y, upper.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, upper.y, upper.z)},
+ {new Point3d(upper.x, lower.y, lower.z), new Point3d(upper.x, lower.y, upper.z)}
+ };
+
+ for (int i=0;i<edges.length;i++) {
+ // System.out.println ("Testing edge: "+edges[i][0]+" - "+edges[i][1]);
+ double distToEdge =
+ Distance.rayToSegment (origin, direction, edges[i][0], edges[i][1],
+ rayPt, null, null);
+
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+ // System.out.println ("PickConeRay: distance: "+distance+" radius:"+radius);
+ if (distToEdge <= radius*radius) {
+ // System.out.println ("Intersects!");
+ return true;
+ }
+ }
+ return false; // Not close enough
+ }
+ //
+ // ================ BOUNDING POLYTOPE ================
+ //
+ else if (bounds instanceof BoundingPolytope) {
+ int i, j;
+
+ // First, check to see if we are too far to intersect the polytope's
+ // bounding sphere
+ Point3d sphCenter = new Point3d();
+ BoundingSphere bsphere = new BoundingSphere (bounds);
+
+ bsphere.getCenter (sphCenter);
+ double sphRadius = bsphere.getRadius();
+
+ double sqDist =
+ Distance.pointToRay (sphCenter, origin, direction, rayPt, null);
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+ if (sqDist > (sphRadius+radius)*(sphRadius+radius)) {
+ return false; // we are too far to intersect
+ }
+
+ // Now check to see if ray intersects with polytope
+ if (bounds.intersect (origin, direction, iPnt)) {
+ return true;
+ }
+
+ // Now check distance to edges. Since we don't know a priori how
+ // the polytope is structured, we will cycle through. We discard edges
+ // when their center is not on the polytope surface.
+ BoundingPolytope ptope = (BoundingPolytope)bounds;
+ Point3d midpt = new Point3d();
+ double distToEdge;
+ for (i=0;i<ptope.nVerts;i++) {
+ for (j=i;i<ptope.nVerts;i++) {
+ // TODO: make BoundingPolytope.pointInPolytope available to package
+ // scope
+ midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
+ midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
+ midpt.z = (ptope.verts[i].z + ptope.verts[j].z) * 0.5;
+
+ if (! PickCylinder.pointInPolytope (ptope,
+ midpt.x, midpt.y, midpt.z)) {
+ continue;
+ }
+ distToEdge =
+ Distance.rayToSegment (origin, direction,
+ ptope.verts[i], ptope.verts[j],
+ rayPt, null, null);
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+ if (distToEdge <= radius*radius) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ /*
+ else {
+ throw new RuntimeException("intersect method not implemented");
+ }
+ */
+ return false;
+ }
+
+ // Only use within J3D.
+ // Return a new PickConeRay that is the transformed (t3d) of this pickConeRay.
+ PickShape transform(Transform3D t3d) {
+
+ Point3d end = new Point3d();
+
+ PickConeRay newPCR = new PickConeRay();
+
+ newPCR.origin.x = origin.x;
+ newPCR.origin.y = origin.y;
+ newPCR.origin.z = origin.z;
+ newPCR.spreadAngle = spreadAngle;
+
+ end.x = origin.x + direction.x;
+ end.y = origin.y + direction.y;
+ end.z = origin.z + direction.z;
+
+ t3d.transform(newPCR.origin);
+ t3d.transform(end);
+
+ newPCR.direction.x = end.x - newPCR.origin.x;
+ newPCR.direction.y = end.y - newPCR.origin.y;
+ newPCR.direction.z = end.z - newPCR.origin.z;
+ newPCR.direction.normalize();
+
+ return newPCR;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickConeSegment.java b/src/classes/share/javax/media/j3d/PickConeSegment.java
new file mode 100644
index 0000000..f839040
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickConeSegment.java
@@ -0,0 +1,292 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import com.sun.j3d.internal.Distance;
+
+/**
+ * PickConeSegment is a finite cone segment pick shape. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ *
+ * @since Java 3D 1.2
+ */
+public final class PickConeSegment extends PickCone {
+
+ Point3d end;
+
+ /**
+ * Constructs an empty PickConeSegment.
+ * The origin and end point of the cone are
+ * initialized to (0,0,0). The spread angle is initialized
+ * to <code>PI/64</code> radians.
+ */
+ public PickConeSegment() {
+ end = new Point3d();
+ }
+
+ /**
+ * Constructs a finite cone pick shape from the specified
+ * parameters.
+ * @param origin the origin of the cone
+ * @param end the end of the cone along the direction vector
+ * @param spreadAngle the spread angle of the cone in radians
+ */
+ public PickConeSegment (Point3d origin, Point3d end, double spreadAngle) {
+ this.origin = new Point3d(origin);
+ this.end = new Point3d(end);
+ this.direction = new Vector3d();
+ this.spreadAngle = spreadAngle;
+ calcDirection(); // calculate direction, based on start and end
+ }
+
+ /**
+ * Sets the parameters of this PickCone to the specified values.
+ * @param origin the origin of the cone
+ * @param end the end of the cone
+ * @param spreadAngle the spread angle of the cone in radians
+ */
+ public void set(Point3d origin, Point3d end, double spreadAngle) {
+ this.origin.set(origin);
+ this.end.set (end);
+ this.spreadAngle = spreadAngle;
+ calcDirection(); // calculate direction, based on start and end
+ }
+
+ /**
+ * Gets the end point of this PickConeSegment.
+ * @param end the Point3d object into which the end point
+ * will be copied.
+ */
+ public void getEnd(Point3d end) {
+ end.set(this.end);
+ }
+
+ /** Calculates the direction for this PickCylinderSegment, based on start
+ and end points.
+ */
+ private void calcDirection() {
+ this.direction.x = end.x - origin.x;
+ this.direction.y = end.y - origin.y;
+ this.direction.z = end.z - origin.z;
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ * @param bounds the bounds object to check
+ * @param pickPos the location of the point of intersection (not used for
+ * method. Provided for compatibility).
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+ Point4d iPnt = new Point4d();
+ Vector3d vector = new Vector3d();
+ Point3d rayPt = new Point3d();
+
+ double distance;
+ double radius;
+
+ //
+ // ================ BOUNDING SPHERE ================
+ //
+ if (bounds instanceof BoundingSphere) {
+ Point3d sphCenter = ((BoundingSphere)bounds).getCenter();
+ double sphRadius = ((BoundingSphere)bounds).getRadius();
+ double sqDist =
+ Distance.pointToSegment (sphCenter, origin, end, rayPt, null);
+
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+ if (sqDist <= (sphRadius+radius)*(sphRadius+radius)) {
+ return true;
+ }
+ return false; // we are too far to intersect
+ }
+ //
+ // ================ BOUNDING BOX ================
+ //
+ else if (bounds instanceof BoundingBox) {
+ // Calculate radius of BoundingBox
+ Point3d lower = new Point3d();
+ ((BoundingBox)bounds).getLower (lower);
+
+ Point3d center = ((BoundingBox)bounds).getCenter ();
+
+ // First, see if cone is too far away from BoundingBox
+ double sqDist =
+ Distance.pointToSegment (center, origin, end, rayPt, null);
+
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+
+ double temp = (center.x - lower.x + radius);
+ double boxRadiusSquared = temp*temp;
+ temp = (center.y - lower.y + radius);
+ boxRadiusSquared += temp*temp;
+ temp = (center.z - lower.z + radius);
+ boxRadiusSquared += temp*temp;
+
+ if (sqDist > boxRadiusSquared) {
+ return false; // we are too far to intersect
+ }
+ else if (sqDist < (radius*radius)) {
+ return true; // center is in cone
+ }
+
+ // Then, see if ray intersects
+ if (((BoundingBox)bounds).intersect (origin, direction, iPnt)) {
+ return true;
+ }
+
+ // Ray does not intersect, test for distance with each edge
+ Point3d upper = new Point3d();
+ ((BoundingBox)bounds).getUpper (upper);
+
+ Point3d[][] edges = {
+ // Top horizontal 4
+ {upper, new Point3d (lower.x, upper.y, upper.z)},
+ {new Point3d(lower.x, upper.y, upper.z), new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, lower.y, upper.z), new Point3d(upper.x, lower.y, upper.z)},
+ {new Point3d(upper.x, lower.y, upper.z), upper},
+ // Bottom horizontal 4
+ {lower, new Point3d(lower.x, upper.y, lower.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(upper.x, upper.y, lower.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, lower.y, lower.z)},
+ {new Point3d(upper.x, lower.y, lower.z), lower},
+ // Vertical 4
+ {lower, new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(lower.x, upper.y, upper.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, upper.y, upper.z)},
+ {new Point3d(upper.x, lower.y, lower.z), new Point3d(upper.x, lower.y, upper.z)}
+ };
+ for (int i=0;i<edges.length;i++) {
+ // System.out.println ("Testing edge: "+edges[i][0]+" - "+edges[i][1]);
+ double distToEdge =
+ Distance.segmentToSegment (origin, end, edges[i][0], edges[i][1],
+ rayPt, null, null);
+
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+ /* System.out.println ("PickConeSegment: distance: " +
+ distance+" radius: " + radius +
+ " distToEdge:" +Math.sqrt(distToEdge));
+ */
+
+ if (distToEdge <= radius*radius) {
+ // System.out.println ("Intersects!");
+ return true;
+ }
+ }
+ return false; // Not close enough
+ }
+ //
+ // ================ BOUNDING POLYTOPE ================
+ //
+ else if (bounds instanceof BoundingPolytope) {
+ int i, j;
+
+ // First, check to see if we are too far to intersect the polytope's
+ // bounding sphere
+ Point3d sphCenter = new Point3d();
+ BoundingSphere bsphere = new BoundingSphere (bounds);
+
+ bsphere.getCenter (sphCenter);
+ double sphRadius = bsphere.getRadius();
+
+ double sqDist =
+ Distance.pointToSegment (sphCenter, origin, end, rayPt, null);
+
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+
+ if (sqDist > (sphRadius+radius)*(sphRadius+radius)) {
+ return false; // we are too far to intersect
+ }
+
+ // Now check to see if ray intersects with polytope
+ if (bounds.intersect (origin, direction, iPnt)) {
+ return true;
+ }
+
+ // Now check distance to edges. Since we don't know a priori how
+ // the polytope is structured, we will cycle through. We discard edges
+ // when their center is not on the polytope surface.
+ BoundingPolytope ptope = (BoundingPolytope)bounds;
+ Point3d midpt = new Point3d();
+ double distToEdge;
+ for (i=0;i<ptope.nVerts;i++) {
+ for (j=i;i<ptope.nVerts;i++) {
+ // TODO: make BoundingPolytope.pointInPolytope available to package
+ // scope
+ midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
+ midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
+ midpt.z = (ptope.verts[i].z + ptope.verts[j].z) * 0.5;
+
+ if (! PickCylinder.pointInPolytope (ptope,
+ midpt.x, midpt.y, midpt.z)) {
+ continue;
+ }
+ distToEdge =
+ Distance.segmentToSegment (origin, end,
+ ptope.verts[i], ptope.verts[j],
+ rayPt, null, null);
+ vector.sub (rayPt, origin);
+ distance = vector.length();
+ radius = getRadius (distance);
+ if (distToEdge <= radius*radius) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ /*
+ else {
+ throw new RuntimeException("intersect method not implemented");
+ }
+ */
+ return false;
+ }
+
+ // Only use within J3D.
+ // Return a new PickConeSegment that is the transformed (t3d) of this pickConeSegment.
+ PickShape transform(Transform3D t3d) {
+
+ PickConeSegment newPCS = new PickConeSegment();
+
+ newPCS.origin.x = origin.x;
+ newPCS.origin.y = origin.y;
+ newPCS.origin.z = origin.z;
+ newPCS.spreadAngle = spreadAngle;
+ newPCS.end.x = end.x;
+ newPCS.end.y = end.y;
+ newPCS.end.z = end.z;
+
+ t3d.transform(newPCS.origin);
+ t3d.transform(newPCS.end);
+
+ newPCS.direction.x = newPCS.end.x - newPCS.origin.x;
+ newPCS.direction.y = newPCS.end.y - newPCS.origin.y;
+ newPCS.direction.z = newPCS.end.z - newPCS.origin.z;
+ newPCS.direction.normalize();
+
+ return newPCS;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickCylinder.java b/src/classes/share/javax/media/j3d/PickCylinder.java
new file mode 100644
index 0000000..85d1161
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickCylinder.java
@@ -0,0 +1,97 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * PickCylinder is the abstract base class of all cylindrical pick shapes.
+ *
+ * @since Java 3D 1.2
+ */
+public abstract class PickCylinder extends PickShape {
+
+ Point3d origin;
+ Vector3d direction;
+ double radius;
+
+ /**
+ * Constructs an empty PickCylinder.
+ * The origin of the cylinder is
+ * initialized to (0,0,0). The radius is initialized
+ * to 0.
+ */
+ public PickCylinder() {
+ origin = new Point3d();
+ direction = new Vector3d();
+ radius = 0.0;
+ }
+
+ /**
+ * Gets the origin point of this cylinder object.
+ * @param origin the Point3d object into which the origin
+ * point will be copied
+ */
+ public void getOrigin(Point3d origin) {
+ origin.set(this.origin);
+ }
+
+ /**
+ * Gets the radius of this cylinder object
+ * @return the radius in radians
+ */
+ public double getRadius() {
+ return radius;
+ }
+
+ /**
+ * Gets the direction of this cylinder.
+ * @param direction the Vector3d object into which the direction
+ * will be copied
+ */
+ public void getDirection(Vector3d direction) {
+ direction.set(this.direction);
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ */
+ abstract boolean intersect(Bounds bounds, Point4d pickPos);
+
+ // This is a duplicate of the same method, declared private inside of
+ // BoundingPolytope
+ // TODO: remove this once the original method is available (public) in
+ // BoundingPolytope
+ static boolean pointInPolytope(BoundingPolytope ptope,
+ double x, double y, double z ){
+ Vector4d p;
+ int i = ptope.planes.length - 1;
+
+ while (i >= 0) {
+ p = ptope.planes[i--];
+ if (( x*p.x + y*p.y + z*p.z + p.w ) > Bounds.EPSILON) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ Point3d getStartPoint() {
+ return origin;
+ }
+
+ int getPickType() {
+ return PICKCYLINDER;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickCylinderRay.java b/src/classes/share/javax/media/j3d/PickCylinderRay.java
new file mode 100644
index 0000000..3c58782
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickCylinderRay.java
@@ -0,0 +1,248 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import com.sun.j3d.internal.Distance;
+
+/**
+ * PickCylinderRay is an infinite cylindrical ray pick shape. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ *
+ * @since Java 3D 1.2
+ */
+
+public final class PickCylinderRay extends PickCylinder {
+
+ /**
+ * Constructs an empty PickCylinderRay.
+ * The origin and direction of the cylindrical ray are
+ * initialized to (0,0,0). The radius is initialized
+ * to 0.
+ */
+ public PickCylinderRay() {
+ }
+
+ /**
+ * Constructs an infinite cylindrical ray pick shape from the specified
+ * parameters.
+ * @param origin the origin of the cylindrical ray.
+ * @param direction the direction of the cylindrical ray.
+ * @param radius the radius of the cylindrical ray.
+ */
+ public PickCylinderRay(Point3d origin, Vector3d direction, double radius) {
+ this.origin = new Point3d(origin);
+ this.direction = new Vector3d(direction);
+ this.radius = radius;
+ }
+
+
+ /**
+ * Sets the parameters of this PickCylinderRay to the specified values.
+ * @param origin the origin of the cylindrical ray.
+ * @param direction the direction of the cylindrical ray.
+ * @param radius the radius of the cylindrical ray.
+ */
+ public void set(Point3d origin, Vector3d direction, double radius) {
+ this.origin.set(origin);
+ this.direction.set(direction);
+ this.radius = radius;
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ * @param bounds the bounds object to check
+ * @param pickPos the location of the point of intersection (not used for
+ * method. Provided for compatibility).
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+ Point4d iPnt = new Point4d();
+
+ //
+ // ================ BOUNDING SPHERE ================
+ //
+ if (bounds instanceof BoundingSphere) {
+ Point3d sphCenter = ((BoundingSphere)bounds).getCenter();
+ double sphRadius = ((BoundingSphere)bounds).getRadius();
+ double sqDist =
+ Distance.pointToRay (sphCenter, origin, direction);
+ if (sqDist <= (sphRadius+radius)*(sphRadius+radius)) {
+ return true;
+ }
+ return false;
+ }
+ //
+ // ================ BOUNDING BOX ================
+ //
+ else if (bounds instanceof BoundingBox) {
+ // Calculate radius of BoundingBox
+ Point3d lower = new Point3d();
+ ((BoundingBox)bounds).getLower (lower);
+
+ Point3d center = ((BoundingBox)bounds).getCenter ();
+
+ double temp = (center.x - lower.x + radius);
+ double boxRadiusSquared = temp*temp;
+ temp = (center.y - lower.y + radius);
+ boxRadiusSquared += temp*temp;
+ temp = (center.z - lower.z + radius);
+ boxRadiusSquared += temp*temp;
+
+ // First, see if cylinder is too far away from BoundingBox
+ double sqDist =
+ Distance.pointToRay (center, origin, direction);
+
+ if (sqDist > boxRadiusSquared ) {
+ return false; // we are too far to intersect
+ }
+ else if (sqDist < (radius*radius)) {
+ return true; // center is in cylinder
+ }
+
+ // Then, see if ray intersects
+ if (bounds.intersect (origin, direction, iPnt)) {
+ return true;
+ }
+
+ // Ray does not intersect, test for distance with each edge
+ Point3d upper = new Point3d();
+ ((BoundingBox)bounds).getUpper (upper);
+
+ Point3d[][] edges = {
+ // Top horizontal 4
+ {upper, new Point3d (lower.x, upper.y, upper.z)},
+ {new Point3d(lower.x, upper.y, upper.z), new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, lower.y, upper.z), new Point3d(upper.x, lower.y, upper.z)},
+ {new Point3d(upper.x, lower.y, upper.z), upper},
+ // Bottom horizontal 4
+ {lower, new Point3d(lower.x, upper.y, lower.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(upper.x, upper.y, lower.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, lower.y, lower.z)},
+ {new Point3d(upper.x, lower.y, lower.z), lower},
+ // Vertical 4
+ {lower, new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(lower.x, upper.y, upper.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, upper.y, upper.z)},
+ {new Point3d(upper.x, lower.y, lower.z), new Point3d(upper.x, lower.y, upper.z)}
+ };
+
+ for (int i=0;i<edges.length;i++) {
+ // System.out.println ("Testing edge: "+edges[i][0]+" - "+edges[i][1]);
+ double distToEdge =
+ Distance.rayToSegment (origin, direction, edges[i][0], edges[i][1]);
+ if (distToEdge <= radius*radius) {
+ // System.out.println ("Intersects!");
+ return true;
+ }
+ }
+
+ return false; // Not close enough
+ }
+ //
+ // ================ BOUNDING POLYTOPE ================
+ //
+ else if (bounds instanceof BoundingPolytope) {
+ int i, j;
+
+ // First, check to see if we are too far to intersect the polytope's
+ // bounding sphere
+ Point3d sphCenter = new Point3d();
+ BoundingSphere bsphere = new BoundingSphere (bounds);
+
+ bsphere.getCenter (sphCenter);
+ double sphRadius = bsphere.getRadius();
+
+ double sqDist =
+ Distance.pointToRay (sphCenter, origin, direction);
+ if (sqDist > (sphRadius+radius) * (sphRadius+radius)) {
+ return false; // we are too far to intersect
+ }
+
+ // Now check to see if ray intersects with polytope
+ if (bounds.intersect (origin, direction, iPnt)) {
+ return true;
+ }
+
+ // Now check distance to edges. Since we don't know a priori how
+ // the polytope is structured, we will cycle through. We discard edges
+ // when their center is not on the polytope surface.
+ BoundingPolytope ptope = (BoundingPolytope)bounds;
+ Point3d midpt = new Point3d();
+ double distToEdge;
+ for (i=0;i<ptope.nVerts;i++) {
+ for (j=i;i<ptope.nVerts;i++) {
+ // TODO: make BoundingPolytope.pointInPolytope available to package
+ // scope
+ midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
+ midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
+ midpt.z = (ptope.verts[i].z + ptope.verts[j].z) * 0.5;
+
+ if (! PickCylinder.pointInPolytope (ptope,
+ midpt.x, midpt.y, midpt.z)) {
+ continue;
+ }
+ distToEdge =
+ Distance.rayToSegment (origin, direction,
+ ptope.verts[i], ptope.verts[j]);
+ if (distToEdge <= radius*radius) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ /*
+ else {
+ throw new RuntimeException("intersect method not implemented");
+ }
+ */
+ return false;
+ }
+
+
+ // Only use within J3D.
+ // Return a new PickCylinderRay that is the transformed (t3d) of this pickCylinderRay.
+ PickShape transform(Transform3D t3d) {
+
+ PickCylinderRay newPCR = new PickCylinderRay();
+ Point3d end = new Point3d();
+ /*
+ System.out.println("t3d : ");
+ System.out.println(t3d);
+ */
+ newPCR.origin.x = origin.x;
+ newPCR.origin.y = origin.y;
+ newPCR.origin.z = origin.z;
+ newPCR.radius = radius * t3d.getScale();
+
+ end.x = origin.x + direction.x;
+ end.y = origin.y + direction.y;
+ end.z = origin.z + direction.z;
+
+ t3d.transform(newPCR.origin);
+ t3d.transform(end);
+
+ newPCR.direction.x = end.x - newPCR.origin.x;
+ newPCR.direction.y = end.y - newPCR.origin.y;
+ newPCR.direction.z = end.z - newPCR.origin.z;
+ newPCR.direction.normalize();
+
+ return newPCR;
+ }
+
+}
+
diff --git a/src/classes/share/javax/media/j3d/PickCylinderSegment.java b/src/classes/share/javax/media/j3d/PickCylinderSegment.java
new file mode 100644
index 0000000..b6d2af8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickCylinderSegment.java
@@ -0,0 +1,262 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import com.sun.j3d.internal.Distance;
+
+/**
+ * PickCylinderSegment is a finite cylindrical segment pick shape. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ *
+ * @since Java 3D 1.2
+ */
+
+public final class PickCylinderSegment extends PickCylinder {
+
+ Point3d end;
+
+ /**
+ * Constructs an empty PickCylinderSegment.
+ * The origin and end points of the cylindrical segment are
+ * initialized to (0,0,0). The radius is initialized
+ * to 0.
+ */
+ public PickCylinderSegment() {
+ this.end = new Point3d();
+ }
+
+ /**
+ * Constructs a finite cylindrical segment pick shape from the specified
+ * parameters.
+ * @param origin the origin point of the cylindrical segment.
+ * @param end the end point of the cylindrical segment.
+ * @param radius the radius of the cylindrical segment.
+ */
+ public PickCylinderSegment(Point3d origin, Point3d end, double radius) {
+ this.origin = new Point3d(origin);
+ this.end = new Point3d(end);
+ this.radius = radius;
+ calcDirection(); // calculate direction, based on start and end
+ }
+
+ /**
+ * Sets the parameters of this PickCylinderSegment to the specified values.
+ * @param origin the origin point of the cylindrical segment.
+ * @param end the end point of the cylindrical segment.
+ * @param radius the radius of the cylindrical segment.
+ */
+ public void set(Point3d origin, Point3d end, double radius) {
+ this.origin.set(origin);
+ this.end.set(end);
+ this.radius = radius;
+ calcDirection(); // calculate direction, based on start and end
+ }
+
+ /** Calculates the direction for this PickCylinderSegment, based on start
+ and end points.
+ */
+ private void calcDirection() {
+ this.direction.x = end.x - origin.x;
+ this.direction.y = end.y - origin.y;
+ this.direction.z = end.z - origin.z;
+ }
+
+ /**
+ * Gets the end point of this PickCylinderSegment.
+ * @param end the Point3d object into which the end point
+ * will be copied.
+ */
+ public void getEnd(Point3d end) {
+ end.set(this.end);
+ }
+
+ /**
+ * Returns true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ * @param bounds the bounds object to check
+ * @param pickPos the location of the point of intersection (not used for
+ * method. Provided for compatibility).
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+ Point4d iPnt = new Point4d();
+
+ //
+ // ================ BOUNDING SPHERE ================
+ //
+ if (bounds instanceof BoundingSphere) {
+ Point3d sphCenter = ((BoundingSphere)bounds).getCenter();
+ double sphRadius = ((BoundingSphere)bounds).getRadius();
+ double sqDist =
+ Distance.pointToSegment (sphCenter, origin, end);
+ if (sqDist <= (sphRadius+radius)*(sphRadius+radius)) {
+ return true;
+ }
+ return false; // we are too far to intersect
+ }
+ //
+ // ================ BOUNDING BOX ================
+ //
+ else if (bounds instanceof BoundingBox) {
+ // Calculate radius of BoundingBox
+ Point3d lower = new Point3d();
+ ((BoundingBox)bounds).getLower (lower);
+
+ Point3d center = ((BoundingBox)bounds).getCenter ();
+
+ double temp = (center.x - lower.x + radius);
+ double boxRadiusSquared = temp*temp;
+ temp = (center.y - lower.y + radius);
+ boxRadiusSquared += temp*temp;
+ temp = (center.z - lower.z + radius);
+ boxRadiusSquared += temp*temp;
+
+ // First, see if cylinder is too far away from BoundingBox
+ double sqDist =
+ Distance.pointToSegment (center, origin, end);
+ if (sqDist > boxRadiusSquared) {
+ return false; // we are too far to intersect
+ }
+ else if (sqDist < (radius*radius)) {
+ return true; // center is in cylinder
+ }
+
+ // Then, see if ray intersects
+ if (((BoundingBox)bounds).intersect (origin, end, iPnt)) {
+ return true;
+ }
+
+ // Ray does not intersect, test for distance with each edge
+ Point3d upper = new Point3d();
+ ((BoundingBox)bounds).getUpper (upper);
+
+ Point3d[][] edges = {
+ // Top horizontal 4
+ {upper, new Point3d (lower.x, upper.y, upper.z)},
+ {new Point3d(lower.x, upper.y, upper.z), new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, lower.y, upper.z), new Point3d(upper.x, lower.y, upper.z)},
+ {new Point3d(upper.x, lower.y, upper.z), upper},
+ // Bottom horizontal 4
+ {lower, new Point3d(lower.x, upper.y, lower.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(upper.x, upper.y, lower.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, lower.y, lower.z)},
+ {new Point3d(upper.x, lower.y, lower.z), lower},
+ // Vertical 4
+ {lower, new Point3d(lower.x, lower.y, upper.z)},
+ {new Point3d(lower.x, upper.y, lower.z), new Point3d(lower.x, upper.y, upper.z)},
+ {new Point3d(upper.x, upper.y, lower.z), new Point3d(upper.x, upper.y, upper.z)},
+ {new Point3d(upper.x, lower.y, lower.z), new Point3d(upper.x, lower.y, upper.z)}
+ };
+ for (int i=0;i<edges.length;i++) {
+ // System.out.println ("Testing edge: "+edges[i][0]+" - "+edges[i][1]);
+ double distToEdge =
+ Distance.segmentToSegment (origin, end,
+ edges[i][0], edges[i][1]);
+ if (distToEdge <= radius*radius) {
+ // System.out.println ("Intersects!");
+ return true;
+ }
+ }
+ return false; // Not close enough
+ }
+ //
+ // ================ BOUNDING POLYTOPE ================
+ //
+ else if (bounds instanceof BoundingPolytope) {
+ int i, j;
+
+ // First, check to see if we are too far to intersect the polytope's
+ // bounding sphere
+ Point3d sphCenter = new Point3d();
+ BoundingSphere bsphere = new BoundingSphere (bounds);
+
+ bsphere.getCenter (sphCenter);
+ double sphRadius = bsphere.getRadius();
+
+ double sqDist =
+ Distance.pointToSegment (sphCenter, origin, end);
+ if (sqDist > (sphRadius+radius)*(sphRadius+radius)) {
+ return false; // we are too far to intersect
+ }
+
+ // Now check to see if ray intersects with polytope
+ if (bounds.intersect (origin, direction, iPnt)) {
+ return true;
+ }
+
+ // Now check distance to edges. Since we don't know a priori how
+ // the polytope is structured, we will cycle through. We discard edges
+ // when their center is not on the polytope surface.
+ BoundingPolytope ptope = (BoundingPolytope)bounds;
+ Point3d midpt = new Point3d();
+ double distToEdge;
+ for (i=0;i<ptope.nVerts;i++) {
+ for (j=i;i<ptope.nVerts;i++) {
+ // TODO: make BoundingPolytope.pointInPolytope available to package
+ // scope
+ midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
+ midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
+ midpt.z = (ptope.verts[i].z + ptope.verts[j].z) * 0.5;
+
+ if (! PickCylinder.pointInPolytope (ptope,
+ midpt.x, midpt.y, midpt.z)) {
+ continue;
+ }
+ distToEdge =
+ Distance.segmentToSegment (origin, end,
+ ptope.verts[i], ptope.verts[j]);
+ if (distToEdge <= radius*radius) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ /*
+ else {
+ throw new RuntimeException("intersect method not implemented");
+ }
+ */
+ return false;
+ }
+
+ // Only use within J3D.
+ // Return a new PickCylinderSegment that is the transformed (t3d) of this
+ // pickCylinderSegment.
+ PickShape transform(Transform3D t3d) {
+
+ PickCylinderSegment newPCS = new PickCylinderSegment();
+
+ newPCS.origin.x = origin.x;
+ newPCS.origin.y = origin.y;
+ newPCS.origin.z = origin.z;
+ newPCS.radius = radius * t3d.getScale();
+ newPCS.end.x = end.x;
+ newPCS.end.y = end.y;
+ newPCS.end.z = end.z;
+
+ t3d.transform(newPCS.origin);
+ t3d.transform(newPCS.end);
+
+ newPCS.direction.x = newPCS.end.x - newPCS.origin.x;
+ newPCS.direction.y = newPCS.end.y - newPCS.origin.y;
+ newPCS.direction.z = newPCS.end.z - newPCS.origin.z;
+ newPCS.direction.normalize();
+
+ return newPCS;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/PickPoint.java b/src/classes/share/javax/media/j3d/PickPoint.java
new file mode 100644
index 0000000..074233b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickPoint.java
@@ -0,0 +1,94 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * PickPoint is a pick shape defined as a single point. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ */
+public final class PickPoint extends PickShape {
+
+ Point3d location;
+
+ /**
+ * Constructs a PickPoint using a default point.
+ * The point is initialized to (0,0,0).
+ */
+ public PickPoint() {
+ location = new Point3d();
+ }
+
+ /**
+ * Constructs a PickPoint from the specified parameter.
+ * @param location the pick point.
+ */
+ public PickPoint(Point3d location) {
+ this.location = new Point3d(location);
+ }
+
+ /**
+ * Sets the position of this PickPoint to the specified value.
+ * @param location the new pick point.
+ */
+ public void set(Point3d location) {
+ this.location.x = location.x;
+ this.location.y = location.y;
+ this.location.z = location.z;
+ }
+
+ /**
+ * Gets the position of this PickPoint.
+ * @return the current pick point.
+ */
+ public void get(Point3d location) {
+ location.x = this.location.x;
+ location.y = this.location.y;
+ location.z = this.location.z;
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+ return bounds.intersect(location, pickPos);
+ }
+
+ // Only use within J3D.
+ // Return a new PickPoint that is the transformed (t3d) of this pickPoint.
+ PickShape transform(Transform3D t3d) {
+
+ PickPoint newPPt = new PickPoint();
+
+ newPPt.location.x = location.x;
+ newPPt.location.y = location.y;
+ newPPt.location.z = location.z;
+
+ t3d.transform(newPPt.location);
+
+ return newPPt;
+ }
+
+ Point3d getStartPoint() {
+ return location;
+ }
+
+ int getPickType() {
+ return PICKPOINT;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickRay.java b/src/classes/share/javax/media/j3d/PickRay.java
new file mode 100644
index 0000000..13d2eef
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickRay.java
@@ -0,0 +1,119 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * PickRay is an infinite ray pick shape. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ */
+public final class PickRay extends PickShape {
+
+ Point3d origin;
+ Vector3d direction;
+
+ /**
+ * Constructs an empty PickRay. The origin and direction of the
+ * ray are initialized to (0,0,0).
+ */
+ public PickRay() {
+ origin = new Point3d();
+ direction = new Vector3d();
+ }
+
+ /**
+ * Constructs an infinite ray pick shape from the specified
+ * parameters.
+ * @param origin the origin of the ray.
+ * @param direction the direction of the ray.
+ */
+ public PickRay(Point3d origin, Vector3d direction) {
+ this.origin = new Point3d(origin);
+ this.direction = new Vector3d(direction);
+ }
+
+
+ /**
+ * Sets the parameters of this PickRay to the specified values.
+ * @param origin the origin of the ray.
+ * @param direction the direction of the ray.
+ */
+ public void set(Point3d origin, Vector3d direction) {
+ this.origin.x = origin.x;
+ this.origin.y = origin.y;
+ this.origin.z = origin.z;
+ this.direction.x = direction.x;
+ this.direction.y = direction.y;
+ this.direction.z = direction.z;
+ }
+
+ /**
+ * Retrieves the parameters from this PickRay.
+ * @param origin the Point3d object into which the origin will be copied.
+ * @param direction the Vector3d object into which the direction
+ * will be copied.
+ */
+ public void get(Point3d origin, Vector3d direction) {
+ origin.x = this.origin.x;
+ origin.y = this.origin.y;
+ origin.z = this.origin.z;
+ direction.x = this.direction.x;
+ direction.y = this.direction.y;
+ direction.z = this.direction.z;
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+ return bounds.intersect(origin, direction, pickPos);
+ }
+
+
+ // Only use within J3D.
+ // Return a new PickRay that is the transformed (t3d) of this pickRay.
+ PickShape transform(Transform3D t3d) {
+
+ Point3d end = new Point3d();
+
+ PickRay newPR = new PickRay(origin, direction);
+
+ end.x = origin.x + direction.x;
+ end.y = origin.y + direction.y;
+ end.z = origin.z + direction.z;
+
+ t3d.transform(newPR.origin);
+ t3d.transform(end);
+
+ newPR.direction.x = end.x - newPR.origin.x;
+ newPR.direction.y = end.y - newPR.origin.y;
+ newPR.direction.z = end.z - newPR.origin.z;
+ newPR.direction.normalize();
+
+ return newPR;
+ }
+
+
+ Point3d getStartPoint() {
+ return origin;
+ }
+
+ int getPickType() {
+ return PICKRAY;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickSegment.java b/src/classes/share/javax/media/j3d/PickSegment.java
new file mode 100644
index 0000000..f3d50f2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickSegment.java
@@ -0,0 +1,106 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * PickSegment is a line segment pick shape. It can
+ * be used as an argument to the picking methods in BranchGroup and Locale.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ */
+public final class PickSegment extends PickShape {
+
+ Point3d start;
+ Point3d end;
+
+ /**
+ * Constructs an empty PickSegment.
+ * The start and end points of the line segment are
+ * initialized to (0,0,0).
+ */
+ public PickSegment() {
+ this.start = new Point3d();
+ this.end = new Point3d();
+ }
+
+ /**
+ * Constructs a line segment pick shape from the specified
+ * parameters.
+ * @param start the start point of the line segment.
+ * @param end the end point of the line segment.
+ */
+ public PickSegment(Point3d start, Point3d end) {
+ this.start = new Point3d(start);
+ this.end = new Point3d(end);
+ }
+
+ /**
+ * Sets the parameters of this PickSegment to the specified values.
+ * @param start the start point of the line segment.
+ * @param end the end point of the line segment.
+ */
+ public void set(Point3d start, Point3d end) {
+ this.start.x = start.x;
+ this.start.y = start.y;
+ this.start.z = start.z;
+ this.end.x = end.x;
+ this.end.y = end.y;
+ this.end.z = end.z;
+ }
+
+ /**
+ * Gets the parameters from this PickSegment.
+ * @param start the Point3d object into which the start
+ * point will be copied.
+ * @param end the Point3d object into which the end point
+ * will be copied.
+ */
+ public void get(Point3d start, Point3d end) {
+ start.x = this.start.x;
+ start.y = this.start.y;
+ start.z = this.start.z;
+ end.x = this.end.x;
+ end.y = this.end.y;
+ end.z = this.end.z;
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ */
+ final boolean intersect(Bounds bounds, Point4d pickPos) {
+ return bounds.intersect(start, end, pickPos);
+ }
+
+
+
+ // Only use within J3D.
+ // Return a new PickSegment that is the transformed (t3d) of this pickSegment.
+ PickShape transform(Transform3D t3d) {
+ PickSegment newPS = new PickSegment(start, end);
+ t3d.transform(newPS.start);
+ t3d.transform(newPS.end);
+ return newPS;
+ }
+
+ Point3d getStartPoint() {
+ return start;
+ }
+
+ int getPickType() {
+ return PICKSEGMENT;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PickShape.java b/src/classes/share/javax/media/j3d/PickShape.java
new file mode 100644
index 0000000..afad4cf
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickShape.java
@@ -0,0 +1,71 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.Point4d;
+import javax.vecmath.Point3d;
+
+/**
+ * An abstract class for describing a pick shape that can be used with
+ * the BranchGroup and Locale picking methods.
+ *
+ * @see BranchGroup#pickAll
+ * @see Locale#pickAll
+ */
+public abstract class PickShape {
+
+ // Use for picking
+ static final int PICKRAY = 1;
+ static final int PICKSEGMENT = 2;
+ static final int PICKPOINT = 3;
+ static final int PICKCYLINDER = 4;
+ static final int PICKCONE = 5;
+ static final int PICKBOUNDINGBOX = 6;
+ static final int PICKBOUNDINGSPHERE = 7;
+ static final int PICKBOUNDINGPOLYTOPE = 8;
+ static final int PICKUNKNOWN = 9;
+
+ /**
+ * Constructs a PickShape object.
+ */
+ public PickShape() {
+ }
+
+ /**
+ * Return true if shape intersect with bounds.
+ * The point of intersection is stored in pickPos.
+ */
+ abstract boolean intersect(Bounds bounds, Point4d pickPos);
+
+ // Only use within J3D.
+ // Return a new PickShape that is the transformed (t3d) of this pickShape.
+ abstract PickShape transform(Transform3D t3d);
+
+ // Get the start point use to compute the distance
+ // with intersect point for this shape.
+ abstract Point3d getStartPoint();
+
+ // Return the distance between the original of this
+ // pickShape and iPnt
+ double distance(Point3d iPnt) {
+ Point3d p = getStartPoint();
+ double x = iPnt.x - p.x;
+ double y = iPnt.y - p.y;
+ double z = iPnt.z - p.z;
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+
+ // Return one of PickShape type constant define above
+ abstract int getPickType();
+
+}
+
diff --git a/src/classes/share/javax/media/j3d/Picking.java b/src/classes/share/javax/media/j3d/Picking.java
new file mode 100644
index 0000000..11df565
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Picking.java
@@ -0,0 +1,660 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+
+/**
+ * Internal class that implements picking functionality.
+ */
+
+class Picking {
+
+ static SceneGraphPath[] pickAll(Locale locale, PickShape shape) {
+ if(locale == null) {
+ return null;
+ }
+
+ GeometryAtom geomAtoms[] =
+ locale.universe.geometryStructure.pickAll(locale, shape);
+ if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+ // although getSceneGraphPath() also return null, we
+ // save time for synchronization
+ return null;
+ }
+ synchronized (locale.universe.sceneGraphLock) {
+ return getSceneGraphPath(null, null, geomAtoms, locale);
+ }
+ }
+
+
+
+ static SceneGraphPath[] pickAll(BranchGroup node,
+ PickShape shape) {
+ if (node == null) {
+ return null;
+ }
+
+ BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
+
+ if (nodeR.inSharedGroup) {
+ throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
+ }
+
+ Locale locale = nodeR.locale;
+
+ GeometryAtom geomAtoms[] =
+ locale.universe.geometryStructure.pickAll(locale, shape);
+
+ if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+ return null;
+ }
+
+ synchronized (nodeR.universe.sceneGraphLock) {
+ return getSceneGraphPath(initSceneGraphPath(nodeR),
+ nodeR, geomAtoms, locale);
+ }
+ }
+
+
+ static SceneGraphPath[] pickAllSorted(Locale locale,
+ PickShape shape) {
+ if(locale == null) {
+ return null;
+ }
+
+ GeometryAtom geomAtoms[] =
+ locale.universe.geometryStructure.pickAll(locale, shape);
+
+ if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+ return null;
+ }
+
+ sortGeomAtoms(geomAtoms, shape);
+
+ synchronized (locale.universe.sceneGraphLock) {
+ return getSceneGraphPath(null, null, geomAtoms, locale);
+ }
+ }
+
+
+ static SceneGraphPath[] pickAllSorted(BranchGroup node,
+ PickShape shape) {
+ if (node == null) {
+ return null;
+ }
+
+ BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
+
+ if (nodeR.inSharedGroup) {
+ throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
+ }
+
+ Locale locale = nodeR.locale;
+
+ GeometryAtom geomAtoms[] =
+ locale.universe.geometryStructure.pickAll(locale, shape);
+
+ if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+ return null;
+ }
+
+ // we have to sort first before eliminate duplicate Text3D
+ // since we want the closest geometry atoms of Text3D
+ sortGeomAtoms(geomAtoms, shape);
+
+ synchronized (nodeR.universe.sceneGraphLock) {
+ return getSceneGraphPath(initSceneGraphPath(nodeR),
+ nodeR, geomAtoms, locale);
+ }
+ }
+
+
+ static SceneGraphPath pickClosest(Locale locale,
+ PickShape shape) {
+
+ if(locale == null) {
+ return null;
+ }
+
+ GeometryAtom geomAtoms[] =
+ locale.universe.geometryStructure.pickAll(locale, shape);
+
+
+ if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+ return null;
+ }
+
+
+ GeometryAtom geomAtom = selectClosest(geomAtoms, shape);
+
+
+ synchronized (locale.universe.sceneGraphLock) {
+ return getSceneGraphPath(null, null, geomAtom, locale);
+ }
+ }
+
+
+ static SceneGraphPath pickClosest(BranchGroup node,
+ PickShape shape) {
+ if (node == null) {
+ return null;
+ }
+
+ BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
+
+ if (nodeR.inSharedGroup) {
+ throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
+ }
+
+ Locale locale = nodeR.locale;
+
+ GeometryAtom geomAtoms[] =
+ locale.universe.geometryStructure.pickAll(locale, shape);
+
+
+
+ if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+ return null;
+ }
+
+
+ // We must sort all since the closest one in geomAtoms may not
+ // under the BranchGroup node
+ sortGeomAtoms(geomAtoms, shape);
+
+ synchronized (nodeR.universe.sceneGraphLock) {
+ return getFirstSceneGraphPath(initSceneGraphPath(nodeR),
+ nodeR, geomAtoms, locale);
+
+ }
+ }
+
+
+ static SceneGraphPath pickAny(Locale locale, PickShape shape) {
+
+ if(locale == null) {
+ return null;
+ }
+
+ GeometryAtom geomAtom =
+ locale.universe.geometryStructure.pickAny(locale, shape);
+
+ if (geomAtom == null) {
+ return null;
+ }
+
+
+ synchronized (locale.universe.sceneGraphLock) {
+ return getSceneGraphPath(null, null, geomAtom, locale);
+ }
+ }
+
+ static SceneGraphPath pickAny(BranchGroup node, PickShape shape) {
+
+ if (node == null) {
+ return null;
+ }
+
+ BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
+
+ if (nodeR.inSharedGroup) {
+ throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
+ }
+
+ Locale locale = nodeR.locale;
+
+ // since PickAny return from geometry may not lie under
+ // BranchGroup node, we have to use pickAll
+
+ GeometryAtom geomAtoms[] =
+ locale.universe.geometryStructure.pickAll(locale, shape);
+
+ if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+ return null;
+ }
+
+ synchronized (nodeR.universe.sceneGraphLock) {
+ return getFirstSceneGraphPath(initSceneGraphPath(nodeR),
+ nodeR, geomAtoms, locale);
+ }
+ }
+
+
+ /**
+ * Search the path from nodeR up to Locale.
+ * Return the search path as ArrayList if found.
+ * Note that the locale will not insert into path.
+ */
+ static private ArrayList initSceneGraphPath(NodeRetained nodeR) {
+ ArrayList path = new ArrayList(5);
+
+ do {
+ if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)){
+ path.add(nodeR);
+ }
+ nodeR = nodeR.parent;
+ } while (nodeR != null); // reach Locale
+
+ return path;
+ }
+
+ /**
+ * return all SceneGraphPath[] of the geomAtoms.
+ * If initpath is null, the path is search from
+ * geomAtom Shape3D/Morph Node up to Locale
+ * (assume the same locale).
+ * Otherwise, the path is search up to node or
+ * null is return if it is not hit.
+ */
+ static private SceneGraphPath[] getSceneGraphPath(ArrayList initpath,
+ BranchGroupRetained node,
+ GeometryAtom geomAtoms[],
+ Locale locale) {
+
+ ArrayList paths = new ArrayList(5);
+ GeometryAtom geomAtom;
+ NodeRetained target;
+ ArrayList texts = null;
+
+ if (geomAtoms == null) {
+ return null;
+ }
+
+
+ for (int i=0; i < geomAtoms.length; i++) {
+ geomAtom = (GeometryAtom) geomAtoms[i];
+ Shape3DRetained shape = geomAtom.source;
+
+ // isPickable and currentSwitchOn has been check in BHTree
+
+ if (!inside(shape.branchGroupPath, node)) {
+ continue;
+ }
+
+ target = shape.sourceNode;
+
+ if (target == null) {
+ // The node is just detach from branch so sourceNode = null
+ continue;
+ }
+
+ // Special case, for Text3DRetained, it is possible
+ // for different geomAtoms pointing to the same
+ // source Text3DRetained. So we need to combine
+ // those cases and report only once.
+ if (target instanceof Shape3DRetained) {
+ Shape3DRetained s3dR = (Shape3DRetained) target;
+ GeometryRetained geomR = null;
+ for(int cnt=0; cnt<s3dR.geometryList.size(); cnt++) {
+ geomR = (GeometryRetained) s3dR.geometryList.get(cnt);
+ if(geomR != null)
+ break;
+ }
+
+ if (geomR == null)
+ continue;
+
+ if (geomR instanceof Text3DRetained) {
+ // assume this case is not frequent, we allocate
+ // ArrayList only when necessary and we use ArrayList
+ // instead of HashMap since the case of when large
+ // number of distingish Text3DRetained node hit is
+ // rare.
+ if (texts == null) {
+ texts = new ArrayList(3);
+ } else {
+ int size = texts.size();
+ boolean found = false;
+ for (int j=0; j < size; j++) {
+ if (texts.get(j) == target) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ continue; // try next geomAtom
+ }
+ }
+ texts.add(target);
+ }
+ }
+
+ ArrayList path = retrievePath(target, node,
+ geomAtom.source.key);
+
+ if (path == null) {
+ continue;
+ }
+
+ // If target is instance of compile retained, then loop thru
+ // the entire source list and add it to the scene graph path
+ if (target instanceof Shape3DCompileRetained) {
+ Shape3DCompileRetained s3dCR = (Shape3DCompileRetained)target;
+ Node[] mpath = mergePath(path, initpath);
+ for (int n = 0; n < s3dCR.srcList.length; n++) {
+ SceneGraphPath sgpath = new SceneGraphPath(locale,
+ mpath,
+ (Node) s3dCR.srcList[n]);
+ sgpath.setTransform(shape.getCurrentLocalToVworld(0));
+ paths.add(sgpath);
+ }
+
+ }
+ else {
+ SceneGraphPath sgpath = new SceneGraphPath(locale,
+ mergePath(path, initpath),
+ (Node) target.source);
+ sgpath.setTransform(shape.getCurrentLocalToVworld(0));
+ paths.add(sgpath);
+ }
+
+
+ }
+ SceneGraphPath pathArray[] = new SceneGraphPath[paths.size()];
+ return (SceneGraphPath []) paths.toArray(pathArray);
+ }
+
+ /**
+ * return the SceneGraphPath of the geomAtom.
+ * If initpath is null, the path is search from
+ * geomAtom Shape3D/Morph Node up to Locale
+ * (assume the same locale).
+ * Otherwise, the path is search up to node or
+ * null is return if it is not hit.
+ */
+ static private SceneGraphPath getSceneGraphPath(ArrayList initpath,
+ BranchGroupRetained node,
+ GeometryAtom geomAtom,
+ Locale locale) {
+ if (geomAtom == null) {
+ return null;
+ }
+
+ Shape3DRetained shape = geomAtom.source;
+ NodeRetained target = shape.sourceNode;
+
+ if (target == null) {
+ // The node is just detach from branch so sourceNode = null
+ return null;
+ }
+
+ if (!inside(shape.branchGroupPath, node)) {
+ return null;
+ }
+
+ ArrayList path = retrievePath(target, node, shape.key);
+
+ if (path == null) {
+ return null;
+ }
+
+ SceneGraphPath sgpath = new SceneGraphPath(locale,
+ mergePath(path, initpath),
+ (Node)
+ target.source);
+ sgpath.setTransform(shape.getCurrentLocalToVworld(0));
+ return sgpath;
+ }
+
+ /**
+ * Return true if bg is inside cachedBG or bg is null
+ */
+ static private boolean inside(BranchGroupRetained bgArr[],
+ BranchGroupRetained bg) {
+
+ if ((bg == null) || (bgArr == null)) {
+ return true;
+ }
+
+ for (int i=0; i < bgArr.length; i++) {
+ if (bgArr[i] == bg) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * return the first SceneGraphPath of the geomAtom.
+ * If initpath is null, the path is search from
+ * geomAtom Shape3D/Morph Node up to Locale
+ * (assume the same locale).
+ * Otherwise, the path is search up to node or
+ * null is return if it is not hit.
+ */
+ static private SceneGraphPath getFirstSceneGraphPath(ArrayList initpath,
+ BranchGroupRetained node,
+ GeometryAtom geomAtoms[],
+ Locale locale) {
+ if (geomAtoms == null) {
+ return null;
+ }
+
+ for (int i=0; i < geomAtoms.length; i++) {
+ Shape3DRetained shape = geomAtoms[i].source;
+ NodeRetained target = shape.sourceNode;
+
+ if (target == null) {
+ // The node is just detach from branch so sourceNode = null
+ continue;
+ }
+ if (!inside(shape.branchGroupPath, node)) {
+ continue;
+ }
+ ArrayList path = retrievePath(target, node, geomAtoms[i].source.key);
+
+ if (path == null) {
+ continue;
+ }
+ SceneGraphPath sgpath = new SceneGraphPath(locale,
+ mergePath(path, initpath),
+ (Node) target.source);
+ sgpath.setTransform(shape.getCurrentLocalToVworld(0));
+ return sgpath;
+ }
+ return null;
+ }
+
+
+ /**
+ * search the full path from the botton of the scene graph -
+ * startNode, up to the Locale if endNode is null.
+ * If endNode is not null, the path is found up to, but not
+ * including, endNode or return null if endNode not hit
+ * during the search.
+ */
+ static private ArrayList retrievePath(NodeRetained startNode,
+ NodeRetained endNode,
+ HashKey key) {
+
+ ArrayList path = new ArrayList(5);
+ NodeRetained nodeR = startNode;
+
+ if (nodeR.inSharedGroup) {
+ // getlastNodeId() will destroy this key
+ key = new HashKey(key);
+ }
+
+ do {
+ if (nodeR == endNode) { // we found it !
+ return path;
+ }
+
+ if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)) {
+ path.add(nodeR);
+ }
+
+ if (nodeR instanceof SharedGroupRetained) {
+ // retrieve the last node ID
+ String nodeId = key.getLastNodeId();
+ Vector parents = ((SharedGroupRetained) nodeR).parents;
+ int sz = parents.size();
+ NodeRetained prevNodeR = nodeR;
+ for(int i=0; i< sz; i++) {
+ NodeRetained linkR = (NodeRetained) parents.elementAt(i);
+ if (linkR.nodeId.equals(nodeId)) {
+ nodeR = linkR;
+ // Need to add Link to the path report
+ path.add(nodeR);
+ // since !(endNode instanceof Link), we
+ // can skip the check (nodeR == endNode) and
+ // proceed to parent of link below
+ break;
+ }
+ }
+ if (nodeR == prevNodeR) {
+ // branch is already detach
+ return null;
+ }
+ }
+ nodeR = nodeR.parent;
+ } while (nodeR != null); // reach Locale
+
+ if (endNode == null) {
+ // user call pickxxx(Locale locale, PickShape shape)
+ return path;
+ }
+
+ // user call pickxxx(BranchGroup endNode, PickShape shape)
+ // if locale is reached and endNode not hit, this is not
+ // the path user want to select
+ return null;
+ }
+
+ /**
+ * copy p1, (follow by) p2 into a new array, p2 can be null
+ * The path is then reverse before return.
+ */
+ static private Node[] mergePath(ArrayList p1, ArrayList p2) {
+ int s = p1.size();
+ int len;
+ int i;
+ int l;
+ if (p2 == null) {
+ len = s;
+ } else {
+ len = s + p2.size();
+ }
+
+ Node nodes[] = new Node[len];
+ l = len-1;
+ for (i=0; i < s; i++) {
+ nodes[l-i] = (Node) ((NodeRetained) p1.get(i)).source;
+ }
+ for (int j=0; i< len; i++, j++) {
+ nodes[l-i] = (Node) ((NodeRetained) p2.get(j)).source;
+ }
+ return nodes;
+ }
+
+ /**
+ * Select the closest geomAtoms from shape
+ * geomAtoms.length must be >= 1
+ */
+ static private GeometryAtom selectClosest(GeometryAtom geomAtoms[],
+ PickShape shape) {
+ Point4d pickPos = new Point4d();
+ GeometryAtom closestAtom = geomAtoms[0];
+ shape.intersect(closestAtom.source.vwcBounds, pickPos);
+ double distance = pickPos.w;
+
+ for (int i=1; i < geomAtoms.length; i++) {
+ shape.intersect(geomAtoms[i].source.vwcBounds, pickPos);
+ if (pickPos.w < distance) {
+ distance = pickPos.w;
+ closestAtom = geomAtoms[i];
+ }
+ }
+ return closestAtom;
+ }
+
+ /**
+ * Sort the GeometryAtoms distance from shape in ascending order
+ * geomAtoms.length must be >= 1
+ */
+ static private void sortGeomAtoms(GeometryAtom geomAtoms[],
+ PickShape shape) {
+
+ final double distance[] = new double[geomAtoms.length];
+ Point4d pickPos = new Point4d();
+
+ for (int i=0; i < geomAtoms.length; i++) {
+ shape.intersect(geomAtoms[i].source.vwcBounds, pickPos);
+ distance[i] = pickPos.w;
+ }
+
+ class Sort {
+
+ GeometryAtom atoms[];
+
+ Sort(GeometryAtom[] atoms) {
+ this.atoms = atoms;
+ }
+
+ void sorting() {
+ if (atoms.length < 7) {
+ insertSort();
+ } else {
+ quicksort(0, atoms.length-1);
+ }
+ }
+
+ // Insertion sort on smallest arrays
+ final void insertSort() {
+ for (int i=0; i<atoms.length; i++) {
+ for (int j=i; j>0 &&
+ (distance[j-1] > distance[j]); j--) {
+ double t = distance[j];
+ distance[j] = distance[j-1];
+ distance[j-1] = t;
+ GeometryAtom p = atoms[j];
+ atoms[j] = atoms[j-1];
+ atoms[j-1] = p;
+ }
+ }
+ }
+
+ final void quicksort( int l, int r ) {
+ int i = l;
+ int j = r;
+ double k = distance[(l+r) / 2];
+
+ do {
+ while (distance[i]<k) i++;
+ while (k<distance[j]) j--;
+ if (i<=j) {
+ double tmp = distance[i];
+ distance[i] =distance[j];
+ distance[j] = tmp;
+
+ GeometryAtom p=atoms[i];
+ atoms[i]=atoms[j];
+ atoms[j]=p;
+ i++;
+ j--;
+ }
+ } while (i<=j);
+
+ if (l<j) quicksort(l,j);
+ if (l<r) quicksort(i,r);
+ }
+ }
+
+ (new Sort(geomAtoms)).sorting();
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/PointArray.java b/src/classes/share/javax/media/j3d/PointArray.java
new file mode 100644
index 0000000..84366ac
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointArray.java
@@ -0,0 +1,150 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The PointArray object draws the array of vertices as individual points.
+ */
+
+public class PointArray extends GeometryArray {
+
+ // non-public, no parameter constructor
+ PointArray() {}
+
+ /**
+ * Constructs an empty PointArray object with the specified
+ * number of vertices, and vertex format.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @exception IllegalArgumentException if vertexCount is less than 1
+ */
+ public PointArray(int vertexCount, int vertexFormat) {
+ super(vertexCount,vertexFormat);
+
+ if (vertexCount < 1 )
+ throw new IllegalArgumentException(J3dI18N.getString("PointArray0"));
+ }
+
+ /**
+ * Constructs an empty PointArray object with the specified
+ * number of vertices, and vertex format, number of texture coordinate
+ * sets, and texture coordinate mapping array.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 1
+ *
+ * @since Java 3D 1.2
+ */
+ public PointArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap);
+
+ if (vertexCount < 1 )
+ throw new IllegalArgumentException(J3dI18N.getString("PointArray0"));
+ }
+
+ /**
+ * Creates the retained mode PointArrayRetained object that this
+ * PointArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new PointArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ PointArrayRetained rt = (PointArrayRetained) retained;
+ int texSetCount = rt.getTexCoordSetCount();
+ PointArray p;
+ if (texSetCount == 0) {
+ p = new PointArray(rt.getVertexCount(),
+ rt.getVertexFormat());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ p = new PointArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap);
+ }
+ p.duplicateNodeComponent(this);
+ return p;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PointArrayRetained.java b/src/classes/share/javax/media/j3d/PointArrayRetained.java
new file mode 100644
index 0000000..dda27ac
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointArrayRetained.java
@@ -0,0 +1,247 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The PointArray object draws the array of vertices as individual points.
+ */
+
+class PointArrayRetained extends GeometryArrayRetained {
+
+ PointArrayRetained() {
+ this.geoType = GEO_TYPE_POINT_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ Point3d pnt = new Point3d();
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt);
+ if (intersectPntAndRay(pnt, pickRay.origin,
+ pickRay.direction, sdist)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ Vector3d dir =
+ new Vector3d(pickSegment.end.x - pickSegment.start.x,
+ pickSegment.end.y - pickSegment.start.y,
+ pickSegment.end.z - pickSegment.start.z);
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt);
+ if (intersectPntAndRay(pnt, pickSegment.start,
+ dir, sdist) &&
+ (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ case PickShape.PICKBOUNDINGSPHERE:
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ Bounds bounds = ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt);
+ if (bounds.intersect(pnt)) {
+ if (dist == null) {
+ return true;
+ }
+ sdist[0] = pickShape.distance(pnt);
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt);
+
+ if (intersectCylinder(pnt, pickCylinder, sdist)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt);
+
+ if (intersectCone(pnt, pickCone, sdist)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = pnt.x;
+ y = pnt.y;
+ z = pnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("PointArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+
+ }
+
+ boolean intersect(Point3d[] pnts) {
+ Point3d point = new Point3d();
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < validVertexCount) {
+ getVertexData(i++, point);
+ if (intersectTriPnt(pnts[0], pnts[1], pnts[2], point)) {
+ return true;
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i < validVertexCount) {
+ getVertexData(i++, point);
+ if (intersectTriPnt(pnts[0], pnts[1], pnts[2], point) ||
+ intersectTriPnt(pnts[0], pnts[2], pnts[3], point)) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ double dist[] = new double[1];
+ Vector3d dir = new Vector3d();
+
+ while (i < validVertexCount) {
+ getVertexData(i++, point);
+ dir.x = pnts[1].x - pnts[0].x;
+ dir.y = pnts[1].y - pnts[0].y;
+ dir.z = pnts[1].z - pnts[0].z;
+ if (intersectPntAndRay(point, pnts[0], dir, dist) &&
+ (dist[0] <= 1.0)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < validVertexCount) {
+ getVertexData(i++, point);
+ if ((pnts[0].x == point.x) &&
+ (pnts[0].y == point.y) &&
+ (pnts[0].z == point.z)) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+
+ Point3d[] pnt = new Point3d[1];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnt[0] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt[0]);
+ thisToOtherVworld.transform(pnt[0]);
+ if (geom.intersect(pnt)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ Point3d pnt = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt);
+ if (targetBound.intersect(pnt)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int getClassType() {
+ return POINT_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PointAttributes.java b/src/classes/share/javax/media/j3d/PointAttributes.java
new file mode 100644
index 0000000..d42433e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointAttributes.java
@@ -0,0 +1,209 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The PointAttributes object defines all attributes that apply to
+ * point primitives. The point attributes that can be defined are:<p>
+ * <ul>
+ * <li>Size - the size of the point, in pixels. The default is a point
+ * size of one pixel.</li><p>
+ * <li>Antialiasing - for points greater than one-pixel in size,
+ * antialiasing smooths the outline of the point when it is rendered.</li>
+ * <p></ul>
+ * If antialiasing is disabled (the default), fractional point sizes
+ * are rounded to integer sizes, and a screen-aligned square region
+ * of pixels is drawn.<p>
+ * <p>
+ * If antialiasing is enabled, the points are considered transparent
+ * for rendering purposes. They are rendered with all the other transparent
+ * objects and adhere to the other transparency settings such as the
+ * View transparency sorting policy and the View depth buffer freeze
+ * transparent enable.
+ * </p>
+ *
+ * @see Appearance
+ * @see View
+ */
+public class PointAttributes extends NodeComponent {
+
+ /**
+ * Specifies that this PointAttributes object allows reading its
+ * point size information.
+ */
+ public static final int
+ ALLOW_SIZE_READ = CapabilityBits.POINT_ATTRIBUTES_ALLOW_SIZE_READ;
+
+ /**
+ * Specifies that this PointAttributes object allows writing its
+ * point size information.
+ */
+ public static final int
+ ALLOW_SIZE_WRITE = CapabilityBits.POINT_ATTRIBUTES_ALLOW_SIZE_WRITE;
+
+ /**
+ * Specifies that this PointAttributes object allows reading its
+ * point antialiasing flag.
+ */
+ public static final int
+ ALLOW_ANTIALIASING_READ = CapabilityBits.POINT_ATTRIBUTES_ALLOW_ANTIALIASING_READ;
+
+ /**
+ * Specifies that this PointAttributes object allows writing its
+ * point antialiasing flag.
+ */
+ public static final int
+ ALLOW_ANTIALIASING_WRITE = CapabilityBits.POINT_ATTRIBUTES_ALLOW_ANTIALIASING_WRITE;
+
+ /**
+ * Constructs a PointAttributes object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * point size : 1<br>
+ * point antialiasing : false<br>
+ * </ul>
+ */
+ public PointAttributes(){
+ }
+
+ /**
+ * Constructs a PointAttributes object with specified values.
+ * @param pointSize the size of points, in pixels
+ * @param pointAntialiasing flag to set point antialising ON or OFF
+ */
+ public PointAttributes(float pointSize, boolean pointAntialiasing){
+
+ ((PointAttributesRetained)this.retained).initPointSize(pointSize);
+ ((PointAttributesRetained)this.retained).initPointAntialiasingEnable(pointAntialiasing);
+ }
+
+ /**
+ * Sets the point size for this appearance component object.
+ * @param pointSize the size, in pixels, of point primitives
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPointSize(float pointSize) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointAttributes0"));
+
+ if (isLive())
+ ((PointAttributesRetained)this.retained).setPointSize(pointSize);
+ else
+ ((PointAttributesRetained)this.retained).initPointSize(pointSize);
+
+ }
+
+ /**
+ * Gets the point size for this appearance component object.
+ * @return the size, in pixels, of point primitives
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getPointSize() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointAttributes1"));
+ return ((PointAttributesRetained)this.retained).getPointSize();
+ }
+
+ /**
+ * Enables or disables point antialiasing
+ * for this appearance component object.
+ * <p>
+ * If antialiasing is enabled, the points are considered transparent
+ * for rendering purposes. They are rendered with all the other
+ * transparent objects and adhere to the other transparency settings
+ * such as the View transparency sorting policy and the View depth
+ * buffer freeze transparent enable.
+ * </p>
+ * @param state true or false to enable or disable point antialiasing
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see View
+ */
+ public void setPointAntialiasingEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANTIALIASING_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointAttributes2"));
+ if (isLive())
+ ((PointAttributesRetained)this.retained).setPointAntialiasingEnable(state);
+ else
+ ((PointAttributesRetained)this.retained).initPointAntialiasingEnable(state);
+
+ }
+
+ /**
+ * Retrieves the state of the point antialiasing flag.
+ * @return true if point antialiasing is enabled,
+ * false if point antialiasing is disabled
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getPointAntialiasingEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ANTIALIASING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointAttributes3"));
+ return ((PointAttributesRetained)this.retained).getPointAntialiasingEnable();
+ }
+
+ /**
+ * Creates a retained mode PointAttributesRetained object that this
+ * PointAttributes component object will point to.
+ */
+ void createRetained() {
+ this.retained = new PointAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ PointAttributes pa = new PointAttributes();
+ pa.duplicateNodeComponent(this);
+ return pa;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ PointAttributesRetained attr = (PointAttributesRetained)
+ originalNodeComponent.retained;
+ PointAttributesRetained rt = (PointAttributesRetained) retained;
+
+ rt.initPointSize(attr.getPointSize());
+ rt.initPointAntialiasingEnable(attr.getPointAntialiasingEnable());
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/PointAttributesRetained.java b/src/classes/share/javax/media/j3d/PointAttributesRetained.java
new file mode 100644
index 0000000..bee0396
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointAttributesRetained.java
@@ -0,0 +1,208 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * The PointAttributesRetained object defines all rendering state that can be set
+ * as a component object of a Shape3D node.
+ */
+class PointAttributesRetained extends NodeComponentRetained {
+ // A list of pre-defined bits to indicate which component
+ // in this LineAttributesRetained object changed.
+ static final int POINT_SIZE_CHANGED = 0x01;
+ static final int POINT_AA_CHANGED = 0x02;
+
+ // Size, in pixels, of point primitives
+ float pointSize = 1.0f;
+
+ // Point antialiasing switch
+ boolean pointAntialiasing = false;
+
+
+ /**
+ * Sets the point size for this appearance component object.
+ * @param pointSize the size, in pixels, of point primitives
+ */
+ final void initPointSize(float pointSize) {
+ this.pointSize = pointSize;
+ }
+
+ /**
+ * Sets the point size for this appearance component object and sends a
+ * message notifying the interested structures of the change.
+ * @param pointSize the size, in pixels, of point primitives
+ */
+ final void setPointSize(float pointSize) {
+ initPointSize(pointSize);
+ sendMessage(POINT_SIZE_CHANGED, new Float(pointSize));
+ }
+
+ /**
+ * Gets the point size for this appearance component object.
+ * @return the size, in pixels, of point primitives
+ */
+ final float getPointSize() {
+ return pointSize;
+ }
+
+ /**
+ * Enables or disables point antialiasing
+ * for this appearance component object.
+ * @param state true or false to enable or disable point antialiasing
+ */
+ final void initPointAntialiasingEnable(boolean state) {
+ pointAntialiasing = state;
+ }
+
+ /**
+ * Enables or disables point antialiasing
+ * for this appearance component object and sends a
+ * message notifying the interested structures of the change.
+ * @param state true or false to enable or disable point antialiasing
+ */
+ final void setPointAntialiasingEnable(boolean state) {
+ initPointAntialiasingEnable(pointAntialiasing);
+ sendMessage(POINT_AA_CHANGED,
+ (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Retrieves the state of the point antialiasing flag.
+ * @return true if point antialiasing is enabled,
+ * false if point antialiasing is disabled
+ */
+ final boolean getPointAntialiasingEnable() {
+ return pointAntialiasing;
+ }
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ } else {
+ PointAttributesRetained mirrorPa
+ = new PointAttributesRetained();
+ mirrorPa.set(this);
+ mirrorPa.source = source;
+ mirror = mirrorPa;
+ }
+ } else {
+ ((PointAttributesRetained) mirror).set(this);
+ }
+ }
+
+ /**
+ * Update the native context
+ */
+ native void updateNative(long ctx, float pointSize, boolean pointAntialiasing);
+
+ /**
+ * Update the native context
+ */
+ void updateNative(long ctx) {
+ updateNative(ctx, pointSize, pointAntialiasing);
+ }
+
+
+ /**
+ * Initializes a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ ((PointAttributesRetained)mirror).set(this);
+ }
+
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ PointAttributesRetained mirrorPa = (PointAttributesRetained) mirror;
+
+ if ((component & POINT_SIZE_CHANGED) != 0) {
+ mirrorPa.pointSize = ((Float)value).floatValue();
+ }
+ else if ((component & POINT_AA_CHANGED) != 0) {
+ mirrorPa.pointAntialiasing = ((Boolean)value).booleanValue();
+ }
+ }
+
+ boolean equivalent(PointAttributesRetained pr) {
+ return ((pr != null) &&
+ (pr.pointSize == pointSize) &&
+ (pr.pointAntialiasing == pointAntialiasing));
+ }
+
+
+ protected void set(PointAttributesRetained pr) {
+ super.set(pr);
+ pointSize = pr.pointSize;
+ pointAntialiasing = pr.pointAntialiasing;
+ }
+
+
+ final void sendMessage(int attrMask, Object attr) {
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.POINTATTRIBUTES_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.POINTATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ }
+ void handleFrequencyChange(int bit) {
+ if (bit == PointAttributes.ALLOW_SIZE_WRITE ||
+ bit == PointAttributes.ALLOW_ANTIALIASING_WRITE) {
+ setFrequencyChangeMask(bit, 0x1);
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/PointLight.java b/src/classes/share/javax/media/j3d/PointLight.java
new file mode 100644
index 0000000..916dbda
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointLight.java
@@ -0,0 +1,292 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Point3f;
+import javax.vecmath.Color3f;
+
+
+/**
+ * The PointLight object specifies an attenuated light source at a
+ * fixed point in space that radiates light equally in all directions
+ * away from the light source. PointLight has the same attributes as
+ * a Light node, with the addition of location and attenuation
+ * parameters.
+ * <p>
+ * A point light contributes to diffuse and specular reflections,
+ * which in turn depend on the orientation and position of a
+ * surface. A point light does not contribute to ambient reflections.
+ * <p>
+ * A PointLight is attenuated by multiplying the contribution of the
+ * light by an attenuation factor. The attenuation factor causes the
+ * the PointLight's brightness to decrease as distance from the light
+ * source increases.
+ * A PointLight's attenuation factor contains three values:
+ * <P><UL>
+ * <LI>Constant attenuation</LI>
+ * <LI>Linear attenuation</LI>
+ * <LI>Quadratic attenuation</LI></UL>
+ * <p>
+ * A PointLight is attenuated by the reciprocal of the sum of:
+ * <p>
+ * <ul>
+ * The constant attenuation factor<br>
+ * The Linear attenuation factor times the distance between the light
+ * and the vertex being illuminated<br>
+ * The quadratic attenuation factor times the square of the distance
+ * between the light and the vertex
+ * </ul>
+ * <p>
+ * By default, the constant attenuation value is 1 and the other
+ * two values are 0, resulting in no attenuation.
+ */
+
+public class PointLight extends Light {
+ /**
+ * Specifies that this PointLight node allows reading its position
+ * information.
+ */
+ public static final int
+ ALLOW_POSITION_READ = CapabilityBits.POINT_LIGHT_ALLOW_POSITION_READ;
+
+ /**
+ * Specifies that this PointLight node allows writing its position
+ * information.
+ */
+ public static final int
+ ALLOW_POSITION_WRITE = CapabilityBits.POINT_LIGHT_ALLOW_POSITION_WRITE;
+
+ /**
+ * Specifies that this PointLight node allows reading its attenuation
+ * information.
+ */
+ public static final int
+ ALLOW_ATTENUATION_READ = CapabilityBits.POINT_LIGHT_ALLOW_ATTENUATION_READ;
+
+ /**
+ * Specifies that this PointLight node allows writing its attenuation
+ * information.
+ */
+ public static final int
+ ALLOW_ATTENUATION_WRITE = CapabilityBits.POINT_LIGHT_ALLOW_ATTENUATION_WRITE;
+
+ /**
+ * Constructs a PointLight node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * position : (0,0,0)<br>
+ * attenuation : (1,0,0)<br>
+ * </ul>
+ */
+ public PointLight() {
+ }
+
+ /**
+ * Constructs and initializes a point light.
+ * @param color the color of the light source
+ * @param position the position of the light in three-space
+ * @param attenuation the attenutation (constant, linear, quadratic) of the light
+ */
+ public PointLight(Color3f color,
+ Point3f position,
+ Point3f attenuation) {
+ super(color);
+ ((PointLightRetained)this.retained).initPosition(position);
+ ((PointLightRetained)this.retained).initAttenuation(attenuation);
+ }
+
+ /**
+ * Constructs and initializes a point light.
+ * @param lightOn flag indicating whether this light is on or off
+ * @param color the color of the light source
+ * @param position the position of the light in three-space
+ * @param attenuation the attenuation (constant, linear, quadratic) of the light
+ */
+ public PointLight(boolean lightOn,
+ Color3f color,
+ Point3f position,
+ Point3f attenuation) {
+ super(lightOn, color);
+ ((PointLightRetained)this.retained).initPosition(position);
+ ((PointLightRetained)this.retained).initAttenuation(attenuation);
+ }
+
+ /**
+ * Creates the retained mode PointLightRetained object that this
+ * PointLight component object will point to.
+ */
+ void createRetained() {
+ this.retained = new PointLightRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Set light position.
+ * @param position the new position
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPosition(Point3f position) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointLight0"));
+
+ if (isLive())
+ ((PointLightRetained)this.retained).setPosition(position);
+ else
+ ((PointLightRetained)this.retained).initPosition(position);
+ }
+
+ /**
+ * Set light position.
+ * @param x the new X position
+ * @param y the new Y position
+ * @param z the new Z position
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPosition(float x, float y, float z) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointLight1"));
+
+ if (isLive())
+ ((PointLightRetained)this.retained).setPosition(x,y,z);
+ else
+ ((PointLightRetained)this.retained).initPosition(x,y,z);
+ }
+
+ /**
+ * Gets this Light's current position and places it in the parameter specified.
+ * @param position the vector that will receive this node's position
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getPosition(Point3f position) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointLight2"));
+
+ ((PointLightRetained)this.retained).getPosition(position);
+ }
+
+ /**
+ * Sets this Light's current attenuation values and places it in the parameter specified.
+ * @param attenuation the vector that will receive the attenuation values
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAttenuation(Point3f attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ATTENUATION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointLight3"));
+
+ if (isLive())
+ ((PointLightRetained)this.retained).setAttenuation(attenuation);
+ else
+ ((PointLightRetained)this.retained).initAttenuation(attenuation);
+ }
+
+ /**
+ * Sets this Light's current attenuation values and places it in the parameter specified.
+ * @param constant the light's constant attenuation
+ * @param linear the light's linear attenuation
+ * @param quadratic the light's quadratic attenuation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAttenuation(float constant, float linear, float quadratic) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ATTENUATION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointLight3"));
+
+ if (isLive())
+ ((PointLightRetained)this.retained).setAttenuation(constant, linear, quadratic);
+ else
+ ((PointLightRetained)this.retained).initAttenuation(constant, linear, quadratic);
+ }
+
+ /**
+ * Gets this Light's current attenuation values and places it in the parameter specified.
+ * @param attenuation the vector that will receive the attenuation values
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getAttenuation(Point3f attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ATTENUATION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointLight5"));
+
+ ((PointLightRetained)this.retained).getAttenuation(attenuation);
+ }
+
+
+
+
+ /**
+ * 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) {
+ PointLight p = new PointLight();
+ p.duplicateNode(this, forceDuplicate);
+ return p;
+ }
+
+
+ /**
+ * Copies all PointLight 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ PointLightRetained attr = (PointLightRetained) originalNode.retained;
+ PointLightRetained rt = (PointLightRetained) retained;
+
+ Point3f p = new Point3f();
+ attr.getPosition(p);
+ rt.initPosition(p);
+
+ attr.getAttenuation(p);
+ rt.initAttenuation(p);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PointLightRetained.java b/src/classes/share/javax/media/j3d/PointLightRetained.java
new file mode 100644
index 0000000..8b0750a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointLightRetained.java
@@ -0,0 +1,322 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * A Retained PointLight source.
+ */
+class PointLightRetained extends LightRetained {
+ static final int POSITION_CHANGED = LAST_DEFINED_BIT << 1;
+ static final int ATTENUATION_CHANGED = LAST_DEFINED_BIT << 2;
+ static final int LAST_POINTLIGHT_DEFINED_BIT = ATTENUATION_CHANGED;
+
+ /**
+ * The attenuation vector consisting of
+ * constant, linear, and quadratic coefficients.
+ */
+ Point3f attenuation = new Point3f(1.0f, 0.0f, 0.0f);
+
+ // The position at which this light source exists.
+ Point3f position = new Point3f();
+
+ // The transformed position of this light
+ Point3f xformPosition = new Point3f();
+
+ // local to vworld scale for attenuation
+ double localToVworldScale;
+
+ // scaled linearAttenuation from lc to ec
+ float linearAttenuationInEc;
+
+ // scaled quadraticAttenuation from lc to ec
+ float quadraticAttenuationInEc;
+
+ PointLightRetained() {
+ this.nodeType = NodeRetained.POINTLIGHT;
+ lightType = 3;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+ /**
+ * Initializes this light's position from the vector provided.
+ * @param position the new position
+ */
+ void initPosition(Point3f position) {
+ this.position.set(position);
+
+ if (staticTransform != null) {
+ staticTransform.transform.transform(this.position, this.position);
+ }
+ }
+
+ /**
+ * Sets this light's position from the vector provided.
+ * @param position the new position
+ */
+ void setPosition(Point3f position) {
+ initPosition(position);
+ sendMessage(POSITION_CHANGED, new Point3f(position));
+ }
+
+
+ /**
+ * Initializes this light's position from the three values provided.
+ * @param x the new x position
+ * @param y the new y position
+ * @param z the new z position
+ */
+ void initPosition(float x, float y, float z) {
+ this.position.x = x;
+ this.position.y = y;
+ this.position.z = z;
+
+ if (staticTransform != null) {
+ staticTransform.transform.transform(this.position, this.position);
+ }
+ }
+
+
+ /**
+ * Sets this light's position from the three values provided.
+ * @param x the new x position
+ * @param y the new y position
+ * @param z the new z position
+ */
+ void setPosition(float x, float y, float z) {
+ setPosition(new Point3f(x, y, z));
+ }
+
+
+ /**
+ * Retrieves this light's position and places it in the
+ * vector provided.
+ * @param position the variable to receive the position vector
+ */
+ void getPosition(Point3f position) {
+ position.set(this.position);
+
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ invTransform.transform(position, position);
+ }
+ }
+
+
+ /**
+ * Initializes the point light's attenuation constants.
+ * @param attenuation a vector consisting of constant, linear, and quadratic coefficients
+ */
+ void initAttenuation(Point3f attenuation) {
+ this.attenuation.set(attenuation);
+ }
+
+ /**
+ * Sets the point light's attenuation constants.
+ * @param attenuation a vector consisting of constant, linear, and quadratic coefficients
+ */
+ void setAttenuation(Point3f attenuation) {
+ initAttenuation(attenuation);
+ sendMessage(ATTENUATION_CHANGED, new Point3f(attenuation));
+ }
+
+ /**
+ * Sets the point light's attenuation.
+ * @param constant the point light's constant attenuation
+ * @param linear the linear attenuation of the light
+ * @param quadratic the quadratic attenuation of the light
+ */
+ void initAttenuation(float constant,
+ float linear,
+ float quadratic) {
+ this.attenuation.x = constant;
+ this.attenuation.y = linear;
+ this.attenuation.z = quadratic;
+ }
+
+ /**
+ * Sets the point light's attenuation.
+ * @param constant the point light's constant attenuation
+ * @param linear the linear attenuation of the light
+ * @param quadratic the quadratic attenuation of the light
+ */
+ void setAttenuation(float constant,
+ float linear,
+ float quadratic) {
+ setAttenuation(new Point3f(constant, linear, quadratic));
+ }
+
+ /**
+ * Retrieves the light's attenuation and places the value in the parameter
+ * specified.
+ * @param attenuation the variable that will contain the attenuation
+ */
+ void getAttenuation(Point3f attenuation) {
+ attenuation.set(this.attenuation);
+ }
+
+ /**
+ * This update function, and its native counterpart,
+ * updates a point light. This includes its color, attenuation,
+ * and its transformed position.
+ */
+ native void updateLight(long ctx, int lightSlot, float red, float green,
+ float blue, float ax, float ay, float az,
+ float px, float py, float pz);
+
+ void update(long ctx, int lightSlot, double scale) {
+ validateAttenuationInEc(scale);
+ updateLight(ctx, lightSlot, color.x, color.y, color.z,
+ attenuation.x, linearAttenuationInEc,
+ quadraticAttenuationInEc,
+ xformPosition.x, xformPosition.y,
+ xformPosition.z);
+
+ }
+
+ void setLive(SetLiveState s) {
+ super.setLive(s);
+ J3dMessage createMessage = super.initMessage(9);
+ Object[] objs = (Object[])createMessage.args[4];
+ objs[7] = new Point3f(position);
+ objs[8] = new Point3f(attenuation);
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ // This is called only from SpotLightRetained, so as
+ // to not create a message for initialization. for spotlight
+ // the initialization of the message is done by SpotLightRetained
+ void doSetLive(SetLiveState s) {
+ super.setLive(s);
+ }
+
+ J3dMessage initMessage(int num) {
+ J3dMessage createMessage = super.initMessage(num);
+ Object[] objs = (Object[])createMessage.args[4];
+ objs[7] = new Point3f(position);
+ objs[8] = new Point3f(attenuation);
+ return createMessage;
+ }
+
+
+ // Note : if you add any more fields here , you need to update
+ // updateLight() in RenderingEnvironmentStructure
+ void updateMirrorObject(Object[] objs) {
+
+ int component = ((Integer)objs[1]).intValue();
+ Transform3D mlLastLocalToVworld;
+ int i;
+ int numLgts = ((Integer)objs[2]).intValue();
+ LightRetained[] mLgts = (LightRetained[]) objs[3];
+
+
+ if ((component & POSITION_CHANGED) != 0) {
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i] instanceof PointLightRetained) {
+ PointLightRetained ml = (PointLightRetained)mLgts[i];
+ mlLastLocalToVworld = ml.getLastLocalToVworld();
+ ml.position = (Point3f) objs[4];
+ mlLastLocalToVworld.transform(ml.position,
+ ml.xformPosition);
+ ml.localToVworldScale =
+ mlLastLocalToVworld.getDistanceScale();
+ }
+ }
+ }
+ else if ((component & ATTENUATION_CHANGED) != 0) {
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i] instanceof PointLightRetained) {
+ PointLightRetained ml = (PointLightRetained)mLgts[i];
+ ml.attenuation.set((Point3f)objs[4]);
+ }
+ }
+ }
+ else if ((component & INIT_MIRROR) != 0) {
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i] instanceof PointLightRetained) {
+ PointLightRetained ml = (PointLightRetained)mirrorLights[i];
+ ml.position = (Point3f)((Object[]) objs[4])[7];
+ ml.attenuation.set((Point3f)((Object[]) objs[4])[8]);
+ mlLastLocalToVworld = ml.getLastLocalToVworld();
+ mlLastLocalToVworld.transform(ml.position,
+ ml.xformPosition);
+ ml.localToVworldScale =
+ mlLastLocalToVworld.getDistanceScale();
+ }
+ }
+ }
+
+ // call the parent's mirror object update routine
+ super.updateMirrorObject(objs);
+ }
+
+ void validateAttenuationInEc(double vworldToCoexistenceScale) {
+ double localToEcScale = localToVworldScale * vworldToCoexistenceScale;
+
+ linearAttenuationInEc = (float)(attenuation.y / localToEcScale);
+ quadraticAttenuationInEc =
+ (float)(attenuation.z / (localToEcScale * localToEcScale));
+ }
+
+
+
+ // Clones only the retained side, internal use only
+ protected Object clone() {
+ PointLightRetained pr =
+ (PointLightRetained)super.clone();
+
+ pr.attenuation = new Point3f(attenuation);
+ pr.position = new Point3f(position);
+ pr.xformPosition = new Point3f();
+ return pr;
+ }
+
+
+ // Called on the mirror object
+ void updateTransformChange() {
+ super.updateTransformChange();
+
+ Transform3D lastLocalToVworld = getLastLocalToVworld();
+
+ lastLocalToVworld.transform(position, xformPosition);
+ localToVworldScale = lastLocalToVworld.getDistanceScale();
+
+ validateAttenuationInEc(0.0861328125);
+ }
+
+ void sendMessage(int attrMask, Object attr) {
+
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.LIGHT_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorLights);
+ else
+ createMessage.args[2] = new Integer(1);
+ createMessage.args[3] = mirrorLights.clone();
+ createMessage.args[4] = attr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ xform.transform.transform(position, position);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PointSound.java b/src/classes/share/javax/media/j3d/PointSound.java
new file mode 100644
index 0000000..59a098d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointSound.java
@@ -0,0 +1,528 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Point2f;
+import javax.vecmath.Point3f;
+
+
+/**
+ * The PointSound node (a sub-class of the Sound node) defines a spatially
+ * located sound source whose waves radiate uniformly in all directions from
+ * a given location in space. It has the same attributes as a Sound object
+ * with the addition of a location and the specification of distance-based
+ * gain attenuation for listener positions between an array of distances.
+ *<P>
+ * A sound's amplitude is attenuated based on the distance between the listener
+ * and the sound source position. A piecewise linear curve (defined in terms of
+ * pairs of distance and gain scale factor) specifies the gain scale factor slope.
+ *
+ * The PointSound's location and attenuation distances are defined in the local
+ * coordinate system of the node.
+ *<P>
+ * Distance Gain Attenuation
+ * <UL>
+ * Associated with distances from the listener to the sound source via an
+ * array of (distance, gain-scale-factor) pairs. The gain scale factor
+ * applied to the sound source is the linear interpolated gain value between
+ * the distance value range that includes the current distance from
+ * the listener to the sound source. If the distance from the listener to
+ * the sound source is less than the first distance in the array, the first
+ * gain scale factor is applied to the sound source. This creates a
+ * spherical region around the listener within which all sound gain is
+ * uniformly scaled by the first gain in the array. If the distance from
+ * the listener to the sound source is greater than the last distance in
+ * the array, the last gain scale factor is applied to the sound source.
+ *<P>
+ * Distance elements in this array of Point2f is a monotonically-increasing
+ * set of floating point numbers measured from the location of the sound
+ * source. Gain scale factors elements in this list of pairs can be any
+ * positive floating point numbers. While for most applications this list
+ * of gain scale factors will usually be monotonically-decreasing, they
+ * do not have to be.
+ * If this
+ * is not set, no distance gain attenuation is performed (equivalent to
+ * using a distance gain of 1.0 for all distances).
+ *<P>
+ * getDistanceGainLength method returns the length of the distance gain
+ * attenuation arrays. Arrays passed into getDistanceGain methods should all
+ * be at least this size.
+ *<P>
+ * There are two methods for getDistanceGain, one returning an array of
+ * points, the other returning separate arrays for each attenuation
+ * component.</UL>
+ */
+
+public class PointSound extends Sound {
+ // Constants
+ //
+ // These flags, when enabled using the setCapability method, allow an
+ // application to invoke methods that respectively read and write the position
+ // and the distance gain array. These capability flags are enforced only when
+ // the node is part of a live or compiled scene graph
+
+ /**
+ * Specifies that this node allows access to its object's position
+ * information.
+ */
+ public static final int
+ ALLOW_POSITION_READ = CapabilityBits.POINT_SOUND_ALLOW_POSITION_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's position
+ * information.
+ */
+ public static final int
+ ALLOW_POSITION_WRITE = CapabilityBits.POINT_SOUND_ALLOW_POSITION_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's distance
+ * gain attenuation information.
+ */
+ public static final int
+ ALLOW_DISTANCE_GAIN_READ = CapabilityBits.POINT_SOUND_ALLOW_DISTANCE_GAIN_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's distance
+ * gain attenuation information.
+ */
+ public static final int
+ ALLOW_DISTANCE_GAIN_WRITE = CapabilityBits.POINT_SOUND_ALLOW_DISTANCE_GAIN_WRITE;
+
+ /**
+ * Constructs and initializes a new PointSound node using default
+ * parameters. The following default values are used:
+ * <ul>
+ * position vector: (0.0, 0.0, 0.0)<br>
+ * Back attenuation: null<br>
+ * distance gain attenuation: null (no attenuation performed)<br>
+ * </ul>
+ */
+ public PointSound() {
+ // Uses default values defined for Sound and PointSound nodes
+ super();
+ }
+
+ /**
+ * Constructs a PointSound node object using only the provided parameter
+ * values for sound data, sample gain, and position. The remaining fields
+ * are set to the above default values. This form uses a point as input for
+ * its position.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source
+ * @param position 3D location of source
+ */
+ public PointSound(MediaContainer soundData,
+ float initialGain,
+ Point3f position) {
+ super(soundData, initialGain);
+ ((PointSoundRetained)this.retained).setPosition(position);
+ }
+
+ /**
+ * Constructs a PointSound node object using only the provided parameter
+ * values for sound data, sample gain, and position. The remaining fields
+ * are set to to the above default values. This form uses individual float
+ * parameters for the elements of the position point.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source data
+ * @param posX x coordinate of location of source
+ * @param posY y coordinate of location of source
+ * @param posZ z coordinate of location of source
+ */
+ public PointSound(MediaContainer soundData,
+ float initialGain,
+ float posX, float posY, float posZ ) {
+ super(soundData, initialGain);
+ ((PointSoundRetained)this.retained).setPosition(posX,posY,posZ);
+ }
+
+ // The next four constructors fill all this classes fields with the provided
+ // arguments values.
+ // See the header for the setDistanceGain method for details on how the
+ // those arrays are interpreted.
+
+ /**
+ * Construct a PointSound object accepting Point3f as input for the position
+ * and accepting an array of Point2f for the distance attenuation values
+ * where each pair in the array contains a distance and a gain scale factor.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source
+ * @param loopCount number of times loop is looped
+ * @param release flag denoting playing sound data to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param position 3D location of source
+ * @param distanceGain array of (distance,gain) pairs controling attenuation
+ */
+ public PointSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ Point3f position,
+ Point2f[] distanceGain) {
+
+ super(soundData, initialGain, loopCount, release, continuous,
+ enable, region, priority );
+ ((PointSoundRetained)this.retained).setPosition(position);
+ ((PointSoundRetained)this.retained).setDistanceGain(distanceGain);
+ }
+
+ /**
+ * Construct a PointSound object accepting individual float parameters for
+ * the elements of the position point, and accepting an array of Point2f for
+ * the distance attenuation values where each pair in the array contains a
+ * distance and a gain scale factor.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source
+ * @param loopCount number of times loop is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param posX x coordinate of location of source
+ * @param posY y coordinate of location of source
+ * @param posZ z coordinate of location of source
+ * @param distanceGain array of (distance,gain) pairs controling attenuation
+ */
+ public PointSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ float posX, float posY, float posZ,
+ Point2f[] distanceGain) {
+
+ super(soundData, initialGain, loopCount, release,
+ continuous, enable, region, priority );
+ ((PointSoundRetained)this.retained).setPosition(posX,posY,posZ);
+ ((PointSoundRetained)this.retained).setDistanceGain(distanceGain);
+ }
+
+ /**
+ * Construct a PointSound object accepting points as input for the position.
+ * and accepting separate arrays for the distance and gain scale factors
+ * components of distance attenuation.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source
+ * @param loopCount number of times loop is looped
+ * @param release flag denoting playing sound data to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param position 3D location of source
+ * @param attenuationDistance array of distance values used for attenuation
+ * @param attenuationGain array of gain scale factors used for attenuation
+ */
+ public PointSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ Point3f position,
+ float[] attenuationDistance,
+ float[] attenuationGain) {
+
+ super(soundData, initialGain, loopCount, release, continuous,
+ enable, region, priority );
+ ((PointSoundRetained)this.retained).setPosition(position);
+ ((PointSoundRetained)this.retained).setDistanceGain(
+ attenuationDistance, attenuationGain);
+ }
+
+ /**
+ * Construct a PointSound object accepting individual float parameters for
+ * the elements of the position points, and accepting separate arrays for
+ * the distance and gain scale factors components of distance attenuation.
+ * @param soundData sound data associated with this sound source node
+ * @param initialGain amplitude scale factor applied to sound source
+ * @param loopCount number of times loop is looped
+ * @param release flag denoting playing sound to end
+ * @param continuous denotes that sound silently plays when disabled
+ * @param enable sound switched on/off
+ * @param region scheduling bounds
+ * @param priority playback ranking value
+ * @param posX x coordinate of location of source
+ * @param posY y coordinate of location of source
+ * @param posZ z coordinate of location of source
+ * @param attenuationDistance array of distance values used for attenuation
+ * @param attenuationGain array of gain scale factors used for attenuation
+ */
+ public PointSound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ float posX, float posY, float posZ,
+ float[] attenuationDistance,
+ float[] attenuationGain) {
+
+ super(soundData, initialGain, loopCount, release,
+ continuous, enable, region, priority );
+ ((PointSoundRetained)this.retained).setPosition(posX,posY,posZ);
+ ((PointSoundRetained)this.retained).setDistanceGain(
+ attenuationDistance, attenuationGain);
+ }
+
+ /**
+ * Creates the retained mode PointSoundRetained object that this
+ * PointSound object will point to.
+ */
+ void createRetained() {
+ this.retained = new PointSoundRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Sets this sound's location from the vector provided.
+ * @param position the new location
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPosition(Point3f position) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound0"));
+
+ ((PointSoundRetained)this.retained).setPosition(position);
+ }
+
+ /**
+ * Sets this sound's position from the three values provided.
+ * @param x the new x position
+ * @param y the new y position
+ * @param z the new z position
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPosition(float x, float y, float z) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound0"));
+
+ ((PointSoundRetained)this.retained).setPosition(x,y,z);
+ }
+
+ /**
+ * Retrieves this sound's direction and places it in the
+ * vector provided.
+ * @param position the variable to receive the direction vector
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getPosition(Point3f position) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound2"));
+
+ ((PointSoundRetained)this.retained).getPosition(position);
+ }
+
+ /**
+ * Sets this sound's distance gain attenuation - where gain scale factor
+ * is applied to sound based on distance listener is from sound source.
+ * This form of setDistanceGain takes these pairs of values as an array of
+ * Point2f.
+ * @param attenuation defined by pairs of (distance,gain-scale-factor)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDistanceGain(Point2f[] attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound3"));
+
+ ((PointSoundRetained)this.retained).setDistanceGain(attenuation);
+ }
+
+ /**
+ * Sets this sound's distance gain attenuation as an array of Point2fs.
+ * This form of setDistanceGain accepts two separate arrays for these values.
+ * The distance and gainScale arrays should be of the same length. If the
+ * gainScale array length is greater than the distance array length, the
+ * gainScale array elements beyond the length of the distance array are
+ * ignored. If the gainScale array is shorter than the distance array, the
+ * last gainScale array value is repeated to fill an array of length equal
+ * to distance array.
+ * @param distance array of monotonically-increasing floats
+ * @param gain array of non-negative scale factors
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDistanceGain(float[] distance, float[] gain) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound3"));
+
+ ((PointSoundRetained)this.retained).setDistanceGain(distance, gain);
+ }
+
+ /**
+ * Get the length of this node's distance gain attenuation arrays.
+ * @return distance gain attenuation array length
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getDistanceGainLength() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound4"));
+
+ return (((PointSoundRetained)this.retained).getDistanceGainLength());
+ }
+
+ /**
+ * Gets this sound's distance attenuation. The distance attenuation
+ * pairs are copied into the specified array.
+ * The array must be large enough to hold all of the points.
+ * The individual array elements must be allocated by the caller.
+ * @param attenuation arrays containing distance attenuation pairs
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDistanceGain(Point2f[] attenuation) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound4"));
+
+ ((PointSoundRetained)this.retained).getDistanceGain(attenuation);
+ }
+
+ /**
+ * Gets this sound's distance gain attenuation values in separate arrays.
+ * The arrays must be large enough to hold all of the values.
+ * @param distance array of float distance from sound source
+ * @param gain array of non-negative scale factors associated with
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDistanceGain(float[] distance, float[] gain) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DISTANCE_GAIN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PointSound4"));
+
+ ((PointSoundRetained)this.retained).getDistanceGain(distance,gain);
+ }
+
+ /**
+ * Creates 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) {
+ PointSound p = new PointSound();
+ p.duplicateNode(this, forceDuplicate);
+ return p;
+ }
+
+ /**
+ * 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>PointSound</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+
+
+ /**
+ * Copies all PointSound 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ PointSoundRetained orgRetained = (PointSoundRetained)originalNode.retained;
+ PointSoundRetained thisRetained = (PointSoundRetained)this.retained;
+
+ Point3f p = new Point3f();
+ orgRetained.getPosition(p);
+ thisRetained.setPosition(p);
+
+ int len = orgRetained.getDistanceGainLength();
+ float distance[] = new float[len];
+ float gain[] = new float[len];
+ orgRetained.getDistanceGain(distance, gain);
+ thisRetained.setDistanceGain(distance, gain);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/PointSoundRetained.java b/src/classes/share/javax/media/j3d/PointSoundRetained.java
new file mode 100644
index 0000000..d0006c0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PointSoundRetained.java
@@ -0,0 +1,289 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.lang.Math;
+import java.net.URL;
+import javax.vecmath.Point3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point2f;
+
+/**
+ * The PointSoundRetained node (a sub-class of the SoundRetained node) defines
+ * a spatially-located sound source whose waves radiate uniformly in all
+ * directions from a given location in space.
+ */
+
+class PointSoundRetained extends SoundRetained {
+ /**
+ * Origin of Sound source in Listener's space.
+ */
+ Point3f position = new Point3f(0.0f, 0.0f, 0.0f);
+
+ /**
+ * The transformed position of this sound
+ */
+ Point3f xformPosition = new Point3f();
+ Transform3D trans = new Transform3D();
+
+ // Pairs of distances and gain scale factors that define piecewise linear
+ // gain attenuation between each pair.
+ float[] attenuationDistance;
+ float[] attenuationGain;
+
+ PointSoundRetained() {
+ this.nodeType = NodeRetained.POINTSOUND;
+ }
+
+ /**
+ * Sets this sound's location from the vector provided.
+ * @param position the new location
+ */
+ void setPosition(Point3f position) {
+ if (staticTransform != null) {
+ staticTransform.transform.transform(position, this.position);
+ } else {
+ this.position.set(position);
+ }
+
+ getLastLocalToVworld().transform(position, xformPosition);
+
+ dispatchAttribChange(POSITION_DIRTY_BIT, (new Point3f(this.position)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Sets this sound's position from the three values provided.
+ * @param x the new x position
+ * @param y the new y position
+ * @param z the new z position
+ */
+ void setPosition(float x, float y, float z) {
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ if (staticTransform != null) {
+ staticTransform.transform.transform(this.position);
+ }
+
+ getLastLocalToVworld().transform(position, xformPosition);
+
+ dispatchAttribChange(POSITION_DIRTY_BIT, (new Point3f(this.position)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves this sound's location and places it in the vector provided.
+ * @param position the variable to receive the location vector
+ */
+ void getPosition(Point3f position) {
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ invTransform.transform(this.position, position);
+ } else {
+ position.set(this.position);
+ }
+ }
+
+ void getXformPosition(Point3f position) {
+ position.set(this.xformPosition);
+ }
+
+ /**
+ * Sets this sound's distance gain attenuation - where gain scale factor
+ * is applied to sound based on distance listener is from sound source.
+ * @param distance attenuation pairs of (distance,gain-scale-factor)
+ */
+ void setDistanceGain(Point2f[] attenuation) {
+ // if attenuation array null set both attenuation components to null
+ if (attenuation == null) {
+ this.attenuationDistance = null;
+ this.attenuationGain = null;
+ // QUESTION: is this needed so that dispatch***() doesn't
+ // fail with null?
+ return;
+ }
+
+ int attenuationLength = attenuation.length;
+ this.attenuationDistance = new float[attenuationLength];
+ this.attenuationGain = new float[attenuationLength];
+ for (int i = 0; i < attenuationLength; i++) {
+ this.attenuationDistance[i] = attenuation[i].x;
+ this.attenuationGain[i] = attenuation[i].y;
+ }
+ dispatchAttribChange(DISTANCE_GAIN_DIRTY_BIT, attenuation);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Sets this sound's distance gain given separate arrays.
+ * applied to sound based on distance listener is from sound source.
+ * @param distance array of monotonically-increasing floats.
+ * @param gain array of amplitude scale factors associated with distances.
+ */
+ void setDistanceGain(float[] distance, float[] gain) {
+ // if distance or gain arrays are null then treat both as null
+ if (distance == null) {
+ this.attenuationDistance = null;
+ this.attenuationGain = null;
+ // QUESTION: is this needed so that dispatch***() doesn't
+ // fail with null?
+ return;
+ }
+
+ int gainLength = gain.length;
+ int distanceLength = distance.length;
+ this.attenuationDistance = new float[distanceLength];
+ this.attenuationGain = new float[distanceLength];
+ // Copy the distance array into nodes field
+ System.arraycopy(distance, 0, this.attenuationDistance, 0, distanceLength);
+ // Copy the gain array an array of same length as the distance array
+ if (distanceLength <= gainLength) {
+ System.arraycopy(gain, 0, this.attenuationGain, 0, distanceLength);
+ }
+ else {
+ System.arraycopy(gain, 0, this.attenuationGain, 0, gainLength);
+ // Extend gain array to length of distance array
+ // replicate last gain values.
+ for (int i=gainLength; i< distanceLength; i++) {
+ this.attenuationGain[i] = gain[gainLength - 1];
+ }
+ }
+ Point2f [] attenuation = new Point2f[distanceLength];
+ for (int i=0; i<distanceLength; i++) {
+ attenuation[i] = new Point2f(this.attenuationDistance[i],
+ this.attenuationGain[i]);
+ }
+ dispatchAttribChange(DISTANCE_GAIN_DIRTY_BIT, attenuation);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Gets this sound's distance attenuation array length
+ * @return distance gain attenuation array length
+ */
+ int getDistanceGainLength() {
+ if (attenuationDistance == null)
+ return 0;
+ else
+ return this.attenuationDistance.length;
+ }
+
+ /**
+ * Retieves sound's distance attenuation
+ * Put the contents of the two separate distance and gain arrays into
+ * an array of Point2f.
+ * @param attenuation containing distance attenuation pairs
+ */
+ void getDistanceGain(Point2f[] attenuation) {
+ // write into arrays passed in, don't do a new
+ if (attenuation == null)
+ return;
+ if (this.attenuationDistance == null ||
+ this.attenuationGain == null)
+ return;
+ int attenuationLength = attenuation.length;
+ // attenuationDistance and Gain array lengths should be the same
+ int distanceLength = this.attenuationDistance.length;
+ if (distanceLength > attenuationLength)
+ distanceLength = attenuationLength;
+ for (int i=0; i< distanceLength; i++) {
+ attenuation[i].x = attenuationDistance[i];
+ attenuation[i].y = attenuationGain[i];
+ }
+ }
+
+ /**
+ * Retieves this sound's attenuation distance and gain arrays, returned in
+ * separate arrays.
+ * @param distance array of monotonically-increasing floats.
+ * @param gain array of amplitude scale factors associated with distances.
+ */
+ void getDistanceGain(float[] distance, float[] gain) {
+ // write into arrays passed in, don't do a new
+ if (distance == null || gain == null)
+ return;
+ if (this.attenuationDistance == null || this.attenuationGain == null)
+ return;
+ // These two array length should be the same
+ int attenuationLength = this.attenuationDistance.length;
+ int distanceLength = distance.length;
+ if (distanceLength > attenuationLength)
+ distanceLength = attenuationLength;
+ System.arraycopy(this.attenuationDistance, 0, distance, 0, distanceLength);
+ attenuationLength = this.attenuationDistance.length;
+ int gainLength = gain.length;
+ if (gainLength > attenuationLength)
+ gainLength = attenuationLength;
+ System.arraycopy(this.attenuationGain, 0, gain, 0, gainLength);
+ }
+
+ /**
+ * This updates the positional fields of point sound.
+ *
+ * Distance gain attenuation field not maintained in mirror object.
+ */
+ void updateMirrorObject(Object[] objs) {
+ if (debugFlag)
+ debugPrint("PointSoundRetained:updateMirrorObj()");
+ int component = ((Integer)objs[1]).intValue();
+ int numSnds = ((Integer)objs[2]).intValue();
+ SoundRetained[] mSnds = (SoundRetained[]) objs[3];
+ if (component == -1) {
+ // update every field
+ initMirrorObject(((PointSoundRetained)objs[2]));
+ return;
+ }
+ if ((component & POSITION_DIRTY_BIT) != 0) {
+ for (int i = 0; i < numSnds; i++) {
+ PointSoundRetained point = (PointSoundRetained)mSnds[i];
+ point.position = (Point3f)objs[4];
+ point.getLastLocalToVworld().transform(point.position,
+ point.xformPosition);
+ }
+ }
+
+ // call the parent's mirror object update routine
+ super.updateMirrorObject(objs);
+ }
+
+ synchronized void initMirrorObject(PointSoundRetained ms) {
+ super.initMirrorObject(ms);
+ ms.position.set(this.position);
+ ms.xformPosition.set(this.xformPosition);
+ }
+
+ // Called on the mirror object
+ void updateTransformChange() {
+ super.updateTransformChange();
+ getLastLocalToVworld().transform(position, xformPosition);
+ // set flag looked at by Scheduler to denote Transform change
+ // this flag will force resneding transformed position to AudioDevice
+ if (debugFlag)
+ debugPrint("PointSoundRetained xformPosition is (" + xformPosition.x +
+ ", " + xformPosition.y + ", "+ xformPosition.z + ")");
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ xform.transform.transform(position, position);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PolygonAttributes.java b/src/classes/share/javax/media/j3d/PolygonAttributes.java
new file mode 100644
index 0000000..96ffddb
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PolygonAttributes.java
@@ -0,0 +1,464 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The PolygonAttributes object defines attributes for rendering polygon
+ * primitives.
+ * Polygon primitives include triangles, triangle strips, triangle fans,
+ * and quads.
+ * The polygon attributes that can be defined are:</li>
+ * <p><ul>
+ * <li>Rasterization mode - defines how the polygon is drawn: as points,
+ * outlines, or filled.<p>
+ * <ul>
+ * <li>POLYGON_POINT - the polygon is rendered as points
+ * drawn at the vertices.</li><p>
+ * <li>POLYGON_LINE - the polygon is rendered as lines
+ * drawn between consecutive vertices.</li><p>
+ * <li>POLYGON_FILL - the polygon is rendered by filling the interior
+ * between the vertices. The default mode.</li>
+ * <p></ul>
+ * <li>Face culling - defines which polygons are culled (discarded)
+ * before they are converted to screen coordinates.<p>
+ * <ul>
+ * <li>CULL_NONE - disables face culling.</li>
+ * <li>CULL_BACK - culls all back-facing polygons. The default.</li>
+ * <li>CULL_FRONT - culls all front-facing polygons.</li>
+ * <p></ul>
+ * <li>Back-face normal flip - specifies whether vertex normals of
+ * back-facing polygons are flipped (negated) prior to lighting. The
+ * setting is either true, meaning to flip back-facing normals, or
+ * false. The default is false.</li>
+ * <p>
+ * <li>Offset - the depth values of all pixels generated by polygon
+ * rasterization can be offset by a value that is computed for that
+ * polygon. Two values are used to specify the offset:</li><p>
+ * <ul>
+ * <li>Offset bias - the constant polygon offset that is added to
+ * the final device coordinate Z value of polygon primitives.</li>
+ * <p>
+ * <li>Offset factor - the factor to be multiplied by the
+ * slope of the polygon and then added to the final, device coordinate
+ * Z value of the polygon primitives.</li><p>
+ * </ul>
+ * These values can be either positive or negative. The default
+ * for both of these values is 0.0.<p>
+ * </ul>
+ *
+ * @see Appearance
+ */
+public class PolygonAttributes extends NodeComponent {
+
+ /**
+ * Specifies that this PolygonAttributes object allows reading its
+ * cull face information.
+ */
+ public static final int
+ ALLOW_CULL_FACE_READ = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_CULL_FACE_READ;
+
+ /**
+ * Specifies that this PolygonAttributes object allows writing its
+ * cull face information.
+ */
+ public static final int
+ ALLOW_CULL_FACE_WRITE = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_CULL_FACE_WRITE;
+
+ /**
+ * Specifies that this PolygonAttributes object allows reading its
+ * back face normal flip flag.
+ */
+ public static final int
+ ALLOW_NORMAL_FLIP_READ = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_NORMAL_FLIP_READ;
+
+ /**
+ * Specifies that this PolygonAttributes object allows writing its
+ * back face normal flip flag.
+ */
+ public static final int
+ ALLOW_NORMAL_FLIP_WRITE = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_NORMAL_FLIP_WRITE;
+
+ /**
+ * Specifies that this PolygonAttributes object allows reading its
+ * polygon mode information.
+ */
+ public static final int
+ ALLOW_MODE_READ = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_MODE_READ;
+
+ /**
+ * Specifies that this PolygonAttributes object allows writing its
+ * polygon mode information.
+ */
+ public static final int
+ ALLOW_MODE_WRITE = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_MODE_WRITE;
+
+ /**
+ * Specifies that this PolygonAttributes object allows reading its
+ * polygon offset information.
+ */
+ public static final int
+ ALLOW_OFFSET_READ = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_OFFSET_READ;
+
+ /**
+ * Specifies that this PolygonAttributes object allows writing its
+ * polygon offset information.
+ */
+ public static final int
+ ALLOW_OFFSET_WRITE = CapabilityBits.POLYGON_ATTRIBUTES_ALLOW_OFFSET_WRITE;
+
+ // Polygon rasterization modes
+ /**
+ * Render polygonal primitives as points drawn at the vertices
+ * of the polygon.
+ */
+ public static final int POLYGON_POINT = 0;
+ /**
+ * Render polygonal primitives as lines drawn between consecutive
+ * vertices of the polygon.
+ */
+ public static final int POLYGON_LINE = 1;
+ /**
+ * Render polygonal primitives by filling the interior of the polygon.
+ */
+ public static final int POLYGON_FILL = 2;
+
+ /**
+ * Don't perform any face culling.
+ */
+ public static final int CULL_NONE = 0;
+ /**
+ * Cull all back-facing polygons. This is the default mode.
+ */
+ public static final int CULL_BACK = 1;
+ /**
+ * Cull all front-facing polygons.
+ */
+ public static final int CULL_FRONT = 2;
+
+ /**
+ * Constructs a PolygonAttributes object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * cull face : CULL_BACK<br>
+ * back face normal flip : false<br>
+ * polygon mode : POLYGON_FILL<br>
+ * polygon offset : 0.0<br>
+ * polygon offset factor : 0.0<br>
+ * </ul>
+ */
+ public PolygonAttributes() {
+ // Just use defaults for all attributes
+ }
+
+ /**
+ * Constructs a PolygonAttributes object with specified values.
+ * @param polygonMode polygon rasterization mode; one of POLYGON_POINT,
+ * POLYGON_LINE, or POLYGON_FILL
+ * @param cullFace polygon culling mode; one of CULL_NONE,
+ * CULL_BACK, or CULL_FRONT
+ * @param polygonOffset constant polygon offset
+ */
+ public PolygonAttributes(int polygonMode,
+ int cullFace,
+ float polygonOffset) {
+ this(polygonMode, cullFace, polygonOffset, false, 0.0f);
+ }
+
+ /**
+ * Constructs PolygonAttributes object with specified values.
+ * @param polygonMode polygon rasterization mode; one of POLYGON_POINT,
+ * POLYGON_LINE, or POLYGON_FILL
+ * @param cullFace polygon culling mode; one of CULL_NONE,
+ * CULL_BACK, or CULL_FRONT
+ * @param polygonOffset constant polygon offset
+ * @param backFaceNormalFlip back face normal flip flag; true or false
+ */
+ public PolygonAttributes(int polygonMode,
+ int cullFace,
+ float polygonOffset,
+ boolean backFaceNormalFlip) {
+ this(polygonMode, cullFace, polygonOffset, backFaceNormalFlip, 0.0f);
+ }
+
+ /**
+ * Constructs PolygonAttributes object with specified values.
+ * @param polygonMode polygon rasterization mode; one of POLYGON_POINT,
+ * POLYGON_LINE, or POLYGON_FILL
+ * @param cullFace polygon culling mode; one of CULL_NONE,
+ * CULL_BACK, or CULL_FRONT
+ * @param polygonOffset constant polygon offset
+ * @param backFaceNormalFlip back face normal flip flag; true or false
+ * @param polygonOffsetFactor polygon offset factor for slope-based polygon
+ * offset
+ *
+ * @since Java 3D 1.2
+ */
+ public PolygonAttributes(int polygonMode,
+ int cullFace,
+ float polygonOffset,
+ boolean backFaceNormalFlip,
+ float polygonOffsetFactor) {
+
+ if (polygonMode < POLYGON_POINT || polygonMode > POLYGON_FILL)
+ throw new IllegalArgumentException(J3dI18N.getString("PolygonAttributes0"));
+
+ if (cullFace < CULL_NONE || cullFace > CULL_FRONT)
+ throw new IllegalArgumentException(J3dI18N.getString("PolygonAttributes12"));
+
+ ((PolygonAttributesRetained)this.retained).initPolygonMode(polygonMode);
+ ((PolygonAttributesRetained)this.retained).initCullFace(cullFace);
+ ((PolygonAttributesRetained)this.retained).initPolygonOffset(polygonOffset);
+ ((PolygonAttributesRetained)this.retained).initBackFaceNormalFlip(backFaceNormalFlip);
+ ((PolygonAttributesRetained)this.retained).initPolygonOffsetFactor(polygonOffsetFactor);
+ }
+
+ /**
+ * Sets the face culling for this
+ * appearance component object.
+ * @param cullFace the face to be culled, one of:
+ * CULL_NONE, CULL_FRONT, or CULL_BACK
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setCullFace(int cullFace) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CULL_FACE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes2"));
+
+ if (cullFace < CULL_NONE || cullFace > CULL_FRONT)
+ throw new IllegalArgumentException(J3dI18N.getString("PolygonAttributes3"));
+ if (isLive())
+ ((PolygonAttributesRetained)this.retained).setCullFace(cullFace);
+ else
+ ((PolygonAttributesRetained)this.retained).initCullFace(cullFace);
+
+ }
+
+ /**
+ * Gets the face culling for this
+ * appearance component object.
+ * @return the face to be culled
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getCullFace() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CULL_FACE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes4"));
+
+ return ((PolygonAttributesRetained)this.retained).getCullFace();
+ }
+
+ /**
+ * Sets the back face normal flip flag to the specified value.
+ * This flag indicates whether vertex normals of back facing polygons
+ * should be flipped (negated) prior to lighting. When this flag
+ * is set to true and back face culling is disabled, polygons are
+ * rendered as if the polygon had two sides with opposing normals.
+ * This feature is disabled by default.
+ * @param backFaceNormalFlip the back face normal flip flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setBackFaceNormalFlip(boolean backFaceNormalFlip) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_FLIP_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes5"));
+ if (isLive())
+ ((PolygonAttributesRetained)this.retained).setBackFaceNormalFlip(backFaceNormalFlip);
+ else
+ ((PolygonAttributesRetained)this.retained).initBackFaceNormalFlip(backFaceNormalFlip);
+
+ }
+
+ /**
+ * Gets the back face normal flip flag.
+ * @return the back face normal flip flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getBackFaceNormalFlip() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_NORMAL_FLIP_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes6"));
+
+ return ((PolygonAttributesRetained)this.retained).getBackFaceNormalFlip();
+ }
+
+ /**
+ * Sets the polygon rasterization mode for this
+ * appearance component object.
+ * @param polygonMode the polygon rasterization mode to be used; one of
+ * POLYGON_FILL, POLYGON_LINE, or POLYGON_POINT
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPolygonMode(int polygonMode) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_MODE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes7"));
+
+ if (polygonMode < POLYGON_POINT || polygonMode > POLYGON_FILL)
+ throw new IllegalArgumentException(J3dI18N.getString("PolygonAttributes8"));
+ if (isLive())
+ ((PolygonAttributesRetained)this.retained).setPolygonMode(polygonMode);
+ else
+ ((PolygonAttributesRetained)this.retained).initPolygonMode(polygonMode);
+
+ }
+
+ /**
+ * Gets the polygon rasterization mode for this
+ * appearance component object.
+ * @return the polygon rasterization mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getPolygonMode() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes9"));
+
+ return ((PolygonAttributesRetained)this.retained).getPolygonMode();
+ }
+
+ /**
+ * Sets the constant polygon offset to the specified value.
+ * This screen space
+ * offset is added to the final, device coordinate Z value of polygon
+ * primitives.
+ * @param polygonOffset the constant polygon offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPolygonOffset(float polygonOffset) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes10"));
+
+ if (isLive())
+ ((PolygonAttributesRetained)this.retained).setPolygonOffset(polygonOffset);
+ else
+ ((PolygonAttributesRetained)this.retained).initPolygonOffset(polygonOffset);
+
+ }
+
+ /**
+ * Gets the constant polygon offset.
+ * @return the constant polygon offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getPolygonOffset() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes11"));
+
+ return ((PolygonAttributesRetained)this.retained).getPolygonOffset();
+ }
+
+ /**
+ * Sets the polygon offset factor to the specified value.
+ * This factor is multiplied by the slope of the polygon, and
+ * then added to the final, device coordinate Z value of polygon
+ * primitives.
+ * @param polygonOffsetFactor the polygon offset factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void setPolygonOffsetFactor(float polygonOffsetFactor) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes10"));
+
+ if (isLive())
+ ((PolygonAttributesRetained)this.retained).
+ setPolygonOffsetFactor(polygonOffsetFactor);
+ else
+ ((PolygonAttributesRetained)this.retained).
+ initPolygonOffsetFactor(polygonOffsetFactor);
+ }
+
+ /**
+ * Gets the polygon offset factor.
+ * @return the polygon offset factor.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public float getPolygonOffsetFactor() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("PolygonAttributes11"));
+
+ return ((PolygonAttributesRetained)this.retained).getPolygonOffsetFactor();
+ }
+
+ /**
+ * Creates a retained mode PolygonAttributesRetained object that this
+ * PolygonAttributes component object will point to.
+ */
+ void createRetained() {
+ this.retained = new PolygonAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ PolygonAttributes pga = new PolygonAttributes();
+ pga.duplicateNodeComponent(this);
+ return pga;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ PolygonAttributesRetained attr = (PolygonAttributesRetained)
+ originalNodeComponent.retained;
+
+ PolygonAttributesRetained rt = (PolygonAttributesRetained) retained;
+
+ rt.initCullFace(attr.getCullFace());
+ rt.initBackFaceNormalFlip(attr.getBackFaceNormalFlip());
+ rt.initPolygonMode(attr.getPolygonMode());
+ rt.initPolygonOffset(attr.getPolygonOffset());
+ rt.initPolygonOffsetFactor(attr.getPolygonOffsetFactor());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PolygonAttributesRetained.java b/src/classes/share/javax/media/j3d/PolygonAttributesRetained.java
new file mode 100644
index 0000000..14792a1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PolygonAttributesRetained.java
@@ -0,0 +1,351 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * The PolygonAttributes object defines all rendering state that can be set
+ * as a component object of a Shape3D node.
+ */
+class PolygonAttributesRetained extends NodeComponentRetained {
+ // A list of pre-defined bits to indicate which component
+ // in this LineAttributesRetained object changed.
+ static final int POLYGON_MODE_CHANGED = 0x01;
+ static final int POLYGON_CULL_CHANGED = 0x02;
+ static final int POLYGON_OFFSET_CHANGED = 0x04;
+ static final int POLYGON_BACKFACENORMALFLIP_CHANGED = 0x08;
+ static final int POLYGON_OFFSETFACTOR_CHANGED = 0x10;
+
+ // Polygon rasterization mode (point, line, fill)
+ int polygonMode = PolygonAttributes.POLYGON_FILL;
+
+ // Face culling mode
+ int cullFace = PolygonAttributes.CULL_BACK;
+
+ // back face normal flip flag
+ boolean backFaceNormalFlip = false;
+
+ // constant polygon offset
+ float polygonOffset;
+
+ // polygon offset factor
+ float polygonOffsetFactor;
+
+ /**
+ * Sets the face culling for this
+ * appearance component object,
+ * @param cullFace the face to be culled, one of:
+ * CULL_NONE, CULL_FRONT, or CULL_BACK
+ */
+ final void initCullFace(int cullFace) {
+ this.cullFace = cullFace;
+ }
+
+ /**
+ * Sets the face culling for this
+ * appearance component object and sends a message notifying
+ * the interested structures of the change.
+ * @param cullFace the face to be culled, one of:
+ * CULL_NONE, CULL_FRONT, or CULL_BACK
+ */
+ final void setCullFace(int cullFace) {
+ initCullFace(cullFace);
+ sendMessage(POLYGON_CULL_CHANGED, new Integer(cullFace));
+ }
+
+ /**
+ * Gets the face culling for this
+ * appearance component object.
+ * @return the face to be culled
+ */
+ final int getCullFace() {
+ return cullFace;
+ }
+
+ /**
+ * Sets the back face normal flip flag to the specified value
+ * This flag indicates whether vertex normals of back facing polygons
+ * should be flipped (negated) prior to lighting. When this flag
+ * is set to true and back face culling is disabled, polygons are
+ * rendered as if the polygon had two sides with opposing normals.
+ * This feature is disabled by default
+ * @param backFaceNormalFlip the back face normal flip flag
+ */
+ final void initBackFaceNormalFlip(boolean backFaceNormalFlip) {
+ this.backFaceNormalFlip = backFaceNormalFlip;
+ }
+
+ /**
+ * Sets the back face normal flip flag to the specified value
+ * and sends a message notifying
+ * the interested structures of the change.
+ * This flag indicates whether vertex normals of back facing polygons
+ * should be flipped (negated) prior to lighting. When this flag
+ * is set to true and back face culling is disabled, polygons are
+ * rendered as if the polygon had two sides with opposing normals.
+ * This feature is disabled by default
+ * @param backFaceNormalFlip the back face normal flip flag
+ */
+ final void setBackFaceNormalFlip(boolean backFaceNormalFlip) {
+ initBackFaceNormalFlip(backFaceNormalFlip);
+ sendMessage(POLYGON_BACKFACENORMALFLIP_CHANGED,
+ (backFaceNormalFlip ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Gets the back face normal flip flag.
+ * @return the back face normal flip flag
+ */
+ final boolean getBackFaceNormalFlip() {
+ return backFaceNormalFlip;
+ }
+
+ /**
+ * Sets the polygon rasterization mode for this
+ * appearance component object.
+ * @param polygonMode the polygon rasterization mode to be used; one of
+ * POLYGON_FILL, POLYGON_LINE, or POLYGON_POINT
+ */
+ final void initPolygonMode(int polygonMode) {
+ this.polygonMode = polygonMode;
+ }
+
+ /**
+ * Sets the polygon rasterization mode for this
+ * appearance component object and sends a message notifying
+ * the interested structures of the change.
+ * @param polygonMode the polygon rasterization mode to be used; one of
+ * POLYGON_FILL, POLYGON_LINE, or POLYGON_POINT
+ */
+ final void setPolygonMode(int polygonMode) {
+ initPolygonMode(polygonMode);
+ sendMessage(POLYGON_MODE_CHANGED, new Integer(polygonMode));
+ }
+
+ /**
+ * Gets the polygon rasterization mode for this
+ * appearance component object.
+ * @return polygonMode the polygon rasterization mode
+ */
+ final int getPolygonMode() {
+ return polygonMode;
+ }
+
+ /**
+ * Sets the polygon offset to the specified value and sends a
+ * message notifying the interested structures of the change.
+ * This screen space offset is added to the final, device
+ * coordinate Z value of polygon primitives.
+ * @param polygonOffset the polygon offset
+ */
+ final void initPolygonOffset(float polygonOffset) {
+ this.polygonOffset = polygonOffset;
+ }
+
+ /**
+ * Sets the polygon offset to the specified value and sends a
+ * message notifying the interested structures of the change.
+ * This screen space offset is added to the final, device
+ * coordinate Z value of polygon primitives.
+ * @param polygonOffset the polygon offset
+ */
+ final void setPolygonOffset(float polygonOffset) {
+ initPolygonOffset(polygonOffset);
+ sendMessage(POLYGON_OFFSET_CHANGED, new Float(polygonOffset));
+ }
+
+
+ /**
+ * Gets the polygon offset.
+ * @return polygonOffset the polygon offset
+ */
+ final float getPolygonOffset() {
+ return polygonOffset;
+ }
+
+
+ /**
+ * Sets the polygon offset factor to the specified value and sends a
+ * message notifying the interested structures of the change.
+ * This factor is multiplied by the slope of the polygon, and
+ * then added to the final, device coordinate Z value of polygon
+ * primitives.
+ * @param polygonOffsetFactor the polygon offset factor
+ */
+ final void initPolygonOffsetFactor(float polygonOffsetFactor) {
+ this.polygonOffsetFactor = polygonOffsetFactor;
+ }
+
+ /**
+ * Sets the polygon offset factor to the specified value and sends a
+ * message notifying the interested structures of the change.
+ * This factor is multiplied by the slope of the polygon, and
+ * then added to the final, device coordinate Z value of polygon
+ * primitives.
+ * @param polygonOffsetFactor the polygon offset
+ */
+ final void setPolygonOffsetFactor(float polygonOffsetFactor) {
+ initPolygonOffsetFactor(polygonOffsetFactor);
+ sendMessage(POLYGON_OFFSETFACTOR_CHANGED,
+ new Float(polygonOffsetFactor));
+ }
+
+
+ /**
+ * Gets the polygon offset factor.
+ * @return polygonOffset the polygon offset factor
+ */
+ final float getPolygonOffsetFactor() {
+ return polygonOffsetFactor;
+ }
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ } else {
+ PolygonAttributesRetained mirrorPa = new PolygonAttributesRetained();
+ mirrorPa.set(this);
+ mirrorPa.source = source;
+ mirror = mirrorPa;
+ }
+ } else {
+ ((PolygonAttributesRetained) mirror).set(this);
+ }
+ }
+
+
+ /**
+ * Updates the native context
+ */
+ native void updateNative(long ctx,
+ int polygonMode, int cullFace,
+ boolean backFaceNormalFlip,
+ float polygonOffset,
+ float polygonOffsetFactor);
+
+ /**
+ * Updates the native context
+ */
+ void updateNative(long ctx) {
+ updateNative(ctx,
+ polygonMode, cullFace, backFaceNormalFlip,
+ polygonOffset, polygonOffsetFactor);
+ }
+
+ /**
+ * Initializes a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ ((PolygonAttributesRetained) mirror).set(this);
+ }
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ PolygonAttributesRetained mirrorPa = (PolygonAttributesRetained) mirror;
+
+ if ((component & POLYGON_MODE_CHANGED) != 0) {
+ mirrorPa.polygonMode = ((Integer)value).intValue();
+ }
+ else if ((component & POLYGON_CULL_CHANGED) != 0) {
+ mirrorPa.cullFace = ((Integer)value).intValue();
+ }
+ else if ((component & POLYGON_BACKFACENORMALFLIP_CHANGED) != 0) {
+ mirrorPa.backFaceNormalFlip = ((Boolean)value).booleanValue();
+ }
+ else if ((component & POLYGON_OFFSET_CHANGED) != 0) {
+ mirrorPa.polygonOffset = ((Float)value).floatValue();
+ }
+ else if ((component & POLYGON_OFFSETFACTOR_CHANGED) != 0) {
+ mirrorPa.polygonOffsetFactor = ((Float) value).floatValue();
+ }
+ }
+
+
+ boolean equivalent(PolygonAttributesRetained pr) {
+ return ((pr != null) &&
+ (pr.cullFace == cullFace) &&
+ (pr.backFaceNormalFlip == backFaceNormalFlip) &&
+ (pr.polygonOffset == polygonOffset) &&
+ (pr.polygonMode == polygonMode) &&
+ (pr.polygonOffsetFactor == polygonOffsetFactor));
+ }
+
+ protected void set(PolygonAttributesRetained pr) {
+ super.set(pr);
+ cullFace = pr.cullFace;
+ backFaceNormalFlip = pr.backFaceNormalFlip;
+ polygonMode = pr.polygonMode;
+ polygonOffset = pr.polygonOffset;
+ polygonOffsetFactor = pr.polygonOffsetFactor;
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.POLYGONATTRIBUTES_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.POLYGONATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+
+ }
+ void handleFrequencyChange(int bit) {
+ if (bit == PolygonAttributes.ALLOW_CULL_FACE_WRITE ||
+ bit == PolygonAttributes.ALLOW_NORMAL_FLIP_WRITE||
+ bit == PolygonAttributes.ALLOW_MODE_WRITE ||
+ bit == PolygonAttributes.ALLOW_OFFSET_WRITE) {
+ setFrequencyChangeMask(bit, 0x1);
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/PositionInterpolator.java b/src/classes/share/javax/media/j3d/PositionInterpolator.java
new file mode 100644
index 0000000..627453d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PositionInterpolator.java
@@ -0,0 +1,204 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Vector3d;
+
+
+/**
+ * Position interpolator behavior. This class defines a behavior
+ * that modifies the translational component of its target
+ * TransformGroup by linearly interpolating between a pair of
+ * specified positions (using the value generated by the
+ * specified Alpha object). The interpolated position is used
+ * to generate a translation transform along the local X-axis
+ * of this interpolator.
+ */
+
+public class PositionInterpolator extends TransformInterpolator {
+
+ private Transform3D translation = new Transform3D();
+ private Vector3d transv = new Vector3d();
+
+ float startPosition;
+ float endPosition;
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ PositionInterpolator() {
+ }
+
+ /**
+ * Constructs a trivial position interpolator with a specified target,
+ * an axisOfTranslation set to Identity, a startPosition of 0.0f, and
+ * an endPosition of 1.0f.
+ * @param alpha The alpha object for this Interpolator
+ * @param target The target for this position Interpolator
+ */
+ public PositionInterpolator(Alpha alpha, TransformGroup target) {
+ super(alpha, target);
+
+ this.startPosition = 0.0f;
+ this.endPosition = 1.0f;
+ }
+
+
+ /**
+ * Constructs a new position interpolator that varies the target
+ * TransformGroup's translational component (startPosition and endPosition).
+ * @param alpha the alpha object for this interpolator
+ * @param target the transformgroup node effected by this positionInterpolator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * system in which this interpolator operates. The translation is
+ * done along the X-axis of this local coordinate system.
+ * @param startPosition the starting position
+ * @param endPosition the ending position
+ */
+ public PositionInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ float startPosition,
+ float endPosition) {
+
+ super(alpha, target, axisOfTransform );
+
+ this.startPosition = startPosition;
+ this.endPosition = endPosition;
+ }
+
+ /**
+ * This method sets the startPosition for this interpolator.
+ * @param position The new start position
+ */
+ public void setStartPosition(float position) {
+ this.startPosition = position;
+ }
+
+ /**
+ * This method retrieves this interpolator's startPosition.
+ * @return the interpolator's start position value
+ */
+ public float getStartPosition() {
+ return this.startPosition;
+ }
+
+ /**
+ * This method sets the endPosition for this interpolator.
+ * @param position The new end position
+ */
+ public void setEndPosition(float position) {
+ this.endPosition = position;
+ }
+
+ /**
+ * This method retrieves this interpolator's endPosition.
+ * @return the interpolator's end position vslue
+ */
+ public float getEndPosition() {
+ return this.endPosition;
+ }
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.setTransformAxis(Transform3D)</code>
+ */
+ public void setAxisOfTranslation(Transform3D axisOfTranslation) {
+ setTransformAxis(axisOfTranslation);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfTranslation() {
+ 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) {
+
+ double val = (1.0-alphaValue)*startPosition + alphaValue*endPosition;
+
+ // construct a Transform3D from: axis * translation * axisInverse
+ transv.set(val, 0.0, 0.0);
+ translation.setTranslation(transv);
+
+ transform.mul(axis, translation);
+ 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) {
+ PositionInterpolator pi = new PositionInterpolator();
+ pi.duplicateNode(this, forceDuplicate);
+ return pi;
+ }
+
+ /**
+ * Copies all PositionInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ PositionInterpolator pi = (PositionInterpolator) originalNode;
+
+ setStartPosition(pi.getStartPosition());
+ setEndPosition(pi.getEndPosition());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/PositionPathInterpolator.java b/src/classes/share/javax/media/j3d/PositionPathInterpolator.java
new file mode 100644
index 0000000..544d68b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PositionPathInterpolator.java
@@ -0,0 +1,263 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+
+/**
+ * PositionPathInterpolator behavior. This class defines a behavior
+ * that modifies the translational component of its target TransformGroup
+ * by linearly interpolating among a series of predefined knot/position
+ * pairs (using the value generated by the specified Alpha object). The
+ * interpolated position is used to generate a translation transform
+ * in the local coordinate system of this interpolator. The first knot
+ * must have a value of 0.0. The last knot must have a value of 1.0. An
+ * intermediate knot with index k must have a value strictly greater
+ * than any knot with index less than k.
+ */
+
+public class PositionPathInterpolator extends PathInterpolator {
+ private Transform3D position = new Transform3D();
+ private Vector3f pos = new Vector3f();
+
+ // Array of positions at each knot
+ private Point3f positions[];
+ private float prevInterpolationValue = Float.NaN;
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ PositionPathInterpolator() {
+ }
+
+
+ /**
+ * Constructs a new PositionPathInterpolator that varies the transform
+ * of the target TransformGroup.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node affected by this translator
+ * @param axisOfTransform the transform that defines the local
+ * coordinate system in which this interpolator operates
+ * @param knots an array of knot values that specify interpolation points.
+ * @param positions an array of position values at the knots.
+ * @exception IllegalArgumentException if the lengths of the
+ * knots and positions arrays are not the same.
+ */
+ public PositionPathInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ float[] knots,
+ Point3f[] positions) {
+
+ super(alpha, target, axisOfTransform, knots);
+
+ if (knots.length != positions.length)
+ throw new IllegalArgumentException(J3dI18N.getString("PositionPathInterpolator0"));
+ setPathArrays(positions);
+ }
+
+
+ /**
+ * Sets the position at the specified index for this
+ * interpolator.
+ * @param index the index of the position to be changed
+ * @param position the new position at the index
+ */
+ public void setPosition(int index, Point3f position) {
+ this.positions[index].set(position);
+ }
+
+
+ /**
+ * Retrieves the position value at the specified index.
+ * @param index the index of the value requested
+ * @param position the variable to receive the position value at
+ * the specified index
+ */
+ public void getPosition(int index, Point3f position) {
+ position.set(this.positions[index]);
+ }
+
+
+ /**
+ * Replaces the existing arrays of knot values
+ * and position values with the specified arrays.
+ * The arrays of knots and positions are copied
+ * into this interpolator object.
+ * @param knots a new array of knot values that specify
+ * interpolation points
+ * @param positions a new array of position values at the knots
+ * @exception IllegalArgumentException if the lengths of the
+ * knots and positions arrays are not the same.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setPathArrays(float[] knots,
+ Point3f[] positions) {
+
+ if (knots.length != positions.length)
+ throw new IllegalArgumentException(J3dI18N.getString("PositionPathInterpolator0"));
+
+ setKnots(knots);
+ setPathArrays(positions);
+ }
+
+
+ // Set the specific arrays for this path interpolator
+ private void setPathArrays(Point3f[] positions) {
+
+ this.positions = new Point3f[positions.length];
+ for(int i = 0; i < positions.length; i++) {
+ this.positions[i] = new Point3f();
+ this.positions[i].set(positions[i]);
+ }
+ }
+
+
+ /**
+ * Copies the array of position values from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the positions.
+ * The individual array elements must be allocated by the caller.
+ * @param positions array that will receive the positions
+ *
+ * @since Java 3D 1.2
+ */
+ public void getPositions(Point3f[] positions) {
+ for (int i = 0; i < this.positions.length; i++) {
+ positions[i].set(this.positions[i]);
+ }
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.setTransformAxis(Transform3D)</code>
+ */
+ public void setAxisOfTranslation(Transform3D axisOfTranslation) {
+ setTransformAxis(axisOfTranslation);
+ }
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfTranslation() {
+ 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) {
+
+ computePathInterpolation(alphaValue);
+
+ if (currentKnotIndex == 0 &&
+ currentInterpolationValue == 0f) {
+ pos.x = positions[0].x;
+ pos.y = positions[0].y;
+ pos.z = positions[0].z;
+ } else {
+ pos.x = positions[currentKnotIndex].x +
+ (positions[currentKnotIndex+1].x -
+ positions[currentKnotIndex].x) * currentInterpolationValue;
+ pos.y = positions[currentKnotIndex].y +
+ (positions[currentKnotIndex+1].y -
+ positions[currentKnotIndex].y) * currentInterpolationValue;
+ pos.z = positions[currentKnotIndex].z +
+ (positions[currentKnotIndex+1].z -
+ positions[currentKnotIndex].z) * currentInterpolationValue;
+ }
+ position.setIdentity();
+ position.setTranslation(pos);
+
+ // construct a Transform3D from: axis * position * axisInverse
+ transform.mul(axis, position);
+ 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) {
+ PositionPathInterpolator ppi = new PositionPathInterpolator();
+ ppi.duplicateNode(this, forceDuplicate);
+ return ppi;
+ }
+
+
+ /**
+ * Copies all PositionPathInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ PositionPathInterpolator pi =
+ (PositionPathInterpolator) originalNode;
+
+ int len = pi.getArrayLengths();
+
+ // No API available to set the size of positions
+ positions = new Point3f[len];
+
+ Point3f dupPoint = new Point3f();
+ for (int i = 0; i < len; i++) {
+ positions[i] = new Point3f();
+ pi.getPosition(i, dupPoint);
+ setPosition(i, dupPoint);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/QuadArray.java b/src/classes/share/javax/media/j3d/QuadArray.java
new file mode 100644
index 0000000..caa8780
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/QuadArray.java
@@ -0,0 +1,156 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The QuadArray object draws the array of vertices as individual
+ * quadrilaterals. Each group
+ * of four vertices defines a quadrilateral to be drawn.
+ */
+
+public class QuadArray extends GeometryArray {
+
+ // non-public, no parameter constructor
+ QuadArray() {}
+
+ /**
+ * Constructs an empty QuadArray object with the specified
+ * number of vertices, and vertex format.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @exception IllegalArgumentException if vertexCount is less than 4
+ * or vertexCount is <i>not</i> a multiple of 4
+ */
+ public QuadArray(int vertexCount, int vertexFormat) {
+ super(vertexCount,vertexFormat);
+
+ if (vertexCount < 4 || ((vertexCount%4) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("QuadArray0"));
+ }
+
+ /**
+ * Constructs an empty QuadArray object with the specified
+ * number of vertices, and vertex format, number of texture coordinate
+ * sets, and texture coordinate mapping array.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3 or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 4
+ * or vertexCount is <i>not</i> a multiple of 4
+ *
+ * @since Java 3D 1.2
+ */
+ public QuadArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap);
+
+ if (vertexCount < 4 || ((vertexCount%4) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("QuadArray0"));
+ }
+
+ /**
+ * Creates the retained mode QuadArrayRetained object that this
+ * QuadArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new QuadArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ QuadArrayRetained rt = (QuadArrayRetained) retained;
+ int texSetCount = rt.getTexCoordSetCount();
+ QuadArray q;
+ if (texSetCount == 0) {
+ q = new QuadArray(rt.getVertexCount(),
+ rt.getVertexFormat());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ q = new QuadArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap);
+ }
+ q.duplicateNodeComponent(this);
+ return q;
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/QuadArrayRetained.java b/src/classes/share/javax/media/j3d/QuadArrayRetained.java
new file mode 100644
index 0000000..eb0d76f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/QuadArrayRetained.java
@@ -0,0 +1,460 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The QuadArray object draws the array of vertices as individual
+ * quadrilaterals. Each group
+ * of four vertices defines a quadrilateral to be drawn.
+ */
+
+class QuadArrayRetained extends GeometryArrayRetained {
+
+ QuadArrayRetained() {
+ this.geoType = GEO_TYPE_QUAD_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[4];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+ pnts[3] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ getVertexData(i++, pnts[3]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ getVertexData(i++, pnts[3]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ getVertexData(i++, pnts[3]);
+
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ getVertexData(i++, pnts[3]);
+
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ getVertexData(i++, pnts[3]);
+
+ if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ getVertexData(i++, pnts[3]);
+
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ getVertexData(i++, pnts[3]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("QuadArrayRetained0"));
+ default:
+ throw new RuntimeException("PickShape not supported for intersection ");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+
+ }
+
+ // intersect pnts[] with every quad in this object
+ boolean intersect(Point3d[] pnts) {
+ Point3d[] points = new Point3d[4];
+ double dist[] = new double[1];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+ points[3] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[2], points[3],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ }
+ break;
+ case 4: // Quad
+
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3]) ||
+ intersectTriTri(points[0], points[2], points[3],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[2], points[3],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ if (intersectSegment(points, pnts[0], pnts[1], dist,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0]) ||
+ intersectTriPnt(points[0], points[2], points[3],
+ pnts[0])) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) {
+
+ Point3d[] points = new Point3d[4];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+ points[3] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ thisToOtherVworld.transform(points[0]);
+ thisToOtherVworld.transform(points[1]);
+ thisToOtherVworld.transform(points[2]);
+ thisToOtherVworld.transform(points[3]);
+ if (geom.intersect(points)) {
+ return true;
+ }
+ } // for each quad
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ Point3d[] points = new Point3d[4];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+ points[3] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ if (intersectBoundingBox(points, box, null, null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ if (intersectBoundingSphere(points, bsphere, null,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ getVertexData(i++, points[3]);
+ if (intersectBoundingPolytope(points, bpolytope, null, null)) {
+ return true;
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ // From Graphics Gems IV (pg5) and Graphics Gems II, Pg170
+ // The centroid is the area-weighted sum of the centroids of
+ // disjoint triangles that make up the polygon.
+ void computeCentroid() {
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ Point3d pnt0 = getPoint3d();
+ Point3d pnt1 = getPoint3d();
+ Point3d pnt2 = getPoint3d();
+ Point3d pnt3 = getPoint3d();
+ Vector3d vec = getVector3d();
+ Vector3d normal = getVector3d();
+ Vector3d tmpvec = getVector3d();
+
+ double area;
+ double totalarea = 0;
+
+ centroid.x = 0;
+ centroid.y = 0;
+ centroid.z = 0;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnt0);
+ getVertexData(i++, pnt1);
+ getVertexData(i++, pnt2);
+ getVertexData(i++, pnt3);
+
+ // Determine the normal
+ tmpvec.sub(pnt0, pnt1);
+ vec.sub(pnt1, pnt2);
+
+ // Do the cross product
+ normal.cross(tmpvec, vec);
+ normal.normalize();
+ // If a degenerate triangle, don't include
+ if (Double.isNaN(normal.x+normal.y+normal.z))
+ continue;
+ tmpvec.set(0,0,0);
+ // compute the area of each triangle
+ getCrossValue(pnt0, pnt1, tmpvec);
+ getCrossValue(pnt1, pnt2, tmpvec);
+ getCrossValue(pnt2, pnt0, tmpvec);
+ area = normal.dot(tmpvec);
+ totalarea += area;
+ centroid.x += (pnt0.x+pnt1.x+pnt2.x) * area;
+ centroid.y += (pnt0.y+pnt1.y+pnt2.y) * area;
+ centroid.z += (pnt0.z+pnt1.z+pnt2.z) * area;
+
+ // compute the area of each triangle
+ tmpvec.set(0,0,0);
+ getCrossValue(pnt0, pnt2, tmpvec);
+ getCrossValue(pnt2, pnt3, tmpvec);
+ getCrossValue(pnt3, pnt0, tmpvec);
+ area = normal.dot(tmpvec);
+ totalarea += area;
+ centroid.x += (pnt3.x+pnt0.x+pnt2.x) * area;
+ centroid.y += (pnt3.y+pnt0.y+pnt2.y) * area;
+ centroid.z += (pnt3.z+pnt0.z+pnt2.z) * area;
+ }
+ if (totalarea != 0.0) {
+ area = 1.0/(3.0 * totalarea);
+ centroid.x *= area;
+ centroid.y *= area;
+ centroid.z *= area;
+ }
+ freeVector3d(tmpvec);
+ freeVector3d(vec);
+ freeVector3d(normal);
+ freePoint3d(pnt0);
+ freePoint3d(pnt1);
+ freePoint3d(pnt2);
+ freePoint3d(pnt3);
+ }
+
+ int getClassType() {
+ return QUAD_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Raster.java b/src/classes/share/javax/media/j3d/Raster.java
new file mode 100644
index 0000000..8057b50
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Raster.java
@@ -0,0 +1,749 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.Point;
+import java.awt.Dimension;
+
+
+/**
+ * The Raster object extends Geometry to allow drawing a raster image
+ * that is attached to a 3D location in the virtual world.
+ * It contains a 3D point that is defined in the local object
+ * coordinate system of the Shape3D node that references the Raster.
+ * It also contains a type specifier, a clipping mode, a reference to
+ * a ImageComponent2D object and/or a DepthComponent object, an
+ * integer x,y source offset and a size (width, height) to allow
+ * reading or writing a portion of the referenced image, and an
+ * integer x,y destination offset to position the raster relative to
+ * the transformed 3D point.
+ * In addition to being used as a type of geometry for drawing,
+ * a Raster may be used to readback pixel data (color and/or z-buffer)
+ * from the frame buffer in immediate mode.
+ * <p>
+ * The geometric extent of a Raster object is a single 3D point, specified
+ * by the raster position. This means that geometry-based picking or
+ * collision with a Raster object will only intersect the object at
+ * this single point; the 2D raster image is neither pickable
+ * nor collidable.
+ */
+
+public class Raster extends Geometry {
+ /**
+ * Specifies a Raster object with color data.
+ * In this mode, the image reference must point to
+ * a valid ImageComponent object.
+ *
+ * @see #setType
+ */
+ public static final int RASTER_COLOR = 0x1;
+
+ /**
+ * Specifies a Raster object with depth (z-buffer) data.
+ * In this mode, the depthImage reference must point to
+ * a valid DepthComponent object.
+ *
+ * @see #setType
+ */
+ public static final int RASTER_DEPTH = 0x2;
+
+ /**
+ * Specifies a Raster object with both color and depth (z-buffer) data.
+ * In this mode, the image reference must point to
+ * a valid ImageComponent object, and the depthImage reference
+ * must point to a valid DepthComponent object.
+ *
+ * @see #setType
+ */
+ public static final int RASTER_COLOR_DEPTH = RASTER_COLOR | RASTER_DEPTH;
+
+
+ /**
+ * Specifies that this raster object is not drawn
+ * if the raster position is outside the viewing volume.
+ * In this mode, the raster is not drawn when the transformed
+ * raster position is clipped out, even if part of the raster would
+ * have been visible. This is the default mode.
+ *
+ * @see #setClipMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int CLIP_POSITION = 0;
+
+ /**
+ * Specifies that the raster object is clipped as an image after
+ * the raster position has been transformed. In this mode, part
+ * of the raster may be drawn even when the transformed raster
+ * position is clipped out.
+ *
+ * @see #setClipMode
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int CLIP_IMAGE = 1;
+
+
+ /**
+ * Specifies that this Raster allows reading the position.
+ */
+ public static final int
+ ALLOW_POSITION_READ = CapabilityBits.RASTER_ALLOW_POSITION_READ;
+
+ /**
+ * Specifies that this Raster allows writing the position.
+ */
+ public static final int
+ ALLOW_POSITION_WRITE = CapabilityBits.RASTER_ALLOW_POSITION_WRITE;
+
+ /**
+ * Specifies that this Raster allows reading the source or
+ * destination offset.
+ */
+ public static final int
+ ALLOW_OFFSET_READ = CapabilityBits.RASTER_ALLOW_OFFSET_READ;
+
+ /**
+ * Specifies that this Raster allows writing the source or
+ * destination offset.
+ */
+ public static final int
+ ALLOW_OFFSET_WRITE = CapabilityBits.RASTER_ALLOW_OFFSET_WRITE;
+
+ /**
+ * Specifies that this Raster allows reading the image.
+ */
+ public static final int
+ ALLOW_IMAGE_READ = CapabilityBits.RASTER_ALLOW_IMAGE_READ;
+
+ /**
+ * Specifies that this Raster allows writing the image.
+ */
+ public static final int
+ ALLOW_IMAGE_WRITE = CapabilityBits.RASTER_ALLOW_IMAGE_WRITE;
+
+ /**
+ * Specifies that this Raster allows reading the depth component.
+ */
+ public static final int
+ ALLOW_DEPTH_COMPONENT_READ = CapabilityBits.RASTER_ALLOW_DEPTH_COMPONENT_READ;
+
+ /**
+ * Specifies that this Raster allows writing the depth component.
+ */
+ public static final int
+ ALLOW_DEPTH_COMPONENT_WRITE = CapabilityBits.RASTER_ALLOW_DEPTH_COMPONENT_WRITE;
+
+ /**
+ * Specifies that this Raster allows reading the size.
+ */
+ public static final int
+ ALLOW_SIZE_READ = CapabilityBits.RASTER_ALLOW_SIZE_READ;
+
+ /**
+ * Specifies that this Raster allows writing the size.
+ */
+ public static final int
+ ALLOW_SIZE_WRITE = CapabilityBits.RASTER_ALLOW_SIZE_WRITE;
+
+ /**
+ * Specifies that this Raster allows reading the type.
+ */
+ public static final int
+ ALLOW_TYPE_READ = CapabilityBits.RASTER_ALLOW_TYPE_READ;
+
+ /**
+ * Specifies that this Raster allows reading the clip mode.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_CLIP_MODE_READ = CapabilityBits.RASTER_ALLOW_CLIP_MODE_READ;
+
+ /**
+ * Specifies that this Raster allows writing the clip mode.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_CLIP_MODE_WRITE = CapabilityBits.RASTER_ALLOW_CLIP_MODE_WRITE;
+
+
+ /**
+ * Constructs a Raster object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * type : RASTER_COLOR<br>
+ * clipMode : CLIP_POSITION<br>
+ * position : (0,0,0)<br>
+ * srcOffset : (0,0)<br>
+ * size : (0,0)<br>
+ * dstOffset : (0,0)<br>
+ * image : null<br>
+ * depth component : null<br>
+ * </ul>
+ */
+ public Raster() {
+ }
+
+ /**
+ * Constructs a new Raster object with the specified values.
+ * @param pos the position in object coordinates of the upper-left
+ * corner of the raster
+ * @param type the type of raster object, one of: RASTER_COLOR,
+ * RASTER_DEPTH, or RASTER_COLOR_DEPTH
+ * @param xSrcOffset the x offset within the source array of pixels
+ * at which to start copying
+ * @param ySrcOffset the y offset within the source array of pixels
+ * at which to start copying
+ * @param width the number of columns of pixels to copy
+ * @param height the number of rows of pixels to copy
+ * @param image the ImageComponent2D object containing the
+ * color data
+ * @param depthComponent the DepthComponent object containing the depth
+ * (z-buffer) data
+ */
+ public Raster(Point3f pos,
+ int type,
+ int xSrcOffset,
+ int ySrcOffset,
+ int width,
+ int height,
+ ImageComponent2D image,
+ DepthComponent depthComponent) {
+
+ ((RasterRetained)this.retained).setPosition(pos);
+ ((RasterRetained)this.retained).setType(type);
+ ((RasterRetained)this.retained).setSrcOffset(xSrcOffset, ySrcOffset);
+ ((RasterRetained)this.retained).setSize(width, height);
+ ((RasterRetained)this.retained).setImage(image);
+ ((RasterRetained)this.retained).setDepthComponent(depthComponent);
+ }
+
+ /**
+ * Constructs a new Raster object with the specified values.
+ * @param pos the position in object coordinates of the upper-left
+ * corner of the raster
+ * @param type the type of raster object, one of: RASTER_COLOR,
+ * RASTER_DEPTH, or RASTER_COLOR_DEPTH
+ * @param srcOffset the offset within the source array of pixels
+ * at which to start copying
+ * @param size the width and height of the image to be copied
+ * @param image the ImageComponent2D object containing the
+ * color data
+ * @param depthComponent the DepthComponent object containing the depth
+ * (z-buffer) data
+ */
+ public Raster(Point3f pos,
+ int type,
+ Point srcOffset,
+ Dimension size,
+ ImageComponent2D image,
+ DepthComponent depthComponent) {
+
+ ((RasterRetained)this.retained).setPosition(pos);
+ ((RasterRetained)this.retained).setType(type);
+ ((RasterRetained)this.retained).setSrcOffset(srcOffset.x, srcOffset.y);
+ ((RasterRetained)this.retained).setSize(size.width, size.height);
+ ((RasterRetained)this.retained).setImage(image);
+ ((RasterRetained)this.retained).setDepthComponent(depthComponent);
+ }
+
+ /**
+ * Constructs a new Raster object with the specified values.
+ * @param pos the position in object coordinates of the upper-left
+ * corner of the raster
+ * @param type the type of raster object, one of: RASTER_COLOR,
+ * RASTER_DEPTH, or RASTER_COLOR_DEPTH
+ * @param clipMode the clipping mode of the raster object, one of:
+ * CLIP_POSITION or CLIP_IMAGE
+ * @param srcOffset the offset within the source array of pixels
+ * at which to start copying
+ * @param size the width and height of the image to be copied
+ * @param dstOffset the destination pixel offset of the upper-left
+ * corner of the rendered image relative to the transformed position
+ * @param image the ImageComponent2D object containing the
+ * color data
+ * @param depthComponent the DepthComponent object containing the depth
+ * (z-buffer) data
+ *
+ * @since Java 3D 1.3
+ */
+ public Raster(Point3f pos,
+ int type,
+ int clipMode,
+ Point srcOffset,
+ Dimension size,
+ Point dstOffset,
+ ImageComponent2D image,
+ DepthComponent depthComponent) {
+
+ ((RasterRetained)this.retained).setPosition(pos);
+ ((RasterRetained)this.retained).setType(type);
+ ((RasterRetained)this.retained).setClipMode(clipMode);
+ ((RasterRetained)this.retained).setSrcOffset(srcOffset.x, srcOffset.y);
+ ((RasterRetained)this.retained).setSize(size.width, size.height);
+ ((RasterRetained)this.retained).setDstOffset(dstOffset.x, dstOffset.y);
+ ((RasterRetained)this.retained).setImage(image);
+ ((RasterRetained)this.retained).setDepthComponent(depthComponent);
+ }
+
+ /**
+ * Creates the retained mode Raster object that this
+ * Raster object will point to.
+ */
+ void createRetained() {
+ retained = new RasterRetained();
+ retained.setSource(this);
+ }
+
+ /**
+ * Sets the position in object coordinates of this raster. This
+ * position is transformed into device coordinates and is used as
+ * the upper-left corner of the raster.
+ * @param pos the new position of this raster
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPosition(Point3f pos) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster0"));
+ ((RasterRetained)this.retained).setPosition(pos);
+ }
+
+ /**
+ * Retrieves the current position in object coordinates of this raster.
+ * @param pos the vector that will receive the current position
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getPosition(Point3f pos) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster1"));
+
+ ((RasterRetained)this.retained).getPosition(pos);
+ }
+
+ /**
+ * Sets the type of this raster object to one of: RASTER_COLOR,
+ * RASTER_DEPTH, or RASTER_COLOR_DEPTH.
+ * @param type the new type of this raster
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void setType(int type) {
+ checkForLiveOrCompiled();
+ ((RasterRetained)this.retained).setType(type);
+ }
+
+
+ /**
+ * Retrieves the current type of this raster object, one of: RASTER_COLOR,
+ * RASTER_DEPTH, or RASTER_COLOR_DEPTH.
+ * @return type the type of this raster
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getType() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TYPE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster2"));
+ return (((RasterRetained)this.retained).getType());
+ }
+
+
+ /**
+ * Sets the clipping mode of this raster object.
+ * @param clipMode the new clipping mode of this raster,
+ * one of: CLIP_POSITION or CLIP_IMAGE. The default mode
+ * is CLIP_POSITION.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setClipMode(int clipMode) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CLIP_MODE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster10"));
+
+ ((RasterRetained)this.retained).setClipMode(clipMode);
+ }
+
+
+ /**
+ * Retrieves the current clipping mode of this raster object.
+ * @return clipMode the clipping mode of this raster,
+ * one of: CLIP_POSITION or CLIP_IMAGE.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getClipMode() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CLIP_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster11"));
+
+ return (((RasterRetained)this.retained).getClipMode());
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>setSrcOffset(int,int)</code>
+ */
+ public void setOffset(int xSrcOffset, int ySrcOffset) {
+ setSrcOffset(xSrcOffset, ySrcOffset);
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>setSrcOffset(java.awt.Point)</code>
+ */
+ public void setOffset(Point srcOffset) {
+ setSrcOffset(srcOffset);
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>getSrcOffset(java.awt.Point)</code>
+ */
+ public void getOffset(Point srcOffset) {
+ getSrcOffset(srcOffset);
+ }
+
+
+ /**
+ * Sets the offset within the source array of pixels
+ * at which to start copying.
+ * @param xSrcOffset the x offset within the source array of pixels
+ * at which to start copying
+ * @param ySrcOffset the y offset within the source array of pixels
+ * at which to start copying
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setSrcOffset(int xSrcOffset, int ySrcOffset) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster7"));
+
+ ((RasterRetained)this.retained).setSrcOffset(xSrcOffset, ySrcOffset);
+ }
+
+
+ /**
+ * Sets the offset within the source array of pixels
+ * at which to start copying.
+ * @param srcOffset the new source pixel offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setSrcOffset(Point srcOffset) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster7"));
+
+ ((RasterRetained)this.retained).setSrcOffset(srcOffset.x, srcOffset.y);
+ }
+
+
+ /**
+ * Retrieves the current source pixel offset.
+ * @param srcOffset the object that will receive the source offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getSrcOffset(Point srcOffset) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster8"));
+
+ ((RasterRetained)this.retained).getSrcOffset(srcOffset);
+ }
+
+
+ /**
+ * Sets the number of pixels to be copied from the pixel array.
+ * @param width the number of columns in the array of pixels to copy
+ * @param height the number of rows in the array of pixels to copy
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setSize(int width, int height) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster9"));
+
+ ((RasterRetained)this.retained).setSize(width, height);
+ }
+
+ /**
+ * Sets the size of the array of pixels to be copied.
+ * @param size the new size
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setSize(Dimension size) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster9"));
+
+ ((RasterRetained)this.retained).setSize(size.width, size.height);
+ }
+
+
+ /**
+ * Retrieves the current raster size.
+ * @param size the object that will receive the size
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getSize(Dimension size) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster1"));
+
+ ((RasterRetained)this.retained).getSize(size);
+ }
+
+
+ /**
+ * Sets the destination pixel offset of the upper-left corner of
+ * the rendered image relative to the transformed position. This
+ * pixel offset is added to the transformed raster position prior
+ * to rendering the image.
+ *
+ * @param xDstOffset the x coordinate of the new offset
+ * @param yDstOffset the y coordinate of the new offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setDstOffset(int xDstOffset, int yDstOffset) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster7"));
+
+ ((RasterRetained)this.retained).setDstOffset(xDstOffset, yDstOffset);
+ }
+
+
+ /**
+ * Sets the destination pixel offset of the upper-left corner of
+ * the rendered image relative to the transformed position. This
+ * pixel offset is added to the transformed raster position prior
+ * to rendering the image.
+ *
+ * @param dstOffset the new destination pixel offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setDstOffset(Point dstOffset) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster7"));
+
+ ((RasterRetained)this.retained).setDstOffset(dstOffset.x, dstOffset.y);
+ }
+
+
+ /**
+ * Retrieves the current destination pixel offset.
+ * @param dstOffset the object that will receive the destination offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getDstOffset(Point dstOffset) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_OFFSET_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster8"));
+
+ ((RasterRetained)this.retained).getDstOffset(dstOffset);
+ }
+
+
+ /**
+ * Sets the pixel array used to copy pixels to/from a Canvas3D.
+ * This is used when the type is RASTER_COLOR or RASTER_COLOR_DEPTH.
+ * @param image the ImageComponent2D object containing the
+ * color data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setImage(ImageComponent2D image) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster3"));
+ ((RasterRetained)this.retained).setImage(image);
+ }
+
+ /**
+ * Retrieves the current pixel array object.
+ * @return image the ImageComponent2D object containing the
+ * color data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public ImageComponent2D getImage() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster4"));
+ return (((RasterRetained)this.retained).getImage());
+ }
+
+ /**
+ * Sets the depth image used to copy pixels to/from a Canvas3D.
+ * This is used when the type is RASTER_DEPTH or RASTER_COLOR_DEPTH.
+ * @param depthComponent the DepthComponent object containing the
+ * depth (z-buffer) data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDepthComponent(DepthComponent depthComponent) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DEPTH_COMPONENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster5"));
+ ((RasterRetained)this.retained).setDepthComponent(depthComponent);
+ }
+
+ /**
+ * Retrieves the current depth image object.
+ * @return depthImage DepthComponent containing the
+ * depth (z-buffer) data
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public DepthComponent getDepthComponent() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DEPTH_COMPONENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Raster6"));
+ return (((RasterRetained)this.retained).getDepthComponent());
+ }
+
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ Raster r = new Raster();
+ r.duplicateNodeComponent(this);
+ return r;
+ }
+
+
+ /**
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @deprecated replaced with duplicateNodeComponent(
+ * NodeComponent originalNodeComponent, boolean forceDuplicate)
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+ checkDuplicateNodeComponent(originalNodeComponent);
+ }
+
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ RasterRetained raster = (RasterRetained) originalNodeComponent.retained;
+ RasterRetained rt = (RasterRetained) retained;
+
+ Point3f p = new Point3f();
+ raster.getPosition(p);
+ rt.setPosition(p);
+ rt.setType(raster.getType());
+ rt.setClipMode(raster.getClipMode());
+ Point offset = new Point();
+ raster.getSrcOffset(offset);
+ rt.setSrcOffset(offset.x, offset.y);
+ raster.getDstOffset(offset);
+ rt.setDstOffset(offset.x, offset.y);
+ Dimension dim = new Dimension();
+ raster.getSize(dim);
+ rt.setSize(dim.width, dim.height);
+ rt.setImage((ImageComponent2D) getNodeComponent(
+ raster.getImage(),
+ forceDuplicate,
+ originalNodeComponent.nodeHashtable));
+ rt.setDepthComponent((DepthComponent) getNodeComponent(
+ raster.getDepthComponent(),
+ forceDuplicate,
+ originalNodeComponent.nodeHashtable));
+ }
+
+
+ /**
+ * This function is called from getNodeComponent() to see if any of
+ * the sub-NodeComponents duplicateOnCloneTree flag is true.
+ * If it is the case, current NodeComponent needs to
+ * duplicate also even though current duplicateOnCloneTree flag is false.
+ * This should be overwrite by NodeComponent which contains sub-NodeComponent.
+ */
+ boolean duplicateChild() {
+ if (getDuplicateOnCloneTree())
+ return true;
+ RasterRetained rt = (RasterRetained) retained;
+
+ NodeComponent nc = rt.getImage();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getDepthComponent();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ return false;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/RasterRetained.java b/src/classes/share/javax/media/j3d/RasterRetained.java
new file mode 100644
index 0000000..e80d706
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RasterRetained.java
@@ -0,0 +1,719 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.Point;
+import java.awt.Dimension;
+import java.util.ArrayList;
+
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+
+/**
+ * A Retained Raster.
+ */
+
+class RasterRetained extends GeometryRetained {
+
+ /**
+ * Raster type
+ */
+ int type = Raster.RASTER_COLOR;
+
+ int clipMode = Raster.CLIP_POSITION;
+ Point3f position = new Point3f();
+ int xSrcOffset = 0;
+ int ySrcOffset = 0;
+
+ // Used internally in CLIP_IMAGE mode
+ private int xOffset = 0;
+ private int yOffset = 0;
+
+ int width = 0;
+ int height = 0;
+ int xDstOffset = 0;
+ int yDstOffset = 0;
+ ImageComponent2DRetained image = null;
+ DepthComponentRetained depthComponent = null;
+ float lastAlpha = 1.0f;
+
+ private Point3d adjPos; // Position of the Raster after adjusting for dstOffset
+ private Point2d winCoord; // Position of Raster in window coordinates
+ private Transform3D vwip; // Vworld to Image plate transform
+ // false when computeWinCoord() get null RenderMolecule.
+ // In this case rendering is skip.
+ private boolean validVwip;
+
+
+ RasterRetained() {
+ this.geoType = GEO_TYPE_RASTER;
+
+ vwip = new Transform3D();
+ adjPos = new Point3d();
+ winCoord = new Point2d();
+ }
+
+ /**
+ * Set the Raster position
+ * @param position new raster position
+ */
+ final void setPosition(Point3f pos) {
+ geomLock.getLock();
+ position.x = pos.x;
+ position.y = pos.y;
+ position.z = pos.z;
+ geomLock.unLock();
+ sendChangedMessage(J3dThread.UPDATE_GEOMETRY, null, null);
+ }
+
+ /**
+ * Retrieves the Raster's position
+ * @param position the variable to receive the position vector
+ */
+ final void getPosition(Point3f pos) {
+ pos.x = position.x;
+ pos.y = position.y;
+ pos.z = position.z;
+ }
+
+ /**
+ * Sets the type of this raster object to one of: RASTER_COLOR,
+ * RASTER_DEPTH, or RASTER_COLOR_DEPTH.
+ * @param type the new type of this raster
+ */
+ final void setType(int type) {
+ geomLock.getLock();
+ this.type = type;
+ geomLock.unLock();
+ }
+
+
+ /**
+ * Retrieves the current type of this raster object, one of: RASTER_COLOR,
+ * RASTER_DEPTH, or RASTER_COLOR_DEPTH.
+ * @return type the type of this raster
+ */
+ final int getType() {
+ return type;
+ }
+
+ /**
+ * Sets the clipping mode of this raster object.
+ * @param clipMode the new clipping mode of this raster,
+ * one of: CLIP_POSITION or CLIP_IMAGE. The default mode
+ * is CLIP_POSITION.
+ */
+ final void setClipMode(int clipMode) {
+
+ geomLock.getLock();
+ this.clipMode = clipMode;
+ geomLock.unLock();
+ computeBoundingBox();
+ if(source.isLive()) {
+ //update the Shape3Ds that refer to this Raster
+ int un = userLists.size();
+ ArrayList shapeList;
+ Shape3DRetained ms, shape;
+ int sn;
+ for(int i = 0; i < un; i++) {
+ shapeList = (ArrayList)userLists.get(i);
+ sn = shapeList.size();
+ for(int j = 0; j < sn; j++) {
+ ms = (Shape3DRetained)shapeList.get(j);
+ shape = (Shape3DRetained)ms.sourceNode;
+ shape.setBoundsAutoCompute(false);
+ shape.setBounds(geoBounds);
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * Retrieves the current clipping mode of this raster object.
+ * @return clipMode the clipping mode of this raster,
+ * one of: CLIP_POSITION or CLIP_IMAGE.
+ */
+ final int getClipMode() {
+ return clipMode;
+ }
+
+ /**
+ * Sets the offset within the source array of pixels at which
+ * to start copying.
+ * @param xSrcOffset the x offset within the source array of pixels
+ * at which to start copying
+ * @param ySrcOffset the y offset within the source array of pixels
+ * at which to start copying
+ */
+ final void setSrcOffset(int xSrcOffset, int ySrcOffset) {
+ geomLock.getLock();
+ this.xSrcOffset = xSrcOffset;
+ this.ySrcOffset = ySrcOffset;
+ geomLock.unLock();
+ }
+
+ /**
+ * Retrieves the current source pixel offset.
+ * @param srcOffset the object that will receive the source offset
+ */
+ final void getSrcOffset(Point srcOffset) {
+ srcOffset.setLocation(xSrcOffset, ySrcOffset);
+ }
+
+ /**
+ * Sets the number of pixels to be copied from the pixel array.
+ * @param width the number of columns in the array of pixels to copy
+ * @param height the number of rows in the array of pixels to copy
+ */
+ final void setSize(int width, int height) {
+ geomLock.getLock();
+ this.width = width;
+ this.height = height;
+ geomLock.unLock();
+ }
+
+
+ /**
+ * Sets the size of the array of pixels to be copied.
+ * @param size the new size
+ */
+ final void getSize(Dimension size) {
+ size.setSize(width, height);
+ }
+
+
+ /**
+ * Sets the destination pixel offset of the upper-left
+ * corner of the rendered image relative to the transformed position.
+ * @param xDstOffset the x coordinate of the new offset
+ * @param yDstOffset the y coordinate of the new offset
+ */
+ final void setDstOffset(int xDstOffset, int yDstOffset) {
+ geomLock.getLock();
+ this.xDstOffset = xDstOffset;
+ this.yDstOffset = yDstOffset;
+ geomLock.unLock();
+ }
+
+ /**
+ * Retrieves the current destination pixel offset.
+ * @param dstOffset the object that will receive the destination offset
+ */
+ final void getDstOffset(Point dstOffset) {
+ dstOffset.setLocation(xDstOffset, yDstOffset);
+ }
+
+
+ /**
+ * Sets the pixel array used to copy pixels to/from a Canvas3D.
+ * This is used when the type is RASTER_COLOR or RASTER_COLOR_DEPTH.
+ * @param image the ImageComponent2D object containing the
+ * color data
+ */
+ final void setImage(ImageComponent2D image) {
+ ImageComponent2DRetained oldImage = this.image;
+
+ if (this.source.isLive()) {
+
+ if (this.image != null) {
+ this.image.clearLive(refCount);
+ }
+ if (image != null) {
+ ((ImageComponent2DRetained)image.retained).setLive(inBackgroundGroup, refCount);
+ }
+ }
+
+ geomLock.getLock();
+ if (image != null) {
+ ImageComponent2DRetained rimage =
+ (ImageComponent2DRetained)image.retained;
+ rimage.setRasterRef();
+ this.image = rimage;
+ } else {
+ this.image = null;
+ }
+
+
+
+ // Set the lastAlpha to 1.0f
+ lastAlpha = 1.0f;
+ geomLock.unLock();
+ sendChangedMessage((J3dThread.UPDATE_RENDER|J3dThread.UPDATE_RENDERING_ATTRIBUTES),
+ oldImage, this.image);
+ }
+
+ /**
+ * Retrieves the current pixel array object.
+ * @return image the ImageComponent2D object containing the
+ * color data
+ */
+ final ImageComponent2D getImage() {
+ return (image == null ? null : (ImageComponent2D)image.source);
+ }
+
+ /**
+ * Sets the depth image used to copy pixels to/from a Canvas3D.
+ * This is used when the type is RASTER_DEPTH or RASTER_COLOR_DEPTH.
+ * @param depthImage the DepthComponent object containing the
+ * depth (z-buffer) data
+ */
+ final void setDepthComponent(DepthComponent depthComponent) {
+ if (this.source.isLive()) {
+ if (this.depthComponent != null) {
+ this.depthComponent.clearLive(refCount);
+ }
+ if (depthComponent != null) {
+ ((DepthComponentRetained)depthComponent.retained).setLive(inBackgroundGroup, refCount);
+ }
+ }
+ geomLock.getLock();
+ if (depthComponent == null) {
+ this.depthComponent = null;
+ } else {
+ this.depthComponent =
+ (DepthComponentRetained)depthComponent.retained;
+ }
+ geomLock.unLock();
+ }
+
+ /**
+ * Retrieves the current depth image object.
+ * @return depthImage DepthComponent containing the
+ * depth (z-buffer) data
+ */
+ final DepthComponent getDepthComponent() {
+ return (depthComponent == null ? null :
+ (DepthComponent)depthComponent.source);
+ }
+
+ void setLive(boolean inBackgroundGroup, int refCount) {
+ super.doSetLive(inBackgroundGroup, refCount);
+ if (image != null) {
+ image.setLive(inBackgroundGroup, refCount);
+ }
+ if (depthComponent != null) {
+ depthComponent.setLive(inBackgroundGroup, refCount);
+ }
+ isEditable = source.getCapability(Raster.ALLOW_OFFSET_WRITE) ||
+ source.getCapability(Raster.ALLOW_POSITION_WRITE) ||
+ ((type & Raster.RASTER_COLOR) != 0 &&
+ source.getCapability(Raster.ALLOW_IMAGE_WRITE)) ||
+ ((type & Raster.RASTER_DEPTH) != 0 &&
+ source.getCapability(
+ Raster.ALLOW_DEPTH_COMPONENT_WRITE)) ||
+ source.getCapability( Raster.ALLOW_SIZE_WRITE);
+
+ super.markAsLive();
+ }
+
+ void clearLive(int refCount) {
+ super.clearLive(refCount);
+ if (image != null)
+ image.clearLive(refCount);
+ if (depthComponent != null)
+ depthComponent.clearLive(refCount);
+ }
+ /*
+ // Simply pass along to the NodeComponents
+ void compile(CompileState compState) {
+ setCompiled();
+
+ if (image != null)
+ image.compile(compState);
+
+ if (depthComponent != null)
+ depthComponent.compile(compState);
+ }
+ */
+
+ void computeBoundingBox() {
+ if(clipMode == Raster.CLIP_IMAGE) {
+ // Disable view frustum culling by setting the raster's bounds to
+ // infinity.
+ Point3d minBounds = new Point3d(Double.NEGATIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ Double.NEGATIVE_INFINITY);
+ Point3d maxBounds = new Point3d(Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY);
+ geoBounds.setUpper(maxBounds);
+ geoBounds.setLower(minBounds);
+ } else {
+ Point3d center = new Point3d();
+ center.x = position.x;
+ center.y = position.y;
+ center.z = position.z;
+ geoBounds.setUpper(center);
+ geoBounds.setLower(center);
+ }
+ }
+
+ void update() {
+ computeBoundingBox();
+ }
+
+ private void sendChangedMessage(int threads, Object arg1, Object arg2) {
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+ synchronized (universeList) {
+ int numMessages = universeList.size();
+ J3dMessage[] m = new J3dMessage[numMessages];
+ for (int i=0; i<numMessages; i++) {
+ m[i] = VirtualUniverse.mc.getMessage();
+ m[i].type = J3dMessage.GEOMETRY_CHANGED;
+ m[i].threads = threads;
+ m[i].args[0] = Shape3DRetained.
+ getGeomAtomsArray((ArrayList)userLists.get(i));
+ m[i].args[1] = this;
+ Object[] obj = new Object[2];
+ obj[0] = arg1;
+ obj[1] = arg2;
+ m[i].args[2] = obj;
+ m[i].args[3] = new Integer(changedFrequent);
+ m[i].universe = (VirtualUniverse)universeList.get(i);
+ }
+ VirtualUniverse.mc.processMessage(m);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Native method that does the rendering
+ */
+ native void execute(long ctx, GeometryRetained geo,
+ boolean updateAlpha, float alpha,
+ int type, int width, int height,
+ int xSrcOffset, int ySrcOffset,
+ float x, float y, float z, byte[] image);
+ /*
+ native void executeTiled(long ctx, GeometryRetained geo,
+ int format, int width, int height,
+ int xSrcOffset, int ySrcOffset,
+ int deltaw, int deltah,
+ float x, float y, float z, byte[] image);
+ */
+
+ void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha,
+ boolean multiScreen, int screen,
+ boolean ignoreVertexColors, int pass) {
+
+ // Compute the offset position of the raster
+ // This has to be done at render time because we need access
+ // to the Canvas3D info
+
+ // Check if adjusted position needs to be computed
+
+ validVwip = true;
+ adjPos.set((double)position.x, (double)position.y, (double)position.z);
+
+ if(xDstOffset != 0 || yDstOffset != 0) {
+ getOffsetPos(cv, ra, adjPos);
+ }
+
+ xOffset = xSrcOffset;
+ yOffset = ySrcOffset;
+ // Check if the image needs to be clipped
+
+ if (clipMode == Raster.CLIP_IMAGE)
+ clipImage(cv, ra, adjPos);
+
+ if (!validVwip) {
+ return;
+ }
+ if ((image != null) && !image.imageYdownCacheDirty) {
+ // If its a null image do nothing ..
+ if (image != null && image.imageYdown[0] != null) {
+ // Handle alpha, if necessary
+ // Note, raster always makes a copy, so we can update alpha
+ // in the image
+ if (updateAlpha) {
+ // Update Alpha value per screen
+ // If the image is by reference, force a copy, since
+ // we need to copy the alpha values
+ image.updateAlpha(cv, screen, alpha);
+ execute(cv.ctx, this, updateAlpha, alpha,
+ type, width, height, xOffset, yOffset,
+ (float)adjPos.x, (float)adjPos.y , (float)adjPos.z,
+ image.imageYdown[screen]);
+ }
+ else {
+ execute(cv.ctx, this, updateAlpha, alpha,
+ type, width, height, xOffset, yOffset,
+ (float)adjPos.x, (float)adjPos.y , (float)adjPos.z,
+ image.imageYdown[0]);
+ }
+ }
+ }
+ /*
+ else {
+ // Should never come here ...
+ if ((type & Raster.RASTER_COLOR) != 0){
+ // Send down the tiles
+ int tilew = image.tilew;
+ int tileh = image.tileh;
+ int w = width, h = height;
+ int curw, curh;
+ int xo = xOffset, yo = yOffset;
+ float xpos = position.x, ypos = position.y;
+ // First find the tile {x.y} to start from
+ int tileX = 0, tileY = 0;
+ while (xo > tilew) {
+ tileX++;
+ xo -= tilew;
+ }
+
+ while (yo > tileh) {
+ tileY++;
+ yo -= tileh;
+ }
+ int initTileY = image.minTileY+tileY;
+ int initTileX = image.minTileX+tileX;
+ int m,n;
+ int deltaw, deltah = 0;
+ curh = tileh - yo;
+ for (m = initTileY; m < image.minTileY+image.numYTiles; m++) {
+ curw = tilew - xo;
+ deltaw = 0;
+ w = width;
+ for (n = initTileX; n < image.minTileX+image.numXTiles; n++) {
+ java.awt.image.Raster ras;
+ ras = image.bImage[0].getTile(n,m);
+ byte[] tmpImage = ((DataBufferByte)ras.getDataBuffer()).getData();
+ if (w <curw) {
+ curw = w;
+ }
+ executeTiled(cv.ctx, this, image.storedYdownFormat,
+ curw,
+ curh,
+ xo, yo,
+ deltaw, deltah,
+ (float)adjPos.x, (float)adjPos.y , (float)adjPos.z,
+ tmpImage);
+
+ xo = 0;
+ w -= curw;
+ if (w == 0)
+ break;
+ deltaw += curw;
+ curw = tilew;
+ }
+ yo = 0;
+ h -= curh;
+ if (h == 0)
+ break;
+ deltah += curh;
+ curh = tileh;
+ if (h < curh)
+ curh = h;
+ xo = xOffset;
+ }
+ }
+ if ((type & Raster.RASTER_DEPTH) != 0) {
+ execute(cv.ctx, this, updateAlpha, alpha,
+ Raster.RASTER_DEPTH, width, height,
+ xOffset, yOffset,
+ (float)adjPos.x, (float)adjPos.y , (float)adjPos.z,
+ image.imageYdown[screen]);
+ }
+ }
+ */
+ }
+
+ /**
+ * Computes the position of the origin of this Raster in object coordinates
+ * The origin is the top-left corner offset by the destination offset
+ * The offset position is returned in objCoord
+ *
+ * @param objCoord - Position of the Raster in object coordinates
+ * @return nothing. The offset position is returned in objCoord
+ */
+ private void getOffsetPos(Canvas3D canvas, RenderAtom ra, Point3d objCoord) {
+ computeWinCoord(canvas, ra, winCoord, objCoord);
+
+ // Add the destination offset to the Raster position in window coordinates
+ winCoord.x -= xDstOffset;
+ winCoord.y -= yDstOffset;
+
+ // Now back transform this offset pt. from window to object coordinates
+ computeObjCoord(canvas, winCoord, objCoord);
+ // pt. is now in object space again
+ }
+
+ /**
+ * Clips the image against the window. This method simulates
+ * clipping the image by determining the subimage that will be
+ * drawn and adjusting the xOffset and yOffset accordingly. Only
+ * clipping against the left and top edges needs to be handled,
+ * clipping against the right and bottom edges will be handled by
+ * the underlying graphics library automatically.
+ */
+ private void clipImage(Canvas3D canvas, RenderAtom ra, Point3d objCoord) {
+ // check if window coordinates have already been calculated by
+ // getOffsetPos().
+
+ if(xDstOffset == 0 && yDstOffset == 0) {
+ double x = objCoord.x;
+ double y = objCoord.y;
+ double z = objCoord.z;
+ computeWinCoord(canvas, ra, winCoord, objCoord);
+
+ if ((winCoord.x > 0) && (winCoord.y > 0)) {
+ objCoord.x = x;
+ objCoord.y = y;
+ objCoord.z = z;
+ return; // no need to clip
+ }
+ } else {
+ if ((winCoord.x > 0) && (winCoord.y > 0)) {
+ return;
+ }
+ }
+
+
+ // Check if the Raster point will be culled
+ // Note that w use 1 instead of 0, because when hardware
+ // tranform the coordinate back to winCoord it may get
+ // a small negative value due to numerically inaccurancy.
+ // This clip the Raster away and cause flickering
+ // (see bug 4732965)
+ if(winCoord.x < 1) {
+ // Negate the window position and use this as the offset
+ xOffset = (int)-winCoord.x+1;
+ winCoord.x = 1;
+ }
+
+ if(winCoord.y < 1) {
+ // Negate the window position and use this as the offset
+ yOffset = (int)-winCoord.y+1;
+ winCoord.y = 1;
+ }
+
+ //check if user-specified subimage is smaller than the clipped image
+ if (xOffset < xSrcOffset)
+ xOffset = xSrcOffset;
+ if(yOffset < ySrcOffset)
+ yOffset = ySrcOffset;
+ // back transform to object coords
+ if(xDstOffset == 0 && yDstOffset == 0)
+ // Image plate to local Xform needs to be computed
+ computeObjCoord(canvas, winCoord, objCoord);
+ else {
+ // vwip should contain the Imageplate to Local transform
+ // (it was computed by computeObjCoord).
+ // We can simply use the previously computed value here
+ canvas.getPixelLocationInImagePlate(winCoord.x, winCoord.y,
+ objCoord.z, objCoord);
+ vwip.transform(objCoord);
+ }
+
+ }
+
+ private void computeObjCoord(Canvas3D canvas, Point2d winCoord, Point3d objCoord) {
+ // Back transform this pt. from window to object coordinates
+ // Assumes this method is ALWAYS called after computeWinCoord has been
+ // called. computeWinCoord calculates the Vworld to Image Plate Xform.
+ // This method simply uses it without recomputing it.
+
+ canvas.getPixelLocationInImagePlate(winCoord.x, winCoord.y, objCoord.z,
+ objCoord);
+ // Get image plate to object coord transform
+ // inv(P x M)
+ vwip.invert();
+ vwip.transform(objCoord);
+ }
+
+ private void computeWinCoord(Canvas3D canvas, RenderAtom ra,
+ Point2d winCoord, Point3d objCoord) {
+ // Get local to Vworld transform
+ RenderMolecule rm = ra.renderMolecule;
+
+ if (rm == null) {
+ // removeRenderAtom() may set ra.renderMolecule to null
+ // in RenderBin before this renderer thread run.
+ validVwip = false;
+ return;
+ }
+ // MT safe issue: We can't reference ra.renderMolecule below since
+ // RenderBin thread may set it to null anytime. Use rm instead.
+
+ Transform3D lvw = rm.localToVworld[rm.localToVworldIndex[
+ NodeRetained.LAST_LOCAL_TO_VWORLD]];
+
+ // Get Vworld to image plate Xform
+ canvas.getLastVworldToImagePlate(vwip);
+
+ // v' = vwip x lvw x v
+ // where v' = transformed vertex,
+ // lvw = local to Vworld Xform
+ // vwip = Vworld to Image plate Xform
+ // v = vertex
+
+ // Compute composite local to image plate Xform
+ vwip.mul(lvw);
+
+ // Transform the Raster's position from object to world coordinates
+ vwip.transform(objCoord);
+
+ // Get the window coordinates of this point
+ canvas.getPixelLocationFromImagePlate(objCoord, winCoord);
+ }
+
+ int getClassType() {
+ return RASTER_TYPE;
+ }
+
+ // notifies the Raster mirror object that the image data in a referenced
+ // ImageComponent object is changed.
+ // Currently we are not making use of this information.
+
+ void notifyImageComponentImageChanged(ImageComponentRetained image,
+ ImageComponentUpdateInfo value) {
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ return false;
+ }
+ boolean intersect(Bounds targetBound) {
+ return false;
+ }
+
+ boolean intersect(Point3d[] pnts) {
+ return false;
+ }
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained
+ geom) {
+ return false;
+ }
+ boolean intersect(Transform3D thisLocalToVworld,
+ Transform3D otherLocalToVworld,
+ GeometryRetained geom) {
+ return false;
+ }
+
+ boolean intersect(Transform3D thisLocalToVworld, Bounds targetBound) {
+ return false;
+ }
+ void handleFrequencyChange(int bit) {
+ if (bit == Raster.ALLOW_IMAGE_WRITE)
+ setFrequencyChangeMask(bit, 0x1);
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/RenderAtom.java b/src/classes/share/javax/media/j3d/RenderAtom.java
new file mode 100644
index 0000000..ac78e5c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderAtom.java
@@ -0,0 +1,366 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.*;
+
+/**
+ * A RenderAtom is a wrapper for a GeometryAtom in a given RenderBin.
+ */
+
+class RenderAtom extends Object implements ObjectUpdate {
+ /**
+ * The geometry atom of this render atom
+ */
+ GeometryAtom geometryAtom = null;
+
+ /**
+ * The RenderMolecule for this RenderAtom
+ */
+ RenderMolecule renderMolecule = null;
+
+
+ /**
+ * The lights that influence this RenderAtom
+ */
+ LightRetained[] lights = null;
+
+ /**
+ * The fog that influences this RenderAtom
+ */
+ FogRetained fog = null;
+
+ /**
+ * The model clip that influences this RenderAtom
+ */
+ ModelClipRetained modelClip = null;
+
+ /**
+ * The appearance that influences this RenderAtom
+ */
+ AppearanceRetained app = null;
+
+ //
+ // Convert all boolean to a bitmask, saves memory since
+ // there are many RenderAtoms per view
+ //
+
+ /**
+ * Indicates whether or not this object is in
+ * the render bin.
+ */
+ static int IN_RENDERBIN = 0x1;
+
+ // True if the above localeVwcBounds is a local copy rather
+ // than a reference to the bounds in shape
+ static int HAS_SEPARATE_LOCALE_VWC_BOUNDS = 0x2;
+
+ // true if one of the geometries not going to a display list, hence
+ // need to maintain a local localeVwcBounds in order to avoid
+ // conflict with the vwcBounds in shape which is CURRENT
+ // while the checking of localeVwcBounds in rendering time
+ // should be LAST. In order words, localeVwcBounds contain
+ // the last vwcBounds, whereas, shape.vwcBounds contain the
+ // current vwcBounds
+ //
+ static int NEED_SEPARATE_LOCALE_VWC_BOUNDS = 0x4;
+
+ static int ON_UPDATELIST = 0x8;
+ static int ON_LOCALE_VWC_BOUNDS_UPDATELIST = 0x10;
+ // true if comes from Oriented Shape3D
+ static int IS_ORIENTED = 0x20;
+ // true if in dirty oriented render atom list
+ static int IN_DIRTY_ORIENTED_RAs = 0x40;
+
+ // true if in dirty depth sort position list
+ static int IN_SORTED_POS_DIRTY_TRANSP_LIST = 0x80;
+
+ // A bitmask for all the bit specified above in this renderAtom
+ int dirtyMask = 0;
+
+ /**
+ * Environment set that this renderAtom belongs to, used
+ * to compare the new env set with the old one when the
+ * scoping/bounds of a light/fog changes
+ */
+ EnvironmentSet envSet;
+
+ /**
+ * Used for non-text3d
+ */
+ BoundingBox localeVwcBounds = null;
+
+
+ /**
+ * The last time this atom was reported visible
+ */
+ long lastVisibleTime = -1;
+
+ /**
+ * Next and Previous references for the list of RenderAtoms
+ * groupType is a mask set to true if this renderAtom is part of the displaylist array
+ * of the renderMolecule
+ * One per geometry in the geometryArr in the geometryAtom, since
+ * each geometry in the geometryAtom can end up in a different
+ * atomList(primary, secondary, seperatedlist) of the renderMoceule
+ */
+ RenderAtomListInfo[] rListInfo;
+
+ /**
+ * Used in depthSorted transparency, once per rInfo
+ */
+ TransparentRenderingInfo[] parentTInfo = null;
+
+ /**
+ * Used when depth sorted transparecy is turned on
+ * one dlist per rinfo
+ */
+ int[] dlistIds = null;
+
+ // One per geometry in the geometryArr in the geometryAtom
+ static int TEXT3D = 0x1;
+ static int DLIST = 0x2;
+ static int CG = 0x4;
+ static int OTHER = 0x8;
+ static int SEPARATE_DLIST_PER_GEO = 0x10;
+ static int VARRAY = 0x20;
+ static int SEPARATE_DLIST_PER_RINFO = 0x40;
+ static int PRIMARY = TEXT3D | DLIST | CG | OTHER|SEPARATE_DLIST_PER_RINFO;
+
+ // Rendermolecule to which its currently being added
+ RenderMolecule added = null;
+
+ // Rendermolecule from which its currently being removed
+ RenderMolecule removed = null;
+
+ // non-null, if part of the add list(for the next frame) in the renderMolecule
+ RenderAtom nextAdd = null;
+ RenderAtom prevAdd = null;
+
+ // non-null, if part of the remove list(for the next frame) in the renderMolecule
+ RenderAtom nextRemove = null;
+ RenderAtom prevRemove = null;
+
+ RenderAtom() {
+ }
+
+ /**
+ * This sets the inRenderBin flag
+ */
+ synchronized void setRenderBin(boolean value) {
+ if (value == false) {
+ app = null;
+ dirtyMask &= ~IN_RENDERBIN;
+ dirtyMask &= ~ON_LOCALE_VWC_BOUNDS_UPDATELIST;
+ dirtyMask &= ~ON_UPDATELIST;
+ }
+ else {
+ dirtyMask |= IN_RENDERBIN;
+ }
+
+ }
+
+ /**
+ * This returns whether or not this atom goes into the opaque
+ * light bin
+ */
+ boolean isOpaque() {
+ AppearanceRetained app = geometryAtom.source.appearance;
+
+ if (app == null) {
+ return true;
+ }
+
+ TransparencyAttributesRetained ta = app.transparencyAttributes;
+
+ if (!VirtualUniverse.mc.isD3D()) {
+ // D3D doesn't support line/point antialiasing
+ switch (geometryAtom.geoType) {
+ case GeometryRetained.GEO_TYPE_POINT_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET:
+ if ((app.pointAttributes != null) &&
+ app.pointAttributes.pointAntialiasing) {
+ return false;
+ }
+ break;
+ case GeometryRetained.GEO_TYPE_LINE_SET:
+ case GeometryRetained.GEO_TYPE_LINE_STRIP_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ if ((app.lineAttributes != null) &&
+ app.lineAttributes.lineAntialiasing) {
+ return false;
+ }
+ break;
+ case GeometryRetained.GEO_TYPE_RASTER:
+ case GeometryRetained.GEO_TYPE_COMPRESSED:
+ break;
+ default:
+ if (app.polygonAttributes != null) {
+ if ((app.polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT) &&
+ (app.pointAttributes != null) &&
+ app.pointAttributes.pointAntialiasing) {
+ return false;
+ } else if ((app.polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE) &&
+ (app.lineAttributes != null) &&
+ app.lineAttributes.lineAntialiasing) {
+ return false;
+ }
+ }
+ break;
+ }
+
+ return ((ta == null) ||
+ (ta.transparencyMode ==
+ TransparencyAttributes.NONE) ||
+ (ta.transparencyMode ==
+ TransparencyAttributes.SCREEN_DOOR));
+ } else {
+ return ((ta == null) ||
+ (ta.transparencyMode ==
+ TransparencyAttributes.NONE));
+ }
+ }
+
+ boolean inRenderBin() {
+ return ((dirtyMask & IN_RENDERBIN) != 0);
+ }
+
+ boolean hasSeparateLocaleVwcBounds() {
+ return ((dirtyMask & HAS_SEPARATE_LOCALE_VWC_BOUNDS) != 0);
+ }
+
+ boolean needSeparateLocaleVwcBounds() {
+ return ((dirtyMask & NEED_SEPARATE_LOCALE_VWC_BOUNDS) != 0);
+ }
+
+ boolean onUpdateList() {
+ return ((dirtyMask & ON_UPDATELIST) != 0);
+ }
+
+ boolean onLocaleVwcBoundsUpdateList() {
+ return ((dirtyMask & ON_LOCALE_VWC_BOUNDS_UPDATELIST) != 0);
+ }
+
+ boolean isOriented() {
+ return ((dirtyMask & IS_ORIENTED) != 0);
+ }
+
+ boolean inDepthSortList() {
+ return ((dirtyMask & IN_SORTED_POS_DIRTY_TRANSP_LIST) != 0);
+ }
+
+
+ boolean inDirtyOrientedRAs() {
+ return ((dirtyMask & IN_DIRTY_ORIENTED_RAs) != 0);
+ }
+
+ public void updateObject() {
+ if (inRenderBin()) {
+ int lastLVWIndex =
+ renderMolecule.localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD];
+
+ for (int i = 0; i < rListInfo.length; i++) {
+ if (rListInfo[i].geometry() == null)
+ continue;
+
+ if (geometryAtom.source.inBackgroundGroup) {
+ if (rListInfo[i].infLocalToVworld == null)
+ rListInfo[i].infLocalToVworld = new Transform3D();
+
+ // to preserve the character transformation for Text3D atoms
+ renderMolecule.localToVworld[lastLVWIndex].getRotation(
+ rListInfo[i].infLocalToVworld);
+ rListInfo[i].infLocalToVworld.mul(
+ geometryAtom.lastLocalTransformArray[i]);
+ } else {
+ rListInfo[i].localToVworld.mul(
+ renderMolecule.localeLocalToVworld[lastLVWIndex],
+ geometryAtom.lastLocalTransformArray[i]);
+ }
+ }
+ }
+ dirtyMask &= ~ON_UPDATELIST;
+ }
+
+ void updateOrientedTransform() {
+ int lastLVWIndex =
+ renderMolecule.localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD];
+ Transform3D orientedTransform =
+ ((OrientedShape3DRetained)geometryAtom.source).
+ getOrientedTransform(renderMolecule.renderBin.view.viewIndex);
+ for (int i = 0; i < rListInfo.length; i++) {
+
+ if (geometryAtom.geoType == GeometryRetained.GEO_TYPE_TEXT3D &&
+ geometryAtom.lastLocalTransformArray[i] != null) {
+ if (geometryAtom.source.inBackgroundGroup) {
+ if (rListInfo[i].infLocalToVworld == null)
+ rListInfo[i].infLocalToVworld = new Transform3D();
+
+ rListInfo[i].infLocalToVworld.mul(
+ renderMolecule.infLocalToVworld[lastLVWIndex],
+ orientedTransform);
+ rListInfo[i].infLocalToVworld.mul(
+ geometryAtom.lastLocalTransformArray[i]);
+ } else {
+ rListInfo[i].localToVworld.mul(
+ renderMolecule.localeLocalToVworld[lastLVWIndex],
+ orientedTransform);
+ rListInfo[i].localToVworld.mul(
+ geometryAtom.lastLocalTransformArray[i]);
+ }
+ } else {
+ if (geometryAtom.source.inBackgroundGroup) {
+ if (rListInfo[i].infLocalToVworld == null)
+ rListInfo[i].infLocalToVworld = new Transform3D();
+
+ rListInfo[i].infLocalToVworld.mul(
+ renderMolecule.infLocalToVworld[lastLVWIndex],
+ orientedTransform);
+ } else {
+ rListInfo[i].localToVworld.mul(
+ renderMolecule.localeLocalToVworld[lastLVWIndex],
+ orientedTransform);
+ }
+ }
+ }
+ }
+
+ // updateLocaleVwcBounds is called from RenderBin.updateObject()
+ // to update the local copy of the localeVwcBounds
+
+ void updateLocaleVwcBounds() {
+
+ // it is possible that inRenderBin could be false because
+ // the renderAtom could have been removed from RenderBin
+ // in the same frame, and removeRenderAtoms does happen
+ // before updateLocaleVwcBounds
+ if (inRenderBin()) {
+ // Check if the locale of this is different from the
+ // locale on which the view is,then compute the translated
+ // localeVwcBounds
+ if (renderMolecule.renderBin.locale != geometryAtom.source.locale) {
+
+ geometryAtom.source.locale.
+ hiRes.difference(renderMolecule.renderBin.locale.hiRes,
+ renderMolecule.renderBin.localeTranslation);
+ localeVwcBounds.translate(geometryAtom.source.vwcBounds,
+ renderMolecule.renderBin.localeTranslation);
+ } else {
+ localeVwcBounds.set(geometryAtom.source.vwcBounds);
+ }
+ dirtyMask &= ~ON_LOCALE_VWC_BOUNDS_UPDATELIST;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/RenderAtomListInfo.java b/src/classes/share/javax/media/j3d/RenderAtomListInfo.java
new file mode 100644
index 0000000..0509419
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderAtomListInfo.java
@@ -0,0 +1,43 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+/**
+ * Information per geometry in the renderAtom, there are several
+ * of these per RenderAtom, one per geometry in GeometryAtom
+ */
+class RenderAtomListInfo extends Object {
+ // RenderAtom that its a part of
+
+ RenderAtom renderAtom = null;
+
+ // Specific geometry index in the GeometryAtom geometryArr list that
+ // corresponds to this RenderAtomListInfo
+ int index;
+
+ // Prev and next pointer
+ RenderAtomListInfo next = null;
+ RenderAtomListInfo prev = null;
+
+ // Which bucket in the renderMolecule that it falls info
+ int groupType = 0;
+
+ // Used only for Text3D
+ // background geometry rendering
+ Transform3D infLocalToVworld = null;
+ Transform3D localToVworld = null;
+
+
+ GeometryRetained geometry() {
+ return renderAtom.geometryAtom.geometryArray[index];
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/RenderBin.java b/src/classes/share/javax/media/j3d/RenderBin.java
new file mode 100644
index 0000000..089f887
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderBin.java
@@ -0,0 +1,6891 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+/**
+ * The RenderBin is a structure that optimizes rendering by doing efficient
+ * state sorting of objects to be rendered.
+ */
+
+class RenderBin extends J3dStructure implements ObjectUpdate {
+
+ /**
+ * The list of RenderAtoms
+ */
+ ArrayList renderAtoms = new ArrayList(5);
+
+ /**
+ * A couple ArrayLists used during light Processing
+ */
+ ArrayList lightMessageList = new ArrayList(5);
+
+ ArrayList bhTreesArrList = new ArrayList(5);
+
+ // Messges retrieved when a message is sent to RenderingEnv Structure
+ J3dMessage[] m;
+
+ /**
+ * List of renderMolecules that are soleUser
+ * have to do a 2 pass, first update values
+ * then sort based on equivalent material
+ */
+ ArrayList rmUpdateList = new ArrayList();
+ ArrayList aBinUpdateList = new ArrayList();
+
+ /**
+ * List of TextureBin that are soleUser that
+ * needs to have its components updated @updateObject time
+ */
+ ArrayList tbUpdateList = new ArrayList();
+
+ /**
+ * List of Bins that are soleUser that have new renderAtom
+ * added into, which requires a pre-update screening to
+ * check if any of its node component changes could have been
+ * missed because the changes happen when all the render atoms
+ * are temporarily removed from the bin.
+ */
+ ArrayList updateCheckList = new ArrayList();
+
+ /**
+ * The number of lights supported by the underlying context.
+ */
+ int maxLights;
+
+ /**
+ * The opaque objects
+ */
+ LightBin opaqueBin = null;
+
+ /**
+ * OpaqueBins to be added for the next frame
+ */
+ LightBin addOpaqueBin = null;
+
+ // This is a list of textureBins to be rendered, if the transpSortPolicy
+ // is NONE, otherwise, if the transpSortPolicy is geometry, then
+ // this is the list of renderAtoms to be rendered
+ ArrayList allTransparentObjects = new ArrayList(5);
+
+ TransparentRenderingInfo transparentInfo;
+
+ /**
+ * List of RenderAtoms whose postion have changed - only used for
+ * depth sorted transparency
+ */
+ ArrayList positionDirtyList = new ArrayList(5);
+
+
+ /**
+ * A set of freelists for RenderBin type objects
+ */
+ ArrayList lightBinFreelist = new ArrayList(5);
+ ArrayList envSetFreelist = new ArrayList(5);
+ ArrayList attrBinFreelist = new ArrayList(5);
+ ArrayList textureBinFreelist = new ArrayList(5);
+ ArrayList renderMoleculeFreelist = new ArrayList(5);
+ ArrayList transparentInfoFreeList = new ArrayList(5);
+
+ /**
+ * Used when ColoringAttributes is null
+ */
+ Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
+
+ /**
+ * Used when Background is null
+ */
+ Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
+
+ /**
+ * The backgound color data.
+ */
+ BackgroundRetained background = new BackgroundRetained();
+
+ /**
+ * The view platform transforms.
+ */
+ // used for rendering - lights and fog modelling
+ Transform3D vworldToVpc = new Transform3D();
+
+ // used for updating vpSchedSphere
+ Transform3D vpcToVworld = new Transform3D();
+
+ /**
+ * Two bounding spheres to track the scheduling region of
+ * the view platform.
+ */
+ BoundingSphere vpSchedSphereInVworld = new BoundingSphere();
+
+ /**
+ * To cache the view frustum bounding box.
+ */
+ BoundingBox viewFrustumBBox = new BoundingBox();
+ BoundingBox canvasFrustumBBox = new BoundingBox();
+
+ /**
+ * To ensure that vpcToVworld is valid (not null) for the first pass
+ */
+ boolean afterFirst = false;
+
+ /**
+ * back clip distance in vworld
+ */
+ double backClipDistanceInVworld;
+
+ boolean backClipActive = false;
+
+ /**
+ * These variables control when compaction occurs
+ */
+ int frameCount = 0;
+ int frameCountCutoff = 150;
+ int notVisibleCount = 75;
+ long removeCutoffTime = -1;
+
+ /**
+ * variables to process transform messages
+ */
+ boolean transformMsg = false;
+ UpdateTargets targets = null;
+ ArrayList blUsers = null;
+
+ /**
+ * The View for this render bin
+ */
+ View view = null;
+
+ ArrayList toBeAddedTextureResourceFreeList = new ArrayList(5);
+ ArrayList displayListResourceFreeList = new ArrayList(5);
+ boolean resourceToFree = false;
+
+ // a list of top level OrderedGroups
+ ArrayList orderedBins = new ArrayList(5);
+
+ // List of changed elements in the environment that needs to
+ // be reloaded
+ ArrayList changedLts = new ArrayList(5);
+ ArrayList changedFogs = new ArrayList(5);
+ ArrayList changedModelClips = new ArrayList(5);
+
+ // Flag to indicate whether the canvas should be marked
+ static int REEVALUATE_LIGHTS = 0x1;
+ static int REEVALUATE_FOG = 0x2;
+ static int REEVALUATE_MCLIP = 0x4;
+ static int REEVALUATE_ALL_ENV = REEVALUATE_LIGHTS | REEVALUATE_FOG | REEVALUATE_MCLIP;
+ int envDirty = 0;
+
+
+ boolean reEvaluateBg = true;
+
+ boolean reEvaluateClip = true;
+
+ boolean reEvaluateSortMode = false;
+
+
+ // list of renderMolecule
+ // RenderBin will not reused in two different universe, so it is
+ // safe to pass null in last parameters in new IndexedUnorderSet()
+ IndexedUnorderSet renderMoleculeList =
+ new IndexedUnorderSet(RenderMolecule.class,
+ RenderMolecule.RENDER_MOLECULE_LIST, null);
+
+ // List of renderAtoms that have a shared dlist (due to geo.refCount > 1)
+ UnorderList sharedDList = new UnorderList(5, RenderAtomListInfo.class);
+
+ ArrayList dirtyRenderMoleculeList = new ArrayList(5);
+
+
+ /**
+ * ArrayList of objects to be updated
+ */
+ ArrayList objUpdateList = new ArrayList(5);
+
+ ArrayList raLocaleVwcBoundsUpdateList = new ArrayList(5);
+
+ /**
+ * remove the bins first before adding them to new ones
+ */
+ IndexedUnorderSet removeRenderAtomInRMList =
+ new IndexedUnorderSet(RenderMolecule.class,
+ RenderMolecule.REMOVE_RENDER_ATOM_IN_RM_LIST, null);
+
+
+ /**
+ * list of affect OrderedGroups with childIndexOrder changed.
+ */
+ ArrayList ogCIOList = new ArrayList(5);
+
+ /**
+ * list of ordered bins from which orderedCollection are added/removed
+ */
+ ArrayList obList = new ArrayList(5);
+
+ /**
+ * Ordered Bin processing
+ */
+ ArrayList orderedBinsList = new ArrayList(5);
+ ArrayList toBeAddedBinList = new ArrayList(5);
+
+ /**
+ * arraylist of geometry that should be locked to ensure
+ * that the same snapshot of the geometry is rendered
+ * across all canvases
+ */
+ ArrayList lockGeometryList = new ArrayList(5);
+
+
+ /**
+ * arraylist of dlist that will be rebuilt
+ */
+ ArrayList dlistLockList = new ArrayList(5);
+
+ // Background node that contains geometry
+ BackgroundRetained geometryBackground = null;
+
+ // background geometry processing
+ LightBin bgOpaqueBin = null;
+ LightBin bgAddOpaqueBin = null;
+ ArrayList bgOrderedBins = new ArrayList(5);
+ TransparentRenderingInfo bgTransparentInfo;
+
+
+ // vworldToVpc for background geometry
+ Transform3D infVworldToVpc = new Transform3D();
+
+ // true if vpcToVworld has been modified
+ boolean vpcToVworldDirty = true;
+
+ // current active background
+ BackgroundRetained currentActiveBackground = new BackgroundRetained();
+
+ // Flag to indicate that alternate app is dirty
+ boolean altAppearanceDirty = true;
+
+
+ // List of node components that need special processing, due to
+ // extensions
+ ArrayList nodeComponentList = new ArrayList(5);
+
+
+ // List of node components ***for this frame*** that need special
+ // processing due to extension
+ ArrayList newNodeComponentList = new ArrayList(5);
+ ArrayList removeNodeComponentList = new ArrayList(5);
+ ArrayList dirtyNodeComponentList = new ArrayList(5);
+
+ ArrayList textureBinList = new ArrayList(5);
+
+ /**
+ * arraylist of refernce geometry that should be locked when transparency
+ * is on, so that we can make a mirror copy of the colors safely
+ */
+ ArrayList dirtyReferenceGeomList = new ArrayList(5);
+
+
+ /**
+ * used by geometry execute routines to determine if the
+ * alpha values can be zapped
+ */
+ boolean multiScreen = false;
+
+ // list of all Oriented RenderAtoms
+ ArrayList orientedRAs = new ArrayList(5);
+
+ // list of Oriented RenderAtoms whose orientedTransforms require update
+ ArrayList dirtyOrientedRAs = new ArrayList(5);
+
+ // Cached copy of dirty oriented RAs to be updated in MasterControl
+ ArrayList cachedDirtyOrientedRAs = null;
+
+ // list of offScreen message that
+ ArrayList offScreenMessage = new ArrayList(5);
+
+ // Vector used for locale translation
+ Vector3d localeTranslation = new Vector3d();
+
+ // Separate dlists that were added/removed in this snapshot
+ UnorderList addDlist = new UnorderList(5, RenderAtomListInfo.class);
+ UnorderList removeDlist = new UnorderList(5, RenderAtomListInfo.class);
+
+ // Separate dlists per rinfo that were added/removed in this snapshot
+ ArrayList addDlistPerRinfo = new ArrayList(5);
+ ArrayList removeDlistPerRinfo = new ArrayList(5);
+
+ Locale locale = null;
+
+ // Set to true if locale changes as part of UPDATE_VIEW message
+ boolean localeChanged = false;
+
+
+ // Cached copy to be used by all RenderMolecules
+ DisplayListRenderMethod dlistRenderMethod = null;
+
+ // Need to query BHTree again with visibility policy change
+ boolean reactivateView = false;
+
+ /**
+ * A flag indicates that the cached visible GeometryAtoms for this RenderBin might
+ * be invalid.
+ */
+ private boolean visGAIsDirty = false;
+
+ /**
+ * A flag indicates that a visibility query to the GeometryStructure is needed.
+ */
+ private boolean visQuery = false;
+
+ // Temporary dirtylist
+ ArrayList dirtyList = new ArrayList(5);
+
+ // Transaprency sort mode
+ int transpSortMode = View.TRANSPARENCY_SORT_NONE;
+ int cachedTranspSortMode = View.TRANSPARENCY_SORT_NONE;
+
+
+ // Temporary dirtylist
+ ArrayList dirtyDepthSortRenderAtom = new ArrayList(5);
+ int numDirtyTinfo = 0;
+
+ // Eye position in vworld
+ Point3d eyeInVworld = new Point3d();
+ // Number of RenderAtomListInfo in the depthSortedList
+ int nElements = 0;
+
+
+
+ /**
+ * Constructs a new RenderBin
+ */
+ RenderBin(VirtualUniverse u, View v) {
+ super(u, J3dThread.UPDATE_RENDER);
+ vworldToVpc.setIdentity();
+ universe = u;
+ view = v;
+ transpSortMode = v.transparencySortingPolicy;
+ cachedTranspSortMode = v.transparencySortingPolicy;
+ maxLights = VirtualUniverse.mc.maxLights;
+ ViewPlatform vp = view.getViewPlatform();
+ if (vp != null) {
+ locale = ((ViewPlatformRetained) (vp.retained)).locale;
+ }
+ dlistRenderMethod = (DisplayListRenderMethod)
+ VirtualUniverse.mc.getDisplayListRenderMethod();
+ }
+
+ /**
+ * updateObject
+ */
+ public void updateObject() {
+ int i, j, k;
+ RenderMolecule rm;
+ RenderAtomListInfo ra;
+ LightBin tmp;
+ float radius;
+ BackgroundRetained bg;
+ ObjectUpdate ob;
+ OrderedBin orderBin;
+ TextureRetained tex;
+ Integer texIdObj;
+ int size;
+ DetailTextureImage dtex;
+
+ // System.out.println("dirtyRenderMoleculeList.size = "+dirtyRenderMoleculeList.size());
+ // System.out.println("reEvaluateBg = "+reEvaluateBg);
+ // System.out.println("reEvaluateClip = "+reEvaluateClip);
+ // System.out.println("<========+End All Cached Values===========>");
+ // Add the new lightBins that have been created
+ // System.out.println("objUpdateList.size = "+objUpdateList.size());
+ // System.out.println("addOpaqueBin = "+addOpaqueBin);
+ // System.out.println("opaqueBin = "+opaqueBin);
+
+ // List of renderMolecule from which renderAtoms have been removed
+ size = removeRenderAtomInRMList.size();
+ if (size > 0) {
+ RenderMolecule[] rmArr = (RenderMolecule[])
+ removeRenderAtomInRMList.toArray(false);
+ for (i=0 ; i<size; i++) {
+ rmArr[i].updateRemoveRenderAtoms();
+ }
+ }
+
+ // Add any OGs that need to be added to this frame
+ // List of Ordered Groups that have been removed
+
+ size = obList.size();
+ if ( size > 0) {
+ for (i = 0 ; i < size; i++) {
+ orderBin = (OrderedBin)obList.get(i);
+ orderBin.addRemoveOrderedCollection();
+ }
+ }
+
+ size = ogCIOList.size();
+ if(size > 0) {
+ J3dMessage m;
+ for(i=0; i<size; i++) {
+ m = (J3dMessage) ogCIOList.get(i);
+
+ switch(m.type) {
+ case J3dMessage.ORDERED_GROUP_TABLE_CHANGED:
+ OrderedGroupRetained og = (OrderedGroupRetained)m.args[3];
+ if(og != null) {
+ og.childIndexOrder = ((int[])m.args[4]);
+ }
+ break;
+
+
+ case J3dMessage.ORDERED_GROUP_INSERTED:
+ case J3dMessage.ORDERED_GROUP_REMOVED:
+ if(m.args[3] != null) {
+ Object[] ogArr = (Object[]) m.args[3];
+ Object[] ogTableArr = (Object[]) m.args[4];
+ for(j=0; j<ogArr.length; j++) {
+ if(ogArr[j] != null) {
+ ((OrderedGroupRetained)ogArr[j]).childIndexOrder =
+ ((int[])ogTableArr[j]);
+ }
+ }
+ }
+
+ break;
+ }
+ m.decRefcount();
+ }
+ }
+
+
+ if (addOpaqueBin != null) {
+
+ if (opaqueBin != null) {
+ tmp = opaqueBin;
+ while (tmp.next != null) {
+ tmp = tmp.next;
+ }
+ addOpaqueBin.prev = tmp;
+ tmp.next = addOpaqueBin;
+ }
+ else {
+ opaqueBin = addOpaqueBin;
+ }
+ }
+
+ if (bgAddOpaqueBin != null) {
+ if (bgOpaqueBin != null) {
+ tmp = bgOpaqueBin;
+ while (tmp.next != null) {
+ tmp = tmp.next;
+ }
+ bgAddOpaqueBin.prev = tmp;
+ tmp.next = bgAddOpaqueBin;
+ }
+ else {
+ bgOpaqueBin = bgAddOpaqueBin;
+ }
+ }
+
+ size = orderedBinsList.size();
+ if (size > 0 ) {
+
+ for (i = 0; i < size; i++) {
+ ArrayList obs= (ArrayList) orderedBinsList.get(i);
+ ArrayList list = (ArrayList) toBeAddedBinList.get(i);
+
+ int lSize = list.size();
+ for (j = 0; j < lSize; j++) {
+ obs.add(list.get(j));
+ }
+ }
+ }
+
+
+ size = raLocaleVwcBoundsUpdateList.size();
+ if ( size > 0) {
+ RenderAtom renderAtom;
+ for (i = 0; i < size; i++) {
+ renderAtom = (RenderAtom)raLocaleVwcBoundsUpdateList.get(i);
+ renderAtom.updateLocaleVwcBounds();
+ }
+ }
+
+ if ((size = aBinUpdateList.size()) > 0) {
+ for (i = 0; i < size; i++) {
+ AttributeBin abin = (AttributeBin)aBinUpdateList.get(i);
+ abin.updateNodeComponent();
+
+ }
+ }
+
+
+ // Update the sole user TextureBins.
+ if (tbUpdateList.size() > 0) {
+ TextureBin tb;
+ size = tbUpdateList.size();
+ for (i = 0; i < size; i++) {
+ tb = (TextureBin) tbUpdateList.get(i);
+ tb.updateNodeComponent();
+ }
+
+ // do another pass to re-sort TextureBin based on the
+ // texture in the first texture unit state
+ for (i = 0; i < size; i++) {
+ tb = (TextureBin) tbUpdateList.get(i);
+ // Bug Id : 4701430 - Have to be sure tb.attributeBin is
+ // not equal to null. This is a temporary fix for j3d1.3.
+ if (((tb.tbFlag & TextureBin.RESORT) != 0) &&
+ (tb.attributeBin != null)) {
+
+ tb.attributeBin.reInsertTextureBin(tb);
+ tb.tbFlag &= ~TextureBin.RESORT;
+ }
+ }
+ }
+
+
+ // Update the soleUser node components first
+ // This way material equivalence during insertion
+ // of new RMs is based on the updated ones
+ if ((size = rmUpdateList.size()) > 0) {
+ for (i = 0; i < size; i++) {
+ rm = (RenderMolecule)rmUpdateList.get(i);
+
+ boolean changeLists = rm.updateNodeComponent();
+ // If an existing rm went from opaque to transparent or vice-versa
+ // and has not been removed, then switch the RM
+ if (changeLists && rm.textureBin != null) {
+ rm.textureBin.changeLists(rm);
+ }
+ }
+ for (i = 0; i < size; i++) {
+ rm = (RenderMolecule)rmUpdateList.get(i);
+ rm.reEvaluateEquivalence();
+ }
+ }
+
+
+
+ size = objUpdateList.size();
+ if ( size > 0) {
+ for (i = 0; i < size; i++) {
+ ob = (ObjectUpdate)objUpdateList.get(i);
+ ob.updateObject();
+ }
+ }
+
+ size = dirtyReferenceGeomList.size();
+ if ( size > 0) {
+ GeometryArrayRetained geo;
+ Canvas3D canvases[] = view.getCanvases();
+
+ for (i = 0; i < size; i++) {
+ geo = (GeometryArrayRetained) dirtyReferenceGeomList.get(i);
+ // Evaluate the nodeComponentList for all the canvases
+ geo.geomLock.getLock();
+ j = 0;
+ // Do the setup only once{if necessary} for each geometry
+ boolean found = false;
+ while(j < canvases.length && !found) {
+ if ((canvases[j].extensionsSupported & Canvas3D.SUN_GLOBAL_ALPHA) == 0) {
+ if ((geo.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+ geo.setupMirrorInterleavedColorPointer(true);
+ found = true;
+ }
+ else {
+ geo.setupMirrorColorPointer((geo.vertexType & GeometryArrayRetained.COLOR_DEFINED),true);
+ found = true;
+ }
+ }
+ j++;
+ }
+ geo.geomLock.unLock();
+
+ }
+
+ }
+
+ if (reEvaluateBg) {
+ setBackground(currentActiveBackground);
+ }
+
+ size = textureBinList.size();
+//System.out.println("textureBinList.size= " + size);
+ if (size > 0) {
+ Canvas3D canvasList[][] = view.getCanvasList(false);
+ Canvas3D cv;
+ boolean useSharedCtx = false;
+ TextureRetained texture;
+
+ // do a quick check to see if there is any canvas using
+ // shared context
+ for (j = 0; j < canvasList.length && !useSharedCtx; j++) {
+ cv = canvasList[j][0];
+ if (cv.useSharedCtx) {
+ useSharedCtx = true;
+ }
+ }
+
+ for (int m = 0; m <size; m++) {
+ k = 0;
+ TextureBin tb = (TextureBin) textureBinList.get(m);
+ tb.tbFlag |= TextureBin.ON_RENDER_BIN_LIST;
+
+ if (tb.texUnitState == null)
+ continue;
+
+ for (i = 0; i < tb.texUnitState.length; i++) {
+ if (tb.texUnitState[i] != null &&
+ tb.texUnitState[i].texture != null) {
+
+ texture = tb.texUnitState[i].texture;
+
+ // for all the textures in this texture bin list that
+ // need to be reloaded, add the textures to the
+ // corresponding resource reload list if the
+ // resource uses shared context,
+ // so that the texture can be reloaded up front, and
+ // we don't need to do make context current for
+ // each texture reload. Make Context current isn't
+ // cheap.
+
+ if (useSharedCtx) {
+ synchronized (texture.resourceLock) {
+ for (j = 0; j < canvasList.length; j++) {
+ cv = canvasList[j][0];
+ if (cv.useSharedCtx &&
+ cv.screen.renderer != null &&
+ ((cv.screen.renderer.rendererBit &
+ (texture.resourceCreationMask |
+ texture.resourceInReloadList)) == 0)) {
+
+ cv.screen.renderer.textureReloadList.add(
+ texture);
+
+ texture.resourceInReloadList |=
+ cv.screen.renderer.rendererBit;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ size = newNodeComponentList.size();
+ if ( size > 0) {
+//System.out.println("newNodeComponentlist.size= " + size);
+ Canvas3D canvases[] = view.getCanvases();
+ for (i = 0; i < size; i++) {
+ // Evaluate the nodeComponentList for all the canvases
+ ImageComponentRetained nc = (ImageComponentRetained)newNodeComponentList.get(i);
+ // Only evalaute extension for by-ref Images
+ if (nc.isByReference()) {
+ nc.geomLock.getLock();
+ for (j = 0; j <canvases.length; j++) {
+ // If the context is null, then the extension
+ // will be evaluated during context creation in
+ // the renderer
+ if (canvases[j].ctx != 0) {
+ nc.evaluateExtensions(canvases[j].extensionsSupported);
+ }
+ }
+ nc.geomLock.unLock();
+ }
+ nodeComponentList.add(nc);
+ }
+ }
+
+ size = removeNodeComponentList.size();
+ if ( size > 0) {
+ for (i = 0; i < size; i++) {
+ nodeComponentList.remove(removeNodeComponentList.get(i));
+ }
+ }
+
+
+ // reevaluate dirty node component
+ size = dirtyNodeComponentList.size();
+ if (size > 0) {
+ Canvas3D canvases[] = view.getCanvases();
+ for (i = 0; i < size; i++) {
+ // Evaluate the nodeComponentList for all the canvases
+ ImageComponentRetained nc =
+ (ImageComponentRetained)dirtyNodeComponentList.get(i);
+ // Only evalaute extension for by-ref Images
+ if (nc.isByReference()) {
+ nc.geomLock.getLock();
+ for (j = 0; j <canvases.length; j++) {
+ // If the context is null, then the extension
+ // will be evaluated during context creation in
+ // the renderer
+ if (canvases[j].ctx != 0) {
+ nc.evaluateExtensions(
+ canvases[j].extensionsSupported);
+ }
+ }
+ nc.geomLock.unLock();
+ }
+ }
+ }
+
+
+ if (reEvaluateClip) {
+ double[] retVal = null;
+ if ((retVal = universe.renderingEnvironmentStructure.backClipDistanceInVworld(vpSchedSphereInVworld, view)) != null) {
+ backClipDistanceInVworld = retVal[0];
+ backClipActive = true;
+ }
+ else {
+ backClipActive = false;
+ }
+ view.vDirtyMask |= View.CLIP_DIRTY;
+ }
+
+ multiScreen = ((view.getScreens()).length > 1);
+
+ // renderBin is ready now, so send the offScreen message
+ size = offScreenMessage.size();
+ if ( size > 0) {
+ J3dMessage m;
+ for (i=size-1; i>=0; i--) {
+ m = (J3dMessage) offScreenMessage.get(i);
+ m.threads = J3dThread.RENDER_THREAD;
+ ((Canvas3D)m.args[0]).screen.renderer.rendererStructure.addMessage(m);
+
+ // the above call will increment the reference count again
+ m.decRefcount();
+ }
+ }
+
+ // called from renderBin when there are dirtyOrientedRAs
+ // This routin cache the dirtyOrintedRAs to be updated
+ // by mastercontrol
+ if (dirtyOrientedRAs.size() > 0) {
+ // Keep a copy to be handled by mastercontrol
+ cachedDirtyOrientedRAs = (ArrayList)dirtyOrientedRAs.clone();
+ }
+ boolean sortAll = false;
+ if (reEvaluateSortMode && transpSortMode != cachedTranspSortMode) {
+ convertTransparentRenderingStruct(transpSortMode, cachedTranspSortMode);
+ transpSortMode = cachedTranspSortMode;
+ if (transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) {
+ if (transparentInfo != null){
+ sortAll = true;
+ }
+ }
+ }
+
+ if (vpcToVworldDirty) {
+ vworldToVpc.invert(vpcToVworld);
+ // Have the send down the lights, so set the canvas
+ // lightbin to null
+ Canvas3D canvases[] = view.getCanvases();
+ for (i = 0; i < canvases.length; i++) {
+ canvases[i].lightBin = null;
+ }
+ if (canvases.length > 0) {
+ Transform3D xform;
+ canvases[0].getCenterEyeInImagePlate(eyeInVworld);
+ // xform is imagePlateToLocal
+ xform = canvases[0].canvasViewCache.getImagePlateToVworld();
+ xform.transform(eyeInVworld);
+ }
+ if (transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY && transparentInfo != null) {
+ // System.out.println("sortAll 1");
+ sortAll = true;
+ }
+ }
+ size = dirtyDepthSortRenderAtom.size();
+
+
+ if (sortAll || size > 0) {
+ int tsize = allTransparentObjects.size();
+
+ double zVal;
+ for (i = 0; i < tsize; i++) {
+ RenderAtom renderAtom = (RenderAtom)allTransparentObjects.get(i);
+ for (k = 0; k < renderAtom.rListInfo.length; k++) {
+ if (renderAtom.rListInfo[k].geometry() == null)
+ continue;
+ zVal = renderAtom.geometryAtom.centroid[k].distanceSquared(eyeInVworld);
+ renderAtom.parentTInfo[k].zVal = zVal;
+
+ }
+ }
+
+ // Check to see if a majority of the transparent Objects have changed
+ // If less than 66% of all transparentStructs are dirty
+ // then, remove and insert, otherwise resort everything
+
+
+ if (size > 0 && 1.5f * numDirtyTinfo > nElements) {
+ // System.out.println("sortAll 3, size = "+size);
+ sortAll = true;
+ }
+
+ if (size > 0) {
+ TransparentRenderingInfo dirtyList = null, rList;
+ for (i = 0; i < size; i++) {
+ RenderAtom renderAtom = (RenderAtom)dirtyDepthSortRenderAtom.get(i);
+ if (!renderAtom.inRenderBin())
+ continue;
+ renderAtom.dirtyMask &= ~RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST;
+ if (!sortAll) {
+ dirtyList = collectDirtyTRInfo(dirtyList, renderAtom);
+ }
+ }
+
+ if (dirtyList != null) {
+ // System.out.println("====> sort Some");
+ dirtyList = depthSortAll(dirtyList);
+ // Now merge the newly sorted list with the old one
+ transparentInfo = mergeDepthSort(transparentInfo, dirtyList);
+ }
+ }
+ // Sort all the transparent renderAtoms
+ if (sortAll) {
+ transparentInfo = depthSortAll(transparentInfo);
+ }
+ }
+
+ if (addDlist.size() > 0 || removeDlist.size() > 0) {
+ Canvas3D canvasList[][] = view.getCanvasList(false);
+ Canvas3D cv;
+ ArrayList rlist = new ArrayList(5);
+
+ for (i = 0; i < canvasList.length; i++) {
+ cv = canvasList[i][0];
+ if (cv.useSharedCtx) {
+ // Do this only once per renderer for this view
+ if (!rlist.contains(cv.screen.renderer)) {
+ rlist.add(cv.screen.renderer);
+ updateDlistRendererResource(cv.screen.renderer);
+ }
+ } else {
+ updateDlistCanvasResource(canvasList[i]);
+ }
+ }
+
+ }
+
+
+
+ if (dirtyRenderMoleculeList.size() > 0 ||
+ addDlistPerRinfo.size() > 0 ||
+ removeDlistPerRinfo.size() > 0 ||
+ displayListResourceFreeList.size() > 0 ||
+ toBeAddedTextureResourceFreeList.size() > 0 ) {
+
+ Canvas3D canvasList[][] = view.getCanvasList(false);
+ Canvas3D cv;
+
+ for (i = 0; i < canvasList.length; i++) {
+ cv = canvasList[i][0];
+ if (cv.useSharedCtx && (cv.screen.renderer != null)) {
+ updateRendererResource(cv.screen.renderer);
+ } else {
+ updateCanvasResource(canvasList[i]);
+ }
+ }
+
+ Integer id;
+ size = displayListResourceFreeList.size();
+ for (i = 0; i < size; i++) {
+ id = (Integer)displayListResourceFreeList.get(i);
+ VirtualUniverse.mc.freeDisplayListId(id);
+ }
+
+ // lock list of dlist
+ // TODO: Instead of copying could we keep 2 arrays
+ // and just toggle?
+ size = dirtyRenderMoleculeList.size();
+ for (i = 0; i < size; i++) {
+ rm = (RenderMolecule)dirtyRenderMoleculeList.get(i);
+ rm.onUpdateList = 0;
+ ra = rm.primaryRenderAtomList;
+ while (ra != null) {
+ dlistLockList.add(ra.geometry());
+ ra = ra.next;
+ }
+ }
+ size = addDlistPerRinfo.size();
+ for (i = 0; i < size; i++) {
+ ra = (RenderAtomListInfo)addDlistPerRinfo.get(i);
+ if (ra.geometry() != null) {
+ dlistLockList.add(ra.geometry());
+ }
+ }
+
+ }
+
+ clearAllUpdateObjectState();
+ /*
+ if (opaqueBin != null) {
+ System.out.println(this + "***** Begin Dumping OpaqueBin *****");
+ dumpBin(opaqueBin);
+ System.out.println("***** End Dumping OpaqueBin *****");
+ }
+ */
+
+ }
+
+
+ void updateDlistRendererResource(Renderer rdr) {
+ int i, value = 0;
+ int size = 0;
+ RenderAtomListInfo arr[];
+ RenderAtomListInfo ra;
+
+ if (rdr == null) {
+ return;
+ }
+
+ if ((size = addDlist.size()) > 0) {
+ arr = (RenderAtomListInfo []) addDlist.toArray(false);
+ for (i = 0; i < size; i++) {
+ ra = arr[i];
+ GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry();
+
+ // First time thru this renderer or the context that
+ // it is built for no longer matches the context
+ // used in the renderer, create a dlist
+ sharedDList.add(ra);
+ geo.incrDlistRefCount(rdr.rendererBit);
+
+ if (((geo.resourceCreationMask & rdr.rendererBit) == 0) ||
+ (geo.getDlistTimeStamp(rdr.rendererBit) !=
+ rdr.sharedCtxTimeStamp)) {
+ geo.resourceCreationMask |= rdr.rendererBit;
+ dirtyList.add(ra);
+ }
+ }
+ }
+
+ if ((size = removeDlist.size()) > 0) {
+ arr = (RenderAtomListInfo []) removeDlist.toArray(false);
+ for (i = 0; i < size; i++) {
+ ra = arr[i];
+ sharedDList.remove(ra);
+
+ GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry();
+ value = geo.decrDlistRefCount(rdr.rendererBit);
+ // System.out.println("========> geo.refcount = "+geo.refCount);
+ // add this geometry's dlist to be freed
+ if (value == 0) {
+ rdr.displayListResourceFreeList.add(geo.dlistObj);
+ geo.resourceCreationMask &= ~rdr.rendererBit;
+ // All Dlist on all renderer have been freed, then return dlistID
+ if (geo.resourceCreationMask == 0) {
+ geo.freeDlistId();
+ }
+ }
+ }
+ }
+ if ((size = dirtyList.size()) > 0) {
+ for (i = 0; i < size; i++) {
+ ra = (RenderAtomListInfo)dirtyList.get(i);
+ GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry();
+ if ( (geo.resourceCreationMask & rdr.rendererBit) != 0) {
+ rdr.dirtyRenderAtomList.add(ra);
+ }
+ }
+ rdr.dirtyDisplayList = true;
+ dirtyList.clear();
+ }
+ }
+
+ void updateDlistCanvasResource(Canvas3D[] canvases) {
+ int i, j, value;
+ Canvas3D cv;
+ int size = 0;
+ RenderAtomListInfo arr[];
+ RenderAtomListInfo ra;
+
+ // Add the newly added dlist to the sharedList
+ if ((size = addDlist.size()) > 0) {
+ arr = (RenderAtomListInfo []) addDlist.toArray(false);
+ for (i = 0; i <size; i++) {
+ sharedDList.add(arr[i]);
+ }
+ }
+
+ // Remove the newly removed dlist from the sharedList
+ if ((size = removeDlist.size()) > 0) {
+ arr = (RenderAtomListInfo []) removeDlist.toArray(false);
+ for (i = 0; i < size; i++) {
+ sharedDList.remove(arr[i]);
+ }
+ }
+
+ // add to the dirty list per canvas
+ for (j = 0; j < canvases.length; j++) {
+ cv = canvases[j];
+
+ if ((size = addDlist.size()) > 0) {
+ arr = (RenderAtomListInfo []) addDlist.toArray(false);
+ for (i = 0; i <size; i++) {
+ ra = arr[i];
+ GeometryArrayRetained geo = (GeometryArrayRetained) ra.geometry();
+
+ geo.incrDlistRefCount(cv.canvasBit);
+ if ((cv.ctx != 0) &&
+ ((geo.resourceCreationMask & cv.canvasBit) == 0) ||
+ (geo.getDlistTimeStamp(cv.canvasBit) !=
+ cv.ctxTimeStamp)) {
+ geo.resourceCreationMask |= cv.canvasBit;
+ dirtyList.add(ra);
+ }
+ }
+ }
+ if ((size = removeDlist.size()) > 0) {
+ arr = (RenderAtomListInfo []) removeDlist.toArray(false);
+ for (i = 0; i < size; i++) {
+ GeometryArrayRetained geo =
+ (GeometryArrayRetained) arr[i].geometry();
+
+ value = geo.decrDlistRefCount(canvases[j].canvasBit);
+ // add this geometry's dlist to be freed
+ if (value == 0) {
+ if (cv.ctx != 0) {
+ canvases[j].displayListResourceFreeList.add(geo.dlistObj);
+ }
+ geo.resourceCreationMask &= ~canvases[j].canvasBit;
+ // All Dlist on all canvases have been freed, then return dlistID
+ if (geo.resourceCreationMask == 0)
+ geo.freeDlistId();
+ }
+ }
+ }
+ if ((size = dirtyList.size()) > 0) {
+ for (i = 0; i <size; i++) {
+ ra = (RenderAtomListInfo)dirtyList.get(i);
+ GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry();
+ if ((geo.resourceCreationMask & cv.canvasBit) != 0) {
+ cv.dirtyRenderAtomList.add(ra);
+ }
+ }
+ cv.dirtyDisplayList = true;
+ dirtyList.clear();
+ }
+ }
+
+
+ }
+
+ void clearAllUpdateObjectState() {
+ localeChanged = false;
+ obList.clear();
+ rmUpdateList.clear();
+ ogCIOList.clear();
+ aBinUpdateList.clear();
+ tbUpdateList.clear();
+ removeRenderAtomInRMList.clear();
+ addOpaqueBin = null;
+ bgAddOpaqueBin = null;
+ orderedBinsList.clear();
+ toBeAddedBinList.clear();
+ objUpdateList.clear();
+ raLocaleVwcBoundsUpdateList.clear();
+ displayListResourceFreeList.clear();
+ toBeAddedTextureResourceFreeList.clear();
+ dirtyRenderMoleculeList.clear();
+ dirtyReferenceGeomList.clear();
+ reEvaluateBg = false;
+ textureBinList.clear();
+ newNodeComponentList.clear();
+ removeNodeComponentList.clear();
+ dirtyNodeComponentList.clear();
+ reEvaluateClip = false;
+ vpcToVworldDirty = false;
+ offScreenMessage.clear();
+ addDlist.clear();
+ removeDlist.clear();
+ addDlistPerRinfo.clear();
+ removeDlistPerRinfo.clear();
+ clearDirtyOrientedRAs();
+ reEvaluateSortMode = false;
+ dirtyDepthSortRenderAtom.clear();
+ numDirtyTinfo = 0;
+ }
+
+ void updateRendererResource(Renderer rdr) {
+ RenderMolecule rm;
+ TextureRetained tex;
+ Integer texIdObj;
+ DetailTextureImage dtex;
+
+ if (rdr == null)
+ return;
+
+ // Take care of display lists per Rinfo that should be rebuilt
+ int size = addDlistPerRinfo.size();
+
+ if (size > 0) {
+ for (int j = 0; j < size; j++) {
+ RenderAtomListInfo rinfo = (RenderAtomListInfo)addDlistPerRinfo.get(j);
+ if (rinfo.renderAtom.inRenderBin()) {
+ Object[] obj = new Object[2];
+ obj[0] = rinfo;
+ obj[1] = rinfo.renderAtom.renderMolecule;
+ rdr.dirtyDlistPerRinfoList.add(obj);
+ }
+ }
+ rdr.dirtyDisplayList = true;
+ }
+
+
+ // Take care of display lists that should be rebuilt
+ size = dirtyRenderMoleculeList.size();
+ if (size > 0) {
+ for (int j = 0; j < size; j++) {
+ rm =(RenderMolecule)dirtyRenderMoleculeList.get(j);
+ rdr.dirtyRenderMoleculeList.add(rm);
+ }
+ rdr.dirtyDisplayList = true;
+ }
+
+ // Take care of texture that should be freed
+ size = toBeAddedTextureResourceFreeList.size();
+ int id;
+ for (int j=0; j < size; j++) {
+ tex = (TextureRetained)toBeAddedTextureResourceFreeList.get(j);
+ id = tex.objectId;
+ if ((id >= rdr.textureIDResourceTable.size()) ||
+ (id <= 0) ||
+ (rdr.textureIDResourceTable.get(id) != tex)) {
+ // tex.objectId may change by another Renderer thread,
+ // need find original texID from searching
+ // rdr.textureIdResourceTable
+ id = rdr.textureIDResourceTable.indexOf(tex);
+
+ if (id <= 0) {
+ continue;
+ }
+ }
+
+ // Since multiple renderBins (in the same screen)
+ // can share a texture object, make sure that
+ // we are not duplicating what has been added
+ // by a different renderBin in the same screen
+ if ((tex.resourceCreationMask & rdr.rendererBit) != 0) {
+ texIdObj = new Integer(id);
+ if (!rdr.textureIdResourceFreeList.contains(texIdObj)) {
+ rdr.textureIdResourceFreeList.add(texIdObj);
+ tex.resourceCreationMask &= ~rdr.rendererBit;
+ }
+ if (tex instanceof Texture2DRetained) {
+ dtex = ((Texture2DRetained) tex).detailTexture;
+ if ((dtex != null) &&
+ ((dtex.resourceCreationMask[tex.format] & rdr.rendererBit) != 0)) {
+ id = dtex.objectIds[tex.format];
+ if ((id >= rdr.textureIDResourceTable.size()) ||
+ (rdr.textureIDResourceTable.get(id) != dtex)) {
+ id = rdr.textureIDResourceTable.indexOf(dtex);
+ if (id <= 0) {
+ continue;
+ }
+ }
+ texIdObj = new Integer(id);
+ if (!rdr.textureIdResourceFreeList.contains(texIdObj)) {
+ rdr.textureIdResourceFreeList.add(texIdObj);
+ dtex.resourceCreationMask[tex.format] &= ~rdr.rendererBit;
+ }
+ }
+ }
+ }
+ }
+
+ // Take care of display list that should be freed
+ size = displayListResourceFreeList.size();
+ Integer displayListIDObj;
+
+ for (int j=0; j <size; j++) {
+ displayListIDObj = (Integer) displayListResourceFreeList.get(j);
+ // It doesn't harm to free the same ID twice, the
+ // underlying graphics library just ignore the second request
+ rdr.displayListResourceFreeList.add(displayListIDObj);
+ }
+ // Take care of display list that should be freed
+ size = removeDlistPerRinfo.size();
+ for (int j=0; j < size; j++) {
+ RenderAtomListInfo ra = (RenderAtomListInfo)removeDlistPerRinfo.get(j);
+ rdr.displayListResourceFreeList.add(new Integer(ra.renderAtom.dlistIds[ra.index]));
+ ra.groupType = 0;
+ ra.renderAtom.dlistIds[ra.index] = -1;
+ }
+ }
+
+ void updateCanvasResource(Canvas3D[] canvases) {
+ int i, j;
+ RenderMolecule rm;
+ TextureRetained tex;
+ Integer texIdObj;
+ DetailTextureImage dtex;
+
+ // update dirtyRenderMoleculeList for each canvas
+ for (i = 0; i < canvases.length; i++) {
+ Canvas3D cv = canvases[i];
+
+ // Take care of display lists per Rinfo that should be rebuilt
+ int size = addDlistPerRinfo.size();
+ if (size > 0) {
+ for ( j = 0; j < size; j++) {
+ RenderAtomListInfo rinfo = (RenderAtomListInfo)addDlistPerRinfo.get(j);
+ if (rinfo.renderAtom.inRenderBin()) {
+ Object[] obj = new Object[2];
+ obj[0] = rinfo;
+ obj[1] = rinfo.renderAtom.renderMolecule;
+ cv.dirtyDlistPerRinfoList.add(obj);
+ }
+ }
+ cv.dirtyDisplayList = true;
+ }
+ // Take care of display lists that should be rebuilt
+ size = dirtyRenderMoleculeList.size();
+ if (size > 0) {
+ for (j = 0; j < size; j++) {
+ rm = (RenderMolecule)dirtyRenderMoleculeList.get(j);
+ cv.dirtyRenderMoleculeList.add(rm);
+ }
+ cv.dirtyDisplayList = true;
+ }
+ // Take care of texture that should be freed
+ size = toBeAddedTextureResourceFreeList.size();
+ int id;
+ for (j=0; j < size; j++) {
+ tex = (TextureRetained)toBeAddedTextureResourceFreeList.get(j);
+ id = tex.objectId;
+ if ((id >= cv.textureIDResourceTable.size()) ||
+ (id <= 0) ||
+ (cv.textureIDResourceTable.get(id) != tex)) {
+ // tex.objectId may change by another Renderer thread,
+ // need find original texID from searching
+ // rdr.textureIdResourceTable
+ id = cv.textureIDResourceTable.indexOf(tex);
+
+ if (id <= 0) {
+ continue;
+ }
+ }
+
+
+ if ((tex.resourceCreationMask & cv.canvasBit) != 0) {
+ texIdObj = new Integer(id);
+ cv.textureIdResourceFreeList.add(texIdObj);
+ tex.resourceCreationMask &= ~cv.canvasBit;
+ }
+ if (tex instanceof Texture2DRetained) {
+ dtex = ((Texture2DRetained) tex).detailTexture;
+ if ((dtex != null) &&
+ ((dtex.resourceCreationMask[tex.format] & cv.canvasBit) != 0)) {
+ id = dtex.objectIds[tex.format];
+ if ((id >= cv.textureIDResourceTable.size()) ||
+ (cv.textureIDResourceTable.get(id) != dtex)) {
+ id = cv.textureIDResourceTable.indexOf(dtex);
+ if (id <= 0) {
+ continue;
+ }
+ }
+ texIdObj = new Integer(id);
+ if (cv.textureIdResourceFreeList.contains(texIdObj)) {
+ cv.textureIdResourceFreeList.add(texIdObj);
+ dtex.resourceCreationMask[tex.format] &= ~cv.canvasBit;
+ }
+ }
+ }
+ }
+ // Take care of display list that should be freed
+ size = displayListResourceFreeList.size();
+ for (j=0; j < size; j++) {
+ cv.displayListResourceFreeList.add(displayListResourceFreeList.get(j));
+ }
+ // Take care of display list that should be freed
+ size = removeDlistPerRinfo.size();
+ for (j=0; j < size; j++) {
+ RenderAtomListInfo ra = (RenderAtomListInfo)removeDlistPerRinfo.get(j);
+ cv.displayListResourceFreeList.add(new Integer(ra.renderAtom.dlistIds[ra.index]));
+ ra.groupType = 0;
+ ra.renderAtom.dlistIds[ra.index] = -1;
+
+ }
+ }
+
+ }
+
+ void processMessages(long referenceTime) {
+ int i,j, index;
+ Object[] nodes;
+ J3dMessage messages[], m;
+ int component;
+
+ messages = getMessages(referenceTime);
+ int nMsg = getNumMessage();
+
+ if (nMsg > 0) {
+ for (i=0; i < nMsg; i++) {
+ m = messages[i];
+ switch (m.type) {
+ case J3dMessage.INSERT_NODES:
+ insertNodes(m);
+ m.decRefcount();
+ break;
+ case J3dMessage.REMOVE_NODES:
+ removeNodes(m);
+ m.decRefcount();
+ break;
+ case J3dMessage.TRANSFORM_CHANGED:
+ transformMsg = true;
+ m.decRefcount();
+ break;
+ case J3dMessage.LIGHT_CHANGED:
+ // if none of the mirror lights are scoped to this view
+ // ignore this message
+ LightRetained[] mLts =(LightRetained[])m.args[3] ;
+ for (int k = 0; k < mLts.length; k++) {
+ if (universe.renderingEnvironmentStructure.isLightScopedToThisView(mLts[k], view)) {
+ lightMessageList.add(m);
+ break;
+ }
+
+ }
+ break;
+ case J3dMessage.SWITCH_CHANGED:
+ visGAIsDirty = true;
+ visQuery = true;
+ processSwitchChanged(m, referenceTime);
+ // may need to process dirty switched-on transform
+ if (universe.transformStructure.getLazyUpdate()) {
+ transformMsg = true;
+ }
+ m.decRefcount();
+ break;
+ case J3dMessage.BACKGROUND_CHANGED:
+ BackgroundRetained bg = (BackgroundRetained)m.args[0];
+ if (universe.renderingEnvironmentStructure.isBgScopedToThisView(bg, view))
+ reEvaluateBg = true;
+ m.decRefcount();
+ break;
+ case J3dMessage.CLIP_CHANGED:
+ ClipRetained c = (ClipRetained)m.args[0];
+ if (universe.renderingEnvironmentStructure.isClipScopedToThisView(c, view))
+ reEvaluateClip = true;
+ m.decRefcount();
+ break;
+ case J3dMessage.TRANSPARENCYATTRIBUTES_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained) m.args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])m.args[3];
+ RenderAtom ra = null;
+ int start = -1;
+
+ // Get the first ra that is visible
+ for (int k = 0; (k < gaArr.length && (start < 0)); k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = k;
+ }
+ }
+
+ if (start >= 0) {
+ boolean restructure = (nc.mirror.changedFrequent == 0 ||
+ ra.renderMolecule.definingTransparency != nc.mirror);
+ processRenderMoleculeNodeComponentChanged(m.args,
+ RenderMolecule.TRANSPARENCY_DIRTY,
+ start, restructure);
+ }
+ m.decRefcount();
+ break;
+ }
+ case J3dMessage.POLYGONATTRIBUTES_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained) m.args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])m.args[3];
+ RenderAtom ra = null;
+ int start = -1;
+
+ // Get the first ra that is visible
+ // Get the first ra that is visible
+ for (int k = 0; (k < gaArr.length && (start < 0)); k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = k;
+ }
+ }
+
+ if (start >= 0) {
+ boolean restructure = (nc.mirror.changedFrequent == 0 ||
+ ra.renderMolecule.definingPolygonAttributes != nc.mirror);
+ processRenderMoleculeNodeComponentChanged(m.args,
+ RenderMolecule.POLYGONATTRS_DIRTY,
+ start, restructure);
+ }
+ m.decRefcount();
+ break;
+ }
+ case J3dMessage.LINEATTRIBUTES_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained) m.args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])m.args[3];
+ RenderAtom ra = null;
+ int start = -1;
+
+ // Get the first ra that is visible
+ // Get the first ra that is visible
+ for (int k = 0; (k < gaArr.length && (start < 0)); k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = k;
+ }
+ }
+
+ if (start >= 0) {
+ boolean restructure = (nc.mirror.changedFrequent == 0 ||
+ ra.renderMolecule.definingLineAttributes != nc.mirror);
+ processRenderMoleculeNodeComponentChanged(m.args,
+ RenderMolecule.LINEATTRS_DIRTY,
+ start, restructure);
+ }
+ m.decRefcount();
+ break;
+ }
+ case J3dMessage.POINTATTRIBUTES_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained) m.args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])m.args[3];
+ RenderAtom ra = null;
+ int start = -1;
+ // Get the first ra that is visible
+ // Get the first ra that is visible
+ for (int k = 0; (k < gaArr.length && (start < 0)); k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = k;
+ }
+ }
+
+ if (start >= 0) {
+ boolean restructure = (nc.mirror.changedFrequent == 0 ||
+ ra.renderMolecule.definingPointAttributes != nc.mirror);
+
+ processRenderMoleculeNodeComponentChanged(m.args,
+ RenderMolecule.POINTATTRS_DIRTY,
+ start, restructure);
+ }
+ m.decRefcount();
+ break;
+ }
+ case J3dMessage.MATERIAL_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained) m.args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])m.args[3];
+ RenderAtom ra = null;
+ int start = -1;
+
+ // Get the first ra that is visible
+ // Get the first ra that is visible
+ for (int k = 0; (k < gaArr.length && (start < 0)); k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = k;
+ }
+ }
+
+ if (start >= 0) {
+ boolean restructure = (nc.mirror.changedFrequent == 0 ||
+ ra.renderMolecule.definingMaterial != nc.mirror);
+ processRenderMoleculeNodeComponentChanged(m.args,
+ RenderMolecule.MATERIAL_DIRTY,
+ start, restructure);
+ }
+ m.decRefcount();
+ break;
+ }
+ case J3dMessage.COLORINGATTRIBUTES_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained) m.args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])m.args[3];
+ RenderAtom ra = null;
+ int start = -1;
+
+ // Get the first ra that is visible
+ // Get the first ra that is visible
+ for (int k = 0; (k < gaArr.length && (start < 0)); k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = k;
+ }
+ }
+
+ if (start >= 0) {
+ boolean restructure = (nc.mirror.changedFrequent == 0 ||
+ ra.renderMolecule.definingColoringAttributes != nc.mirror);
+ processRenderMoleculeNodeComponentChanged(m.args,
+ RenderMolecule.COLORINGATTRS_DIRTY,
+ start, restructure);
+ }
+ m.decRefcount();
+ break;
+ }
+ case J3dMessage.TEXTUREATTRIBUTES_CHANGED:
+ processTextureAttributesChanged(
+ (NodeComponentRetained) m.args[0],
+ (GeometryAtom[])m.args[3]);
+ m.decRefcount();
+ break;
+ case J3dMessage.IMAGE_COMPONENT_CHANGED:
+ addDirtyNodeComponent((NodeComponentRetained)m.args[0]);
+ m.decRefcount();
+ break;
+ case J3dMessage.TEXTURE_UNIT_STATE_CHANGED:
+ processTextureUnitStateChanged(
+ (NodeComponentRetained) m.args[0],
+ (GeometryAtom[])m.args[3]);
+ m.decRefcount();
+ break;
+ case J3dMessage.TEXCOORDGENERATION_CHANGED:
+ processTexCoordGenerationChanged( (NodeComponentRetained) m.args[0],
+ (GeometryAtom[])m.args[3]);
+ m.decRefcount();
+ break;
+ case J3dMessage.TEXTURE_CHANGED:
+ // Texture is always in a sole user position
+ processTextureChanged((NodeComponentRetained) m.args[0],
+ (GeometryAtom[])m.args[3],
+ m.args);
+
+ m.decRefcount();
+ break;
+ case J3dMessage.RENDERINGATTRIBUTES_CHANGED:
+ processAttributeBinNodeComponentChanged(m.args);
+ component = ((Integer)m.args[1]).intValue();
+ if (component == RenderingAttributesRetained.VISIBLE) {
+ visGAIsDirty = true;
+ visQuery = true;
+ }
+ m.decRefcount();
+ break;
+ case J3dMessage.APPEARANCE_CHANGED:
+ processAppearanceChanged(m.args);
+ m.decRefcount();
+ break;
+ case J3dMessage.FOG_CHANGED:
+ FogRetained mfog = ((FogRetained)m.args[0]).mirrorFog;
+ if (universe.renderingEnvironmentStructure.isFogScopedToThisView(mfog, view)) {
+ processFogChanged(m.args);
+ }
+ m.decRefcount();
+ break;
+ case J3dMessage.ALTERNATEAPPEARANCE_CHANGED:
+ AlternateAppearanceRetained maltapp = ((AlternateAppearanceRetained)m.args[0]).mirrorAltApp;
+ if (universe.renderingEnvironmentStructure.isAltAppScopedToThisView(maltapp, view)) {
+ altAppearanceDirty = true;
+ }
+ m.decRefcount();
+ break;
+ case J3dMessage.MODELCLIP_CHANGED:
+ ModelClipRetained mc= ((ModelClipRetained)m.args[0]).mirrorModelClip;
+ if (universe.renderingEnvironmentStructure.isMclipScopedToThisView(mc, view)) {
+ processModelClipChanged(m.args);
+ }
+ m.decRefcount();
+ break;
+ case J3dMessage.BOUNDINGLEAF_CHANGED:
+ processBoundingLeafChanged(m.args,
+ referenceTime);
+ m.decRefcount();
+ break;
+ case J3dMessage.SHAPE3D_CHANGED:
+ processShapeChanged(m.args, referenceTime);
+ m.decRefcount();
+ break;
+ case J3dMessage.ORIENTEDSHAPE3D_CHANGED:
+ processOrientedShape3DChanged((Object[])m.args[0]);
+ m.decRefcount();
+ break;
+ case J3dMessage.MORPH_CHANGED:
+ processMorphChanged(m.args, referenceTime);
+ component = ((Integer)m.args[1]).intValue();
+ if ((component & MorphRetained.GEOMETRY_CHANGED) == 0) {
+ visGAIsDirty = true;
+ visQuery = true;
+ }
+ m.decRefcount();
+ break;
+ case J3dMessage.UPDATE_VIEW:
+ {
+ View v = (View)m.args[0];
+ ViewPlatform vp = v.getViewPlatform();
+ int comp = ((Integer)(m.args[2])).intValue();
+ int value = ((Integer)(m.args[3])).intValue();
+ if (comp == View.TRANSP_SORT_POLICY_CHANGED) {
+ if (value != transpSortMode) {
+ reEvaluateSortMode = true;
+ cachedTranspSortMode = value;
+ }
+ } else if (vp != null) {
+ if (value != transpSortMode) {
+ reEvaluateSortMode = true;
+ cachedTranspSortMode = value;
+ }
+ updateViewPlatform((ViewPlatformRetained)vp.retained,
+ ((Float)m.args[1]).floatValue());
+ visQuery = true;
+ // TODO : Handle view.visibilityPolicy changed.
+ if(((View.VISIBILITY_POLICY_DIRTY != 0) &&
+ (View.VISIBILITY_DRAW_ALL != view.viewCache.visibilityPolicy)) ||
+ locale != ((ViewPlatformRetained) (vp.retained)).locale) {
+
+ for (int n = (renderAtoms.size() - 1); n>=0 ; n--) {
+ removeARenderAtom((RenderAtom) renderAtoms.get(n));
+ }
+ renderAtoms.clear();
+ visGAIsDirty = true;
+ if (locale != ((ViewPlatformRetained) (vp.retained)).locale) {
+ locale = ((ViewPlatformRetained) (vp.retained)).locale;
+ localeChanged = true;
+ }
+ }
+ }
+ m.decRefcount();
+ }
+ break;
+ case J3dMessage.UPDATE_VIEWPLATFORM:
+ updateViewPlatform((ViewPlatformRetained) m.args[0],
+ ((Float)m.args[1]).floatValue());
+ m.decRefcount();
+ break;
+ case J3dMessage.TEXT3D_DATA_CHANGED:
+ processDataChanged((Object[])m.args[0],
+ (Object[])m.args[1],
+ referenceTime);
+ m.decRefcount();
+ break;
+ case J3dMessage.GEOMETRY_CHANGED:
+ processGeometryChanged(m.args);
+ visGAIsDirty = true;
+ visQuery = true;
+ m.decRefcount();
+ break;
+
+ case J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED:
+ case J3dMessage.REGION_BOUND_CHANGED:
+ processGeometryAtomsChanged((Object[])m.args[0]);
+ visGAIsDirty = true;
+ visQuery = true;
+ m.decRefcount();
+ break;
+ case J3dMessage.TEXT3D_TRANSFORM_CHANGED:
+ processText3DTransformChanged((Object[])m.args[0],
+ (Object[])m.args[1],
+ referenceTime);
+ visQuery = true;
+ m.decRefcount();
+ break;
+ case J3dMessage.ORDERED_GROUP_INSERTED:
+ processOrderedGroupInserted(m);
+ // Do not do decRefcount() here. We'll do it in updateObject().
+ ogCIOList.add(m);
+ break;
+ case J3dMessage.ORDERED_GROUP_REMOVED:
+ processOrderedGroupRemoved(m);
+ // Do not do decRefcount() here. We'll do it in updateObject().
+ ogCIOList.add(m);
+ break;
+ case J3dMessage.ORDERED_GROUP_TABLE_CHANGED:
+ // Do not do decRefcount() here. We'll do it in updateObject().
+ ogCIOList.add(m);
+ break;
+ case J3dMessage.RENDER_OFFSCREEN:
+ offScreenMessage.add(m);
+ break;
+ case J3dMessage.VIEWSPECIFICGROUP_CHANGED:
+ processViewSpecificGroupChanged(m);
+ visQuery = true;
+ m.decRefcount();
+ break;
+ default:
+ m.decRefcount();
+ }
+ }
+
+ if (transformMsg) {
+ processTransformChanged(referenceTime);
+ transformMsg = false;
+ }
+ if (lightMessageList.size() > 0) {
+ processLightChanged();
+ lightMessageList.clear();
+ }
+ VirtualUniverse.mc.addMirrorObject(this);
+
+ // clear the array to prevent memory leaks
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ if (reEvaluateBg) {
+ currentActiveBackground = universe.renderingEnvironmentStructure.
+ getApplicationBackground(vpSchedSphereInVworld, locale, view);
+ }
+
+
+ if (visQuery) {
+ GeometryAtom[] bgGeometryAtoms;
+ boolean allEnComp;
+
+ // computeViewFrustumBox in VisibilityStructure.
+ computeViewFrustumBBox(viewFrustumBBox);
+ // System.out.println("viewFrustumBBox = " + this);
+
+ ViewPlatform vp = view.getViewPlatform();
+ if (vp != null) {
+ allEnComp = universe.geometryStructure.
+ getVisibleBHTrees(this, bhTreesArrList, viewFrustumBBox,
+ locale, referenceTime,
+ visGAIsDirty || reactivateView || localeChanged ||
+ ((view.viewCache.vcDirtyMask &
+ View.VISIBILITY_POLICY_DIRTY) != 0),
+ view.viewCache.visibilityPolicy);
+
+ reactivateView = false;
+ // process background geometry atoms
+ if (currentActiveBackground != null &&
+ currentActiveBackground.geometryBranch != null) {
+ bgGeometryAtoms =
+ currentActiveBackground.getBackgroundGeometryAtoms();
+ if (bgGeometryAtoms != null) {
+ processBgGeometryAtoms(bgGeometryAtoms, referenceTime);
+ }
+ }
+
+ if(!allEnComp) {
+ // Increment the framecount for compaction ...
+ frameCount++;
+ if (frameCount > frameCountCutoff) {
+ frameCount = 0;
+ checkForCompaction();
+ }
+ else if (frameCount == notVisibleCount) {
+ removeCutoffTime = referenceTime;
+ }
+ }
+ }
+ // Reset dirty bits.
+ visGAIsDirty = false;
+ visQuery = false;
+
+ }
+ // Two environments are dirty
+ // If lights, fog or model clip have been added/removed, then
+ // reEvaluate RenderAtoms and mark the lightbin and
+ // env set dirty if applicable
+ if (envDirty == REEVALUATE_ALL_ENV || envDirty == 3 ||
+ envDirty > 4) {
+ reEvaluateEnv(changedLts, changedFogs, changedModelClips, true,
+ altAppearanceDirty);
+ }
+ else if (envDirty == 0 && altAppearanceDirty) {
+ reEvaluateAlternateAppearance();
+ }
+ else {
+ if ((envDirty & REEVALUATE_LIGHTS) != 0) {
+ reEvaluateLights(altAppearanceDirty);
+ }
+ else if ((envDirty & REEVALUATE_FOG) != 0)
+ reEvaluateFog(changedFogs, (changedFogs.size() > 0), altAppearanceDirty);
+ else if ((envDirty & REEVALUATE_MCLIP) != 0)
+ reEvaluateModelClip(changedModelClips, (changedModelClips.size() > 0), altAppearanceDirty);
+ }
+
+
+
+ // do any pre-update node component screening
+
+ if (updateCheckList.size() > 0) {
+ int size = updateCheckList.size();
+ NodeComponentUpdate bin;
+ for (int k = 0; k < size; k++) {
+ bin = (NodeComponentUpdate) updateCheckList.get(k);
+ bin.updateNodeComponentCheck();
+ }
+ updateCheckList.clear();
+ }
+
+
+ changedLts.clear();
+ changedFogs.clear();
+ changedModelClips.clear();
+ envDirty = 0;
+ altAppearanceDirty = false;
+
+ view.renderBinReady = true;
+
+ VirtualUniverse.mc.sendRunMessage(view,
+ J3dThread.RENDER_THREAD);
+ }
+
+
+ void processSwitchChanged(J3dMessage m, long refTime) {
+ int i;
+ UnorderList arrList;
+ int size;
+ Object[] nodes, nodesArr;
+ LeafRetained leaf;
+
+ RenderingEnvironmentStructure rdrEnvStr =
+ universe.renderingEnvironmentStructure;
+
+ UpdateTargets targets = (UpdateTargets)m.args[0];
+ arrList = targets.targetList[Targets.ENV_TARGETS];
+
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (int h=0; h<size; h++) {
+ nodes = (Object[])nodesArr[h];
+ for (i=0; i<nodes.length; i++) {
+
+ if (nodes[i] instanceof LightRetained &&
+ rdrEnvStr.isLightScopedToThisView(nodes[i], view)) {
+ envDirty |= REEVALUATE_LIGHTS;
+ } else if (nodes[i] instanceof FogRetained &&
+ rdrEnvStr.isFogScopedToThisView(nodes[i], view)) {
+ envDirty |= REEVALUATE_FOG;
+ } else if (nodes[i] instanceof ModelClipRetained &&
+ rdrEnvStr.isMclipScopedToThisView(nodes[i], view)) {
+ envDirty |= REEVALUATE_MCLIP;
+ } else if (nodes[i] instanceof BackgroundRetained &&
+ rdrEnvStr.isBgScopedToThisView(nodes[i], view)) {
+ reEvaluateBg = true;
+ } else if (nodes[i] instanceof ClipRetained &&
+ rdrEnvStr.isClipScopedToThisView(nodes[i], view)) {
+ reEvaluateClip = true;
+ } else if (nodes[i] instanceof AlternateAppearanceRetained &&
+ rdrEnvStr.isAltAppScopedToThisView(nodes[i], view)) {
+ altAppearanceDirty = true;
+ }
+ }
+ }
+ }
+
+ arrList = targets.targetList[Targets.BLN_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+ Object[] objArr = (Object[])m.args[1];
+ Object[] obj, users;
+ BoundingLeafRetained mbleaf;
+
+ for (int h=0; h<size; h++) {
+ nodes = (Object[])nodesArr[h];
+ obj = (Object[])objArr[h];
+ for (i=0; i<nodes.length; i++) {
+
+ users = (Object[])obj[i];
+ mbleaf = (BoundingLeafRetained)nodes[i];
+ for (int j = 0; j < users.length; j++) {
+
+ if (users[j] instanceof FogRetained &&
+ rdrEnvStr.isFogScopedToThisView(users[j], view)) {
+ envDirty |= REEVALUATE_FOG;
+ } else if (users[j] instanceof LightRetained &&
+ rdrEnvStr.isLightScopedToThisView(users[j], view)) {
+ envDirty |= REEVALUATE_LIGHTS;
+ } else if (users[j] instanceof ModelClipRetained &&
+ rdrEnvStr.isMclipScopedToThisView(users[j], view)) {
+ envDirty |= REEVALUATE_MCLIP;
+ } else if (users[j] instanceof
+ AlternateAppearanceRetained &&
+ rdrEnvStr.isAltAppScopedToThisView(users[j], view)) {
+ altAppearanceDirty = true;
+ } else if (users[j] instanceof BackgroundRetained &&
+ rdrEnvStr.isBgScopedToThisView(users[j], view)) {
+ reEvaluateBg = true;
+ } else if (users[j] instanceof ClipRetained &&
+ rdrEnvStr.isClipScopedToThisView(users[j], view)) {
+ reEvaluateClip = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Transparency/Line/point/Poly attributes is different from other renderMolecule
+ * attributes since the renderatom could move from opaque bin
+ * to transparent bin
+ */
+ void processPossibleBinChanged(Object[] args) {
+ int i;
+ GeometryAtom[] gaArr = (GeometryAtom[])args[3];
+ for (i = 0; i < gaArr.length; i++) {
+ RenderAtom ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ // If renderAtom is in orderedGroup or with this
+ // change continues to be in the same higher level
+ // lightBin(transparent or opaque) then reInsert at
+ // the textureBin level, other Insert at the lightBin
+ // level
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ }
+ }
+
+
+ /**
+ * This processes a materiala and other rendermolecule node comp change.
+ */
+ void processRenderMoleculeNodeComponentChanged(Object[] args, int mask, int start,
+ boolean restructure) {
+ int i;
+ NodeComponentRetained nc = (NodeComponentRetained) args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])args[3];
+ for (i = start; i < gaArr.length; i++) {
+ RenderAtom ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ // Check if the changed renderAtom is already in
+ // a separate bin - this is to handle the case
+ // when it has been changed to frequent, then to
+ // infrequent and then to frequent again!
+ // If the bin is in soleUser case and one of the components
+ // has been changed to frequent then remove the clone
+ // and point to the mirror
+ // System.out.println("restructure = "+restructure+" ra.renderMolecule.soleUser ="+ra.renderMolecule.soleUser);
+ if (restructure && !ra.renderMolecule.soleUser) {
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ /*
+ if (nc.mirror.changedFrequent != 0) {
+ if ((ra.renderMolecule.soleUserCompDirty& RenderMolecule.ALL_DIRTY_BITS) == 0 ) {
+ rmUpdateList.add(ra.renderMolecule);
+ }
+ ra.renderMolecule.soleUserCompDirty |= mask;
+ }
+ */
+ }
+ else {
+ if ((ra.renderMolecule.soleUserCompDirty& RenderMolecule.ALL_DIRTY_BITS) == 0 ) {
+ rmUpdateList.add(ra.renderMolecule);
+ }
+ ra.renderMolecule.soleUserCompDirty |= mask;
+ }
+ }
+
+ }
+
+
+ void processTextureAttributesChanged(NodeComponentRetained nc,
+ GeometryAtom[] gaArr) {
+
+ RenderAtom ra = null;
+ TextureBin tb;
+ AttributeBin ab;
+ boolean reInsertNeeded = false;
+
+ if (nc.mirror.changedFrequent == 0) {
+ reInsertNeeded = true;
+ }
+
+ for (int k = 0; k < gaArr.length; k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+
+ tb = ra.renderMolecule.textureBin;
+
+ if (!reInsertNeeded) {
+
+ // if changedFrequent is not zero, then need
+ // to check if the node component is currently
+ // in an equivalent bin or not. If it is in an
+ // equivalent bin, then the affected ra needs to
+ // be reinserted to a bin with a soleUser
+ // TextureAttributes
+
+ for (int t = 0; t < tb.texUnitState.length; t++) {
+
+ if (tb.texUnitState[t] == null) {
+ continue;
+
+ } else if (tb.texUnitState[t].texAttrs == nc.mirror) {
+ // the TextureAttributes is already in
+ // a sole user position, no need to do anything;
+ // can bail out now
+ return;
+ }
+ }
+ }
+
+ if ((tb.tbFlag & TextureBin.SOLE_USER) != 0) {
+
+ // if the TextureBin is a sole user, then
+ // no need to reInsert, just simply update the
+ // TextureAttributes references @update
+
+ if (tb.soleUserCompDirty == 0) {
+ tbUpdateList.add(tb);
+ }
+
+ tb.soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TA;
+
+ } else {
+ ab= ra.renderMolecule.textureBin.attributeBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertTextureBin(ab, ra);
+ }
+ }
+ }
+
+ void processTexCoordGenerationChanged(NodeComponentRetained nc,
+ GeometryAtom[] gaArr) {
+
+ RenderAtom ra = null;
+ TextureBin tb;
+ AttributeBin ab;
+ boolean reInsertNeeded = false;
+
+ if (nc.mirror.changedFrequent == 0) {
+ reInsertNeeded = true;
+ }
+
+ for (int k = 0; k < gaArr.length; k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+
+ tb = ra.renderMolecule.textureBin;
+
+ if (!reInsertNeeded) {
+
+ // if changedFrequent is not zero, then need
+ // to check if the node component is currently
+ // in an equivalent bin or not. If it is in an
+ // equivalent bin, then the affected ra needs to
+ // be reinserted to a bin with a soleUser
+ // TexCoordGeneration
+
+ for (int t = 0; t < tb.texUnitState.length; t++) {
+
+ if (tb.texUnitState[t] == null) {
+ continue;
+
+ } else if (tb.texUnitState[t].texGen == nc.mirror) {
+ // the TexCoordGeneration is already in
+ // a sole user position, no need to do anything;
+ // can bail out now
+ return;
+ }
+ }
+ }
+
+ if ((tb.tbFlag & TextureBin.SOLE_USER) != 0) {
+
+ // if the TextureBin is a sole user, then
+ // no need to reInsert, just simply update the
+ // TexCoordGeneration references @update
+
+ if (tb.soleUserCompDirty == 0) {
+ tbUpdateList.add(tb);
+ }
+
+ tb.soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TC;
+
+ } else {
+ ab= ra.renderMolecule.textureBin.attributeBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertTextureBin(ab, ra);
+ }
+ }
+ }
+
+
+ void processTextureChanged(NodeComponentRetained nc,
+ GeometryAtom[] gaArr,
+ Object args[]) {
+
+ RenderAtom ra = null;
+ TextureBin tb;
+ AttributeBin ab;
+ boolean reInsertNeeded = false;
+ int command = ((Integer)args[1]).intValue();
+
+ switch (command) {
+ case TextureRetained.ENABLE_CHANGED: {
+ for (int i = 0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ tb = ra.renderMolecule.textureBin;
+
+ if (tb.soleUserCompDirty == 0) {
+ // put this texture unit state on the sole user
+ // update list if it's not already there
+ tbUpdateList.add(tb);
+ }
+
+ tb.soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TEXTURE;
+ }
+ break;
+ }
+ case TextureRetained.IMAGE_CHANGED: {
+
+ TextureRetained texture = (TextureRetained)nc.mirror;
+ Object imgChangedArgs[] = (Object[])args[2];
+ int level = ((Integer)imgChangedArgs[0]).intValue();
+ int face = ((Integer)imgChangedArgs[2]).intValue();
+ ImageComponent newImage = (ImageComponent)imgChangedArgs[1];
+ ImageComponentRetained oldImage;
+
+ // first remove the old image from the RenderBin's
+ // node component list if necessary.
+ // Note: image reference in the texture mirror object
+ // is not updated yet, so it's ok to reference
+ // the mirror object for the old image reference
+
+ oldImage = texture.images[face][level];
+
+ // it is possible that oldImage.source == null because
+ // the mipmap could have been created by the library, and
+ // hence don't have source
+
+ if (oldImage != null) {
+ this.removeNodeComponent(oldImage);
+ }
+
+ // then add the new one to the list if it is byReference or
+ // modifiable.
+
+ if (newImage != null ) {
+ this.addNodeComponent(newImage.retained);
+ }
+ break;
+ }
+ case TextureRetained.IMAGES_CHANGED: {
+ Object imgChangedArgs[] = (Object [])args[2];
+ ImageComponent images[] = (ImageComponent [])imgChangedArgs[0];
+ int face = ((Integer)imgChangedArgs[1]).intValue();
+ TextureRetained texture = (TextureRetained)nc.mirror;
+ ImageComponentRetained oldImage;
+
+ for (int i = 0; i < texture.maxLevels; i++) {
+
+ // first remove the old image from the RenderBin's
+ // node component list if necessary.
+ // Note: image reference in the texture mirror object
+ // is not updated yet, so it's ok to reference
+ // the mirror object for the old image reference
+
+ oldImage = texture.images[face][i];
+
+ // it is possible that oldImage.source == null because
+ // the mipmap could have been created by the library, and
+ // hence don't have source
+
+ if (oldImage != null) {
+ this.removeNodeComponent(oldImage);
+ }
+
+ // then add the new one to the list if it is byReference
+ if (images[i] != null ) {
+ this.addNodeComponent(((ImageComponent)images[i]).retained);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+
+ void processTextureUnitStateChanged(NodeComponentRetained nc,
+ GeometryAtom[] gaArr) {
+ RenderAtom ra = null;
+ TextureBin tb;
+ AttributeBin ab;
+ boolean mirrorSet = false;
+ boolean firstTextureBin = true;
+
+ for (int k = 0; k < gaArr.length; k++) {
+ ra = gaArr[k].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+
+ tb = ra.renderMolecule.textureBin;
+
+ if (firstTextureBin) {
+
+ for (int t = 0;
+ t < tb.texUnitState.length && !mirrorSet;
+ t++) {
+
+ if (tb.texUnitState[t] == null) {
+ continue;
+
+ } else if (tb.texUnitState[t].mirror == nc.mirror) {
+ mirrorSet = true;
+ firstTextureBin = false;
+ }
+ }
+ firstTextureBin = false;
+ }
+
+ if (mirrorSet) {
+
+ if (tb.soleUserCompDirty == 0) {
+ tbUpdateList.add(tb);
+ }
+
+ tb.soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TUS;
+
+ } else {
+ ab= ra.renderMolecule.textureBin.attributeBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertTextureBin(ab, ra);
+ }
+ }
+ }
+
+
+ /**
+ * This processes a rendering attribute change.
+ */
+
+ void processAttributeBinNodeComponentChanged(Object[] args) {
+ int i;
+ GeometryAtom[] gaArr = (GeometryAtom[])args[3];
+ int component = ((Integer)args[1]).intValue();
+ NodeComponentRetained nc = (NodeComponentRetained) args[0];
+
+ RenderAtom ra = null;
+ int start = -1;
+
+ // Get the first ra that is visible
+ for (i = 0; (i < gaArr.length && (start < 0)); i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = i;
+ }
+ }
+ if (start >= 0) {
+ // Force restucture, when changedFrequent is zero OR
+ // when it is changed to changedFrequent the first time OR
+ // when the ignoreVC changedFrequent flag is set for the first time
+ // when the last one is set for the first time, we need to force
+ // any separate dlist in RMs to go thru VA
+ boolean restructure = (nc.mirror.changedFrequent == 0 ||
+ ra.renderMolecule.textureBin.attributeBin.definingRenderingAttributes != nc.mirror);
+
+ if (component != RenderingAttributesRetained.VISIBLE) {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ if (restructure && !ra.renderMolecule.textureBin.attributeBin.soleUser) {
+ EnvironmentSet e= ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ /*
+ // If changed Frequent the first time,
+ // then the cached value
+ // may not be up-to-date since the nc is
+ // updated in updateObject
+ // So, add it to the list so that the cached value can
+ // be updated
+ if (nc.mirror.changedFrequent != 0) {
+ AttributeBin aBin = ra.renderMolecule.textureBin.attributeBin;
+ if ((aBin.onUpdateList & AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST) == 0 ) {
+ aBinUpdateList.add(aBin);
+ aBin.onUpdateList |= AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
+ }
+ }
+ */
+ }
+ else {
+ AttributeBin aBin = ra.renderMolecule.textureBin.attributeBin;
+ if ((aBin.onUpdateList & AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST) == 0 ) {
+ aBinUpdateList.add(aBin);
+ aBin.onUpdateList |= AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
+ }
+ }
+ }
+ }
+ else {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ renderAtoms.remove(renderAtoms.indexOf(ra));
+ removeARenderAtom(ra);
+ }
+ }
+ }
+ }
+
+
+ void processFogChanged(Object[] args) {
+ FogRetained fog = (FogRetained)args[0];
+ EnvironmentSet e;
+ int component = ((Integer)args[1]).intValue();
+
+ if ((component &(FogRetained.SCOPE_CHANGED |
+ FogRetained.BOUNDS_CHANGED |
+ FogRetained.BOUNDINGLEAF_CHANGED)) != 0){
+ envDirty |= REEVALUATE_FOG;
+ }
+ else {
+ UnorderList list = fog.mirrorFog.environmentSets;
+ synchronized (list) {
+ EnvironmentSet envsets[] = (EnvironmentSet []) list.toArray(false);
+ int size = list.size();
+ for (int i = 0; i < size; i++) {
+ e = envsets[i];
+ e.canvasDirty |= Canvas3D.FOG_DIRTY;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This routine get called whenever a component of the appearance
+ * changes
+ */
+ void processAppearanceChanged(Object[] args){
+ int component = ((Integer)args[1]).intValue();
+ int i;
+ GeometryAtom[] gaArr = (GeometryAtom[] )args[3];
+ GeometryAtom ga;
+ RenderAtom ra = null;
+ AppearanceRetained app = (AppearanceRetained) args[0];
+ int TEXTURE_STATE_CHANGED =
+ AppearanceRetained.TEXTURE_UNIT_STATE |
+ AppearanceRetained.TEXTURE |
+ AppearanceRetained.TEXTURE_ATTR |
+ AppearanceRetained.TEXCOORD_GEN ;
+
+ int start = -1;
+
+ // Get the first ra that is visible
+ for (i = 0; (i < gaArr.length && (start < 0)); i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin()) {
+ continue;
+ }
+ else {
+ start = i;
+ }
+ }
+
+ if (start >= 0) {
+
+ if ((component & TEXTURE_STATE_CHANGED) != 0) {
+
+
+ if (((app.changedFrequent & TEXTURE_STATE_CHANGED) != 0) &&
+ ((ra.renderMolecule.textureBin.tbFlag &
+ TextureBin.SOLE_USER) != 0)) {
+
+/*
+System.out.println("renderbin. texture state changed tb sole user " +
+ ra.renderMolecule.textureBin + " tb.tbFlag= " +
+ ra.renderMolecule.textureBin.tbFlag);
+*/
+
+ TextureBin tb;
+
+
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ tb = ra.renderMolecule.textureBin;
+ if (tb.soleUserCompDirty == 0) {
+ tbUpdateList.add(tb);
+ }
+
+ // mark that the texture unit state ref is changed
+ // also mark that the TextureBin needs to reevaluate
+ // number of active textures
+ tb.soleUserCompDirty |=
+ TextureBin.SOLE_USER_DIRTY_REF;
+ }
+ } else {
+/*
+System.out.println("renderbin. texture state changed tb not sole user " +
+ ra.renderMolecule.textureBin + " tb.tbFlag= " +
+ ra.renderMolecule.textureBin.tbFlag);
+
+System.out.println("......tb.soleUser= " +
+ ((ra.renderMolecule.textureBin.tbFlag & TextureBin.SOLE_USER) != 0) +
+ " app.changedFrequent= " +
+ ((app.changedFrequent & TEXTURE_STATE_CHANGED) != 0));
+
+*/
+
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ AttributeBin ab = ra.renderMolecule.textureBin.attributeBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertTextureBin(ab, ra);
+ }
+ }
+ } else if ((component & AppearanceRetained.RENDERING) != 0) {
+ boolean visible = ((Boolean)args[4]).booleanValue();
+ visGAIsDirty = true;
+ visQuery = true;
+ if (!visible) {
+ // remove all gaAttrs
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ renderAtoms.remove(renderAtoms.indexOf(ra));
+ removeARenderAtom(ra);
+ }
+ }
+ else {
+ if ((app.mirror.changedFrequent & component) != 0 &&
+ ra.renderMolecule.textureBin.attributeBin.soleUser) {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ AttributeBin aBin = ra.renderMolecule.textureBin.attributeBin;
+ if ((aBin.onUpdateList & AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST) == 0 ) {
+ aBinUpdateList.add(aBin);
+ aBin.onUpdateList |= AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
+ }
+
+ }
+ }
+ else {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ EnvironmentSet e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+ }
+ }
+
+ else if ((component & (AppearanceRetained.COLOR |
+ AppearanceRetained.MATERIAL|
+ AppearanceRetained.TRANSPARENCY|
+ AppearanceRetained.POLYGON |
+ AppearanceRetained.LINE|
+ AppearanceRetained.POINT)) != 0) {
+ // System.out.println("AppearanceRetained.POINT = "+AppearanceRetained.POINT);
+ // System.out.println("(app.mirror.changedFrequent & component) != 0 "+app.mirror.changedFrequent );
+ // System.out.println("ra.renderMolecule.soleUser "+ra.renderMolecule.soleUser);
+ if ((app.mirror.changedFrequent & component) != 0 &&
+ ra.renderMolecule.soleUser) {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ if ((ra.renderMolecule.soleUserCompDirty& RenderMolecule.ALL_DIRTY_BITS) == 0 ) {
+ rmUpdateList.add(ra.renderMolecule);
+ }
+ ra.renderMolecule.soleUserCompDirty |= component;
+
+ }
+
+ }
+ else {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+
+ }
+ }
+ }
+ } else {
+ // Nothing is visible
+ if ((component & AppearanceRetained.RENDERING) != 0) {
+ // Rendering attributes change
+ visGAIsDirty = true;
+ visQuery = true;
+ }
+ }
+ }
+
+
+
+
+ void processModelClipChanged(Object[] args) {
+ ModelClipRetained modelClip =
+ (ModelClipRetained)args[0];
+ EnvironmentSet e;
+ int component = ((Integer)args[1]).intValue();
+
+ if ((component & (ModelClipRetained.SCOPE_CHANGED |
+ ModelClipRetained.BOUNDS_CHANGED |
+ ModelClipRetained.BOUNDINGLEAF_CHANGED)) != 0){
+ envDirty |= REEVALUATE_MCLIP;
+
+ } else if ((component & (ModelClipRetained.ENABLE_CHANGED |
+ ModelClipRetained.ENABLES_CHANGED)) != 0) {
+ // need to render modelclip
+ if (!changedModelClips.contains(modelClip.mirrorModelClip))
+ changedModelClips.add(modelClip.mirrorModelClip);
+
+ // need to reevaluate envset
+ envDirty |= REEVALUATE_MCLIP;
+
+ } else {
+ UnorderList list = modelClip.mirrorModelClip.environmentSets;
+ synchronized (list) {
+ EnvironmentSet envsets[] = (EnvironmentSet []) list.toArray(false);
+ int size = list.size();
+ for (int i = 0; i < size; i++) {
+ e = envsets[i];
+ e.canvasDirty |= Canvas3D.MODELCLIP_DIRTY;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This routine get called whenever a region of the boundingleaf
+ * changes
+ */
+ void processBoundingLeafChanged(Object[] args, long refTime){
+ // Notify all users of this bounding leaf, it may
+ // result in the re-evaluation of the lights/fogs/backgrounds
+ Object[] users = (Object[])(args[3]);
+ int i;
+
+ // TODO: Handle other object affected by bounding leaf changes
+ for (i = 0; i < users.length; i++) {
+ LeafRetained leaf = (LeafRetained)users[i];
+ switch(leaf.nodeType) {
+ case NodeRetained.AMBIENTLIGHT:
+ case NodeRetained.POINTLIGHT:
+ case NodeRetained.SPOTLIGHT:
+ case NodeRetained.DIRECTIONALLIGHT:
+ if (universe.renderingEnvironmentStructure.isLightScopedToThisView(leaf, view))
+ envDirty |= REEVALUATE_LIGHTS;
+ break;
+ case NodeRetained.LINEARFOG:
+ case NodeRetained.EXPONENTIALFOG:
+ if (universe.renderingEnvironmentStructure.isFogScopedToThisView(leaf, view))
+ envDirty |= REEVALUATE_FOG;
+ break;
+ case NodeRetained.BACKGROUND:
+ if (universe.renderingEnvironmentStructure.isBgScopedToThisView(leaf, view))
+ reEvaluateBg = true;
+ break;
+ case NodeRetained.CLIP:
+ if (universe.renderingEnvironmentStructure.isClipScopedToThisView(leaf, view))
+ reEvaluateClip = true;
+ break;
+ case NodeRetained.MODELCLIP:
+ if (universe.renderingEnvironmentStructure.isMclipScopedToThisView(leaf, view))
+ envDirty |= REEVALUATE_MCLIP;
+ break;
+ case NodeRetained.ALTERNATEAPPEARANCE:
+ if (universe.renderingEnvironmentStructure.isAltAppScopedToThisView(leaf, view)) altAppearanceDirty = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+
+ void processOrientedShape3DChanged(Object[] gaArr) {
+
+ RenderAtom ra;
+ for (int i = 0; i < gaArr.length; i++) {
+ ra = ((GeometryAtom)gaArr[i]).getRenderAtom(view);
+ if (ra!= null && ra.inRenderBin() && !ra.inDirtyOrientedRAs()) {
+ dirtyOrientedRAs.add(ra);
+ ra.dirtyMask |= RenderAtom.IN_DIRTY_ORIENTED_RAs;
+ }
+ }
+ }
+
+
+ void processShapeChanged(Object[] args, long refTime) {
+
+ int component = ((Integer)args[1]).intValue();
+ int i;
+ RenderAtom ra;
+ RenderAtom raNext;
+ EnvironmentSet e;
+ TextureBin tb;
+ if ((component & Shape3DRetained.APPEARANCE_CHANGED) != 0) {
+ GeometryAtom[] gaArr = (GeometryAtom[])args[4];
+ if (gaArr.length > 0) {
+ if (!gaArr[0].source.appearanceOverrideEnable) {
+ for (i =0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin()) {
+ continue;
+ }
+ ra.app = ra.geometryAtom.source.appearance;
+ e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+ else {
+ for (i =0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin()) {
+ continue;
+ }
+ // if its using the alternate appearance continue ..
+ if (ra.app == ra.geometryAtom.source.otherAppearance)
+ continue;
+ ra.app = ra.geometryAtom.source.appearance;
+ e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+ }
+ }
+ else if ((component & Shape3DRetained.GEOMETRY_CHANGED) != 0) {
+ processDataChanged((Object[])args[2], (Object[])args[3], refTime);
+ }
+ else if ((component & Shape3DRetained.APPEARANCEOVERRIDE_CHANGED) != 0) {
+ AppearanceRetained app, saveApp = null;
+ Shape3DRetained saveShape = null;
+ GeometryAtom[] gaArr = (GeometryAtom[])args[4];
+ Object[] retVal;
+ for (i =0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin())
+ continue;
+ // Once shape could have many geometryAtoms, add the
+ // mirrorShape as a user of an appearance only once
+
+ if (saveShape != ra.geometryAtom.source) {
+ saveShape = ra.geometryAtom.source;
+ if (ra.geometryAtom.source.appearanceOverrideEnable) {
+ retVal =universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ saveShape.otherAppearance = (AppearanceRetained)retVal[1];
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ if (app != null) {
+ app.sgApp.addAMirrorUser(saveShape);
+ }
+ }
+ else {// use the default
+ app = ra.geometryAtom.source.appearance;
+ }
+ }
+ else {
+ // If it were using the alternate appearance
+ // remove itself as the user
+ if (ra.app == saveShape.otherAppearance &&
+ ra.app != null) {
+ ra.app.sgApp.removeAMirrorUser(saveShape);
+ }
+ app = ra.geometryAtom.source.appearance;
+ saveShape.otherAppearance = null;
+ }
+ saveApp = app;
+ }
+ else {
+ app = saveApp;
+ }
+ ra.app = app;
+ e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+
+ }
+
+
+ /**
+ * Process a Text3D data change. This involves removing all the
+ * old geometry atoms in the list, and the creating new ones.
+ */
+ void processDataChanged(Object[] oldGaList,
+ Object[] newGaList, long referenceTime) {
+ Shape3DRetained s, src;
+ RenderAtom ra;
+ RenderMolecule rm;
+ int i, j;
+ Transform3D trans;
+ ArrayList rmChangedList = new ArrayList(5);
+ GeometryRetained geo;
+ GeometryAtom ga;
+
+ for (i=0; i<oldGaList.length; i++) {
+ ga = ((GeometryAtom)oldGaList[i]);
+
+ // Make sure that there is atleast one geo that is non-null
+ geo = null;
+ for (int k = 0; (k < ga.geometryArray.length && geo == null); k++) {
+ geo = ga.geometryArray[k];
+ }
+ if (geo == null)
+ continue;
+
+
+ ra = ga.getRenderAtom(view);
+
+ if (ra != null && ra.inRenderBin()) {
+ renderAtoms.remove(renderAtoms.indexOf(ra));
+ removeARenderAtom(ra);
+ }
+ }
+
+ visQuery = true;
+ visGAIsDirty = true;
+ }
+
+
+ void processMorphChanged(Object[] args, long refTime) {
+
+ int component = ((Integer)args[1]).intValue();
+ int i;
+ RenderAtom ra;
+ TextureBin tb;
+ EnvironmentSet e;
+ RenderAtom raNext;
+ if ((component & MorphRetained.APPEARANCE_CHANGED) != 0) {
+ GeometryAtom[] gaArr = (GeometryAtom[])args[4];
+ if (gaArr.length > 0) {
+ if (!gaArr[0].source.appearanceOverrideEnable) {
+ for (i =0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin()) {
+ continue;
+ }
+ ra.app = ra.geometryAtom.source.appearance;
+ e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+ else {
+ for (i =0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin())
+ continue;
+
+ // if its using the alternate appearance continue ..
+ if (ra.app == ra.geometryAtom.source.otherAppearance)
+ continue;
+ ra.app = ra.geometryAtom.source.appearance;
+ e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+ }
+ }
+ else if ((component & MorphRetained.APPEARANCEOVERRIDE_CHANGED) != 0) {
+ AppearanceRetained app, saveApp = null;
+ Shape3DRetained saveShape = null;
+ GeometryAtom[] gaArr = (GeometryAtom[])args[4];
+ Object[] retVal;
+
+ for (i =0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin())
+ continue;
+ // Once shape could have many geometryAtoms, add the
+ // mirrorShape as a user of an appearance only once
+
+ if (saveShape != ra.geometryAtom.source) {
+ saveShape = ra.geometryAtom.source;
+ if (ra.geometryAtom.source.appearanceOverrideEnable) {
+ retVal =universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ saveShape.otherAppearance = (AppearanceRetained)retVal[1];
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ if (app != null) {
+ app.sgApp.addAMirrorUser(saveShape);
+ }
+ }
+ else {// use the default
+ app = ra.geometryAtom.source.appearance;
+ }
+ }
+ else {
+ // If it were using the alternate appearance
+ // remove itself as the user
+ if (ra.app == saveShape.otherAppearance &&
+ ra.app != null) {
+ ra.app.sgApp.removeAMirrorUser(saveShape);
+ }
+ app = ra.geometryAtom.source.appearance;
+ saveShape.otherAppearance = null;
+ }
+ saveApp = app;
+ }
+ else {
+ app = saveApp;
+ }
+ ra.app = app;
+ e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+
+ }
+
+
+
+ /**
+ * This routine gets called whenever the position of the view platform
+ * has changed.
+ */
+ void updateViewPlatform(ViewPlatformRetained vp, float radius) {
+ Transform3D trans = null;
+ ViewPlatform viewP = view.getViewPlatform();
+ if (viewP != null && (ViewPlatformRetained)viewP.retained == vp) {
+ vpcToVworld = vp.getCurrentLocalToVworld(null);
+ vpcToVworldDirty = true;
+ synchronized(vp) {
+ vp.vprDirtyMask |= View.VPR_VIEWPLATFORM_DIRTY;
+ }
+
+ // vp schedSphere is already set and transform in
+ // BehaviorStructure thread which is run before
+ // RenderBin using vp.updateActivationRadius()
+ vpSchedSphereInVworld = vp.schedSphere;
+ reEvaluateBg = true;
+ reEvaluateClip = true;
+ }
+
+ }
+
+
+
+ /**
+ * This routine removes the GeometryAtoms from RenderBin
+ */
+ void processGeometryAtomsChanged(Object[] gaArr) {
+ int i;
+ RenderAtom ra;
+
+ for (i = 0; i < gaArr.length; i++) {
+ ra = ((GeometryAtom)gaArr[i]).getRenderAtom(view);
+ if (ra != null && ra.inRenderBin()) {
+ renderAtoms.remove(renderAtoms.indexOf(ra));
+ removeARenderAtom(ra);
+ }
+ }
+ }
+
+ /**
+ * process Geometry changed, mark the display list
+ * in which renderMolecule is as dirty
+ */
+ void processGeometryChanged(Object[] args) {
+
+ Object[] gaList = (Object[]) args[0];
+
+ GeometryRetained g = (GeometryRetained)args[1];
+ GeometryAtom ga;
+
+ int i;
+
+ for (i = 0; i < gaList.length; i++) {
+ ga = ((GeometryAtom)gaList[i]);
+ RenderAtom renderAtom = ga.getRenderAtom(view);
+ if (renderAtom == null || !renderAtom.inRenderBin()) {
+ continue;
+ }
+
+
+ // Add the renderMolecule to the dirty list so that
+ // display list will be recreated
+ int j = 0;
+ for ( j = 0; j < renderAtom.rListInfo.length; j++) {
+ if (g == renderAtom.rListInfo[j].geometry())
+ break;
+ }
+ RenderAtomListInfo ra = (RenderAtomListInfo)renderAtom.rListInfo[j];
+ if ((ra.groupType & RenderAtom.DLIST) != 0)
+ addDirtyRenderMolecule(ra.renderAtom.renderMolecule);
+
+ if ((ra.groupType & RenderAtom.SEPARATE_DLIST_PER_RINFO) != 0) {
+ addDlistPerRinfo.add(ra);
+ }
+
+ if ((ra.groupType & RenderAtom.SEPARATE_DLIST_PER_GEO) != 0)
+ addGeometryDlist(ra);
+
+ // Raster send this message only for setImage()
+ if (g instanceof RasterRetained) {
+ Object[] objs = (Object[]) args[2];
+ ImageComponentRetained oldImage = (ImageComponentRetained) objs[0];
+ ImageComponentRetained newImage = (ImageComponentRetained) objs[1];
+
+ RasterRetained geo = (RasterRetained)ra.geometry();
+ if (oldImage != null && oldImage.isByReference()) {
+ removeNodeComponent(oldImage);
+ }
+ if (newImage != null && newImage.isByReference()) {
+ addNodeComponent(newImage);
+ }
+ }
+
+ }
+
+ }
+
+ void addTextureBin(TextureBin tb) {
+ textureBinList.add(tb);
+ }
+
+
+ void removeTextureBin(TextureBin tb) {
+ textureBinList.remove(tb);
+ }
+
+ void addDirtyRenderMolecule(RenderMolecule rm) {
+ int i;
+
+ if ((rm.onUpdateList & RenderMolecule.IN_DIRTY_RENDERMOLECULE_LIST) == 0) {
+ if (rm.onUpdateList == 0) {
+ objUpdateList.add(rm);
+ }
+ rm.onUpdateList |= RenderMolecule.IN_DIRTY_RENDERMOLECULE_LIST;
+ dirtyRenderMoleculeList.add(rm);
+ }
+ }
+
+
+
+ void removeDirtyRenderMolecule(RenderMolecule rm) {
+ int i;
+ if ((rm.onUpdateList & RenderMolecule.IN_DIRTY_RENDERMOLECULE_LIST) != 0) {
+ rm.onUpdateList &= ~RenderMolecule.IN_DIRTY_RENDERMOLECULE_LIST;
+ if (rm.onUpdateList == 0) {
+ objUpdateList.remove(rm);
+ }
+ dirtyRenderMoleculeList.remove(dirtyRenderMoleculeList.indexOf(rm));
+ }
+ }
+
+ void updateDirtyDisplayLists(Canvas3D cv,
+ ArrayList rmList, ArrayList dlistPerRinfoList,
+ ArrayList raList, boolean useSharedCtx ) {
+ int size, i, bitMask;
+ long ctx, timeStamp;
+
+ if (useSharedCtx) {
+ ctx = cv.screen.renderer.sharedCtx;
+ cv.makeCtxCurrent(ctx);
+ bitMask = cv.screen.renderer.rendererBit;
+ timeStamp = cv.screen.renderer.sharedCtxTimeStamp;
+ } else {
+ ctx = cv.ctx;
+ bitMask = cv.canvasBit;
+ timeStamp = cv.ctxTimeStamp;
+ }
+
+ size = rmList.size();
+
+ if (size > 0) {
+ for (i = size-1; i >= 0; i--) {
+ RenderMolecule rm = (RenderMolecule)rmList.get(i);
+ rm.updateDisplayList(cv);
+ }
+ rmList.clear();
+ }
+
+ size = dlistPerRinfoList.size();
+
+ if (size > 0) {
+ for (i = size-1; i >= 0 ; i--) {
+ Object[] obj = (Object[])dlistPerRinfoList.get(i);
+ dlistRenderMethod.buildDlistPerRinfo((RenderAtomListInfo)obj[0], (RenderMolecule)obj[1], cv);
+ }
+ dlistPerRinfoList.clear();
+ }
+
+ size = raList.size();
+ if (size > 0) {
+ RenderAtomListInfo ra;
+ GeometryArrayRetained geo;
+
+ for (i = size-1; i >= 0; i--) {
+ ra = (RenderAtomListInfo)raList.get(i);
+ geo = (GeometryArrayRetained) ra.geometry();
+ geo.resourceCreationMask &= ~bitMask;
+ }
+
+ for (i = size-1; i >= 0; i--) {
+ ra = (RenderAtomListInfo)raList.get(i);
+ geo = (GeometryArrayRetained) ra.geometry();
+ if ((geo.resourceCreationMask & bitMask) == 0) {
+ dlistRenderMethod.buildIndividualDisplayList(ra, cv, ctx);
+ geo.resourceCreationMask |= bitMask;
+ geo.setDlistTimeStamp(bitMask, timeStamp);
+ }
+ }
+ raList.clear();
+ }
+
+ if (useSharedCtx) {
+ cv.makeCtxCurrent(cv.ctx);
+ }
+ }
+
+ void removeRenderMolecule(RenderMolecule rm) {
+ renderMoleculeFreelist.add(rm);
+
+ if ((rm.primaryMoleculeType &(RenderMolecule.DLIST_MOLECULE|RenderMolecule.SEPARATE_DLIST_PER_RINFO_MOLECULE)) != 0)
+ renderMoleculeList.remove(rm);
+ }
+
+ void updateAllRenderMolecule(Canvas3D cv) {
+ int i;
+ int size = renderMoleculeList.size();
+
+ if (size > 0) {
+ RenderMolecule[] rmArr = (RenderMolecule[])
+ renderMoleculeList.toArray(false);
+ for (i = size-1 ; i >= 0; i--) {
+ rmArr[i].updateAllPrimaryDisplayLists(cv);
+ }
+ }
+
+ size = sharedDList.size();
+ if (size > 0) {
+ RenderAtomListInfo ra;
+ GeometryArrayRetained geo;
+ RenderAtomListInfo arr[] =
+ (RenderAtomListInfo []) sharedDList.toArray(false);
+ int bitMask = cv.canvasBit;
+
+ // We need two passes to avoid extra buildDisplayList
+ // when geo are the same. The first pass clean the
+ // rendererBit. Note that we can't rely on
+ // resourceCreation since it is a force recreate.
+
+ for (i = size-1; i >= 0; i--) {
+ geo = (GeometryArrayRetained) arr[i].geometry();
+ geo.resourceCreationMask &= ~bitMask;
+ }
+
+ for (i = size-1; i >= 0; i--) {
+ ra = arr[i];
+ geo = (GeometryArrayRetained) ra.geometry();
+ if ((geo.resourceCreationMask & bitMask) == 0) {
+ dlistRenderMethod.buildIndividualDisplayList(ra, cv, cv.ctx);
+ geo.resourceCreationMask |= bitMask;
+ geo.setDlistTimeStamp(bitMask, cv.ctxTimeStamp);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is called to update all renderMolecule
+ * for a shared context of a renderer
+ */
+ void updateAllRenderMolecule(Renderer rdr, Canvas3D cv) {
+ int i;
+ boolean setCtx = false;
+ GeometryArrayRetained geo;
+ int size = renderMoleculeList.size();
+
+ if (size > 0) {
+ RenderMolecule[] rmArr = (RenderMolecule[])
+ renderMoleculeList.toArray(false);
+
+ cv.makeCtxCurrent(rdr.sharedCtx);
+ setCtx = true;
+ for (i = size-1 ; i >= 0; i--) {
+ rmArr[i].updateAllPrimaryDisplayLists(cv);
+ }
+ }
+
+ size = sharedDList.size();
+ if (size > 0) {
+ RenderAtomListInfo arr[] =
+ (RenderAtomListInfo []) sharedDList.toArray(false);
+ RenderAtomListInfo ra;
+
+ if (!setCtx) {
+ cv.makeCtxCurrent(rdr.sharedCtx);
+ setCtx = true;
+ }
+
+ // We need two passes to avoid extra buildDisplayList
+ // when geo are the same. The first pass clean the
+ // rendererBit.
+ int bitMask = cv.screen.renderer.rendererBit;
+ long timeStamp = cv.screen.renderer.sharedCtxTimeStamp;
+
+ for (i = size-1; i >= 0; i--) {
+ geo = (GeometryArrayRetained) arr[i].geometry();
+ geo.resourceCreationMask &= ~bitMask;
+ }
+
+ for (i = size-1; i >= 0; i--) {
+ ra = arr[i];
+ geo = (GeometryArrayRetained) ra.geometry();
+ if ((geo.resourceCreationMask & bitMask) == 0) {
+ dlistRenderMethod.buildIndividualDisplayList(ra, cv,
+ rdr.sharedCtx);
+ geo.resourceCreationMask |= bitMask;
+ geo.setDlistTimeStamp(bitMask, timeStamp);
+ }
+ }
+ }
+ if (setCtx) {
+ cv.makeCtxCurrent(cv.ctx);
+ }
+ }
+
+ void processText3DTransformChanged(Object[] list,
+ Object[] transforms,
+ long referenceTime) {
+ int i, j, numShapes;
+ GeometryAtom ga;
+ RenderMolecule rm;
+ RenderAtom ra;
+
+ if (transforms.length != 0) {
+ numShapes = list.length;
+ for (i=0; i<numShapes; i++) {
+
+ ga = (GeometryAtom)list[i];
+ ra = ga.getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin()) {
+ continue;
+ }
+ /*
+ System.out.println("numShapes is " + numShapes +
+ " transforms.length is " + transforms.length);
+ */
+ for (j=0; j<transforms.length; j++) {
+
+ ga.lastLocalTransformArray[j] = (Transform3D)transforms[j];
+
+
+
+
+ for(int k = 0; k < ra.rListInfo.length; k++) {
+ if (ra.rListInfo[k].localToVworld == null) {
+ ra.rListInfo[k].localToVworld = VirtualUniverse.mc.getTransform3D(null);
+ }
+ }
+
+ if (ra.isOriented() && !ra.inDirtyOrientedRAs()) {
+ dirtyOrientedRAs.add(ra);
+ ra.dirtyMask |= RenderAtom.IN_DIRTY_ORIENTED_RAs;
+ } else if (!ra.onUpdateList()) {
+ ra.dirtyMask |= RenderAtom.ON_UPDATELIST;
+ objUpdateList.add(ra);
+ }
+ }
+ }
+ }
+ }
+
+
+ void processOrderedGroupRemoved(J3dMessage m) {
+ int i, n;
+ Object[] ogList = (Object[])m.args[0];
+ Object[] ogChildIdList = (Object[])m.args[1];
+ OrderedGroupRetained og;
+ int index;
+ int val;
+ OrderedBin ob;
+ OrderedChildInfo cinfo = null;
+
+ /*
+ System.out.println("RB : processOrderedGroupRemoved message " + m);
+ System.out.println("RB : processOrderedGroupRemoved - ogList.length is " +
+ ogList.length);
+ System.out.println("RB : processOrderedGroupRemoved - obList " +
+ obList);
+ */
+ for (n = 0; n < ogList.length; n++) {
+ og = (OrderedGroupRetained)ogList[n];
+ index = ((Integer)ogChildIdList[n]).intValue();
+
+ ob = og.getOrderedBin(view.viewIndex);
+ // System.out.println("Removed, index = "+index+" ob = "+ob);
+ if (ob != null) {
+ // Add at the end of the childInfo, for remove we don't care about
+ // the childId
+ cinfo = new OrderedChildInfo(OrderedChildInfo.REMOVE, index, -1, null);
+ ob.addChildInfo(cinfo);
+
+ if (!ob.onUpdateList) {
+ obList.add(ob);
+ ob.onUpdateList = true;
+ }
+ }
+ }
+
+ }
+
+
+ void processOrderedGroupInserted(J3dMessage m) {
+ Object[] ogList = (Object[])m.args[0];
+ Object[] ogChildIdList = (Object[])m.args[1];
+ Object[] ogOrderedIdList = (Object[])m.args[2];
+
+
+ OrderedGroupRetained og;;
+ int index;
+ int orderedId;
+ OrderedBin ob;
+ OrderedChildInfo cinfo;
+ // System.out.println("Inserted OG, index = "+index+" orderedId = "+orderedId+" og = "+og+" og.orderedBin = "+og.orderedBin);
+ // System.out.println("Inserted OG, orderedId = "+orderedId);
+ // System.out.println("Inserted, index = "+index+" oid = "+orderedId+" ob = "+ob);
+
+ if(ogList == null)
+ return;
+
+ for (int n = 0; n < ogList.length; n++) {
+ og = (OrderedGroupRetained)ogList[n];
+ index = ((Integer)ogChildIdList[n]).intValue();
+ orderedId = ((Integer)ogOrderedIdList[n]).intValue();
+ ob = og.getOrderedBin(view.viewIndex);
+ cinfo = null;
+
+
+ if (ob != null) {
+ // Add at the end of the childInfo
+ cinfo = new OrderedChildInfo(OrderedChildInfo.ADD, index, orderedId, null);
+ ob.addChildInfo(cinfo);
+
+ if (!ob.onUpdateList) {
+ obList.add(ob);
+ ob.onUpdateList = true;
+ }
+ }
+ }
+ }
+
+ void processTransformChanged(long referenceTime) {
+ int i, j, k, numRenderMolecules, n;
+ Shape3DRetained s;
+ RenderMolecule rm;
+ RenderAtom ra;
+ Transform3D trans;
+ LightRetained[] lights;
+ FogRetained fog;
+ ModelClipRetained modelClip;
+ AppearanceRetained app;
+ Object[] list, nodesArr;
+ UnorderList arrList;
+ int size;
+
+ targets = universe.transformStructure.getTargetList();
+
+ // process geometry atoms
+ arrList = targets.targetList[Targets.GEO_TARGETS];
+ if (arrList != null) {
+ Object[] retVal;
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ //System.out.println("GS:");
+ for (n = 0; n < size; n++) {
+ list = (Object[])nodesArr[n];
+
+ for (i=0; i<list.length; i++) {
+
+ GeometryAtom ga = (GeometryAtom) list[i];
+ //System.out.println(" ga " + ga);
+ ra = ga.getRenderAtom(view);
+ if (ra == null || !ra.inRenderBin())
+ continue;
+
+ rm = ra.renderMolecule;
+
+ if (rm != null && rm.renderBin == this) {
+
+ if (ga.source.inBackgroundGroup && (rm.onUpdateList &
+ RenderMolecule.UPDATE_BACKGROUND_TRANSFORM) == 0) {
+ if (rm.onUpdateList == 0) {
+ objUpdateList.add(rm);
+ }
+ rm.onUpdateList |=
+ RenderMolecule.UPDATE_BACKGROUND_TRANSFORM;
+ }
+
+ lights = universe.renderingEnvironmentStructure.
+ getInfluencingLights(ra, view);
+ fog = universe.renderingEnvironmentStructure.
+ getInfluencingFog(ra, view);
+ modelClip = universe.renderingEnvironmentStructure.
+ getInfluencingModelClip(ra, view);
+
+ if (ra.geometryAtom.source.appearanceOverrideEnable) {
+ retVal = universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+ // TODO: Should we do a more extensive equals
+ // app?
+ if (ra.envSet.equals(ra, lights, fog, modelClip) &&
+ app == ra.app) {
+
+ if (ra.hasSeparateLocaleVwcBounds()
+ && !ra.onLocaleVwcBoundsUpdateList()) {
+ ra.dirtyMask |= ra.ON_LOCALE_VWC_BOUNDS_UPDATELIST;
+ raLocaleVwcBoundsUpdateList.add(ra);
+ }
+
+ // If the locale are different and the xform has changed
+ // then we need to translate the rm's localToVworld by
+ // the locale differences
+ if (locale != ga.source.locale) {
+ if (rm.onUpdateList == 0) {
+ objUpdateList.add(rm);
+ }
+ rm.onUpdateList |= RenderMolecule.LOCALE_TRANSLATION;
+
+ }
+ if ((rm.primaryMoleculeType & RenderMolecule.DLIST_MOLECULE) != 0) {
+ if (rm.onUpdateList == 0) {
+ objUpdateList.add(rm);
+ }
+ rm.onUpdateList |= RenderMolecule.BOUNDS_RECOMPUTE_UPDATE;
+
+ }
+ // Note that the rm LOCALE Translation update should ocuur
+ // Before the ra is added to the object update list
+ // It is a Text3D Molecule
+ else if ((rm.primaryMoleculeType & RenderMolecule.TEXT3D_MOLECULE) != 0){
+
+ if (!ra.onUpdateList()) {
+ ra.dirtyMask |= RenderAtom.ON_UPDATELIST;
+ objUpdateList.add(ra);
+ }
+ }
+ if (ra.isOriented() && !ra.inDirtyOrientedRAs()) {
+ dirtyOrientedRAs.add(ra);
+ ra.dirtyMask |= RenderAtom.IN_DIRTY_ORIENTED_RAs;
+
+ }
+ // If not opaque or in OG or is not a transparent bg geometry
+ // and transp sort mode is sort_geometry, then ..
+ if (!ra.renderMolecule.isOpaqueOrInOG && ra.geometryAtom.source.geometryBackground == null && transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY && !ra.inDepthSortList()) {
+ // Do the updating of the centroid
+ // when the render is running
+ ra.geometryAtom.updateCentroid();
+ // System.out.println("========> adding to the dirty list .., transpSortMode = "+transpSortMode);
+ dirtyDepthSortRenderAtom.add(ra);
+ ra.dirtyMask |= RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST;
+ numDirtyTinfo += ra.rListInfo.length;
+
+ }
+ continue;
+ }
+ // If the appearance has changed ..
+ if (ra.app != app) {
+ if (ra.geometryAtom.source.appearanceOverrideEnable) {
+ // If it was using the alternate appearance, then ..
+ if (ra.app == ra.geometryAtom.source.otherAppearance) {
+ if (ra.app != null) {
+ // remove this mirror shape from the user list
+ ra.geometryAtom.source.otherAppearance.sgApp.removeAMirrorUser(ra.geometryAtom.source);
+ ra.geometryAtom.source.otherAppearance = null;
+ }
+ }
+ // if we are using the alternate app, add the mirror
+ // shape to the userlist
+ if (app != ra.geometryAtom.source.appearance) {
+ // Second check is needed to prevent,
+ // the mirror shape
+ // that has multiple ra's to be added more than
+ // once
+ if (app != null && app != ra.geometryAtom.source.otherAppearance) {
+ app.sgApp.addAMirrorUser(ra.geometryAtom.source);
+ ra.geometryAtom.source.otherAppearance = app;
+ }
+ }
+
+ }
+ }
+
+
+ // Remove the renderAtom from the current
+ // renderMolecule and reinsert
+ getNewEnvironment(ra, lights, fog, modelClip, app);
+ }
+ }
+ }
+ }
+
+ // process misc environment nodes
+ arrList = targets.targetList[Targets.ENV_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+ for (n = 0; n < size; n++) {
+ list = (Object[])nodesArr[n];
+ for (i=0; i<list.length; i++) {
+
+ if (list[i] instanceof LightRetained && universe.renderingEnvironmentStructure.isLightScopedToThisView(list[i], view)) {
+ if (!changedLts.contains(list[i]) )
+ changedLts.add(list[i]);
+ envDirty |= REEVALUATE_LIGHTS; // mark the canvas as dirty as well
+ }
+ else if (list[i] instanceof ModelClipRetained && universe.renderingEnvironmentStructure.isMclipScopedToThisView(list[i], view)) {
+ if (!changedModelClips.contains(list[i]))
+ changedModelClips.add(list[i]);
+ envDirty |= REEVALUATE_MCLIP; // mark the canvas as dirty as well
+ }
+ else if (list[i] instanceof FogRetained && universe.renderingEnvironmentStructure.isFogScopedToThisView(list[i], view)) {
+ if (!changedFogs.contains(list[i]))
+ changedFogs.add(list[i]);
+ envDirty |= REEVALUATE_FOG; // mark the canvas as dirty as well
+ }
+ else if (list[i] instanceof AlternateAppearanceRetained && universe.renderingEnvironmentStructure.isAltAppScopedToThisView(list[i], view)) {
+ altAppearanceDirty = true;
+ }
+ }
+ }
+ }
+
+ // process ViewPlatform nodes
+ arrList = targets.targetList[Targets.VPF_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+ for (n = 0; n < size; n++) {
+ list = (Object[])nodesArr[n];
+ for (i=0; i<list.length; i++) {
+ float radius;
+ synchronized(list[i]) {
+ radius = (float)((ViewPlatformRetained)list[i]).sphere.radius;
+ }
+ updateViewPlatform((ViewPlatformRetained)list[i], radius);
+ }
+ }
+ }
+
+ targets = null;
+
+ blUsers = universe.transformStructure.getBlUsers();
+ if (blUsers != null) {
+ size = blUsers.size();
+ for (j = 0; j < size; j++) {
+ LeafRetained mLeaf = (LeafRetained)blUsers.get(j);
+ if (mLeaf instanceof LightRetained && universe.renderingEnvironmentStructure.isLightScopedToThisView(mLeaf, view)) {
+ envDirty |= REEVALUATE_LIGHTS;
+ }
+ else if (mLeaf instanceof FogRetained && universe.renderingEnvironmentStructure.isFogScopedToThisView(mLeaf, view)) {
+ envDirty |= REEVALUATE_FOG;
+ }
+ else if (mLeaf instanceof ModelClipRetained && universe.renderingEnvironmentStructure.isMclipScopedToThisView(mLeaf, view)) {
+ envDirty |= REEVALUATE_MCLIP;
+ }
+ else if (mLeaf instanceof AlternateAppearanceRetained && universe.renderingEnvironmentStructure.isAltAppScopedToThisView(mLeaf, view)) {
+ altAppearanceDirty = true;
+ }
+ }
+ blUsers = null;
+ }
+
+ visQuery = true;
+
+ }
+
+
+
+ /**
+ * This processes a LIGHT change.
+ */
+ void processLightChanged() {
+ int i, j, k, l, n;
+ LightRetained lt;
+ EnvironmentSet e;
+ Object[] args;
+ LightRetained[] mLts;
+ int component;
+ int lightSize = lightMessageList.size();
+
+ for (n = 0; n < lightSize; n++) {
+ J3dMessage msg = (J3dMessage)lightMessageList.get(n);
+ args = msg.args;
+ mLts = (LightRetained[])args[3];
+ component = ((Integer)args[1]).intValue();
+ lt = (LightRetained) args[0];
+
+
+ if ((component &(LightRetained.SCOPE_CHANGED |
+ LightRetained.BOUNDS_CHANGED |
+ LightRetained.BOUNDINGLEAF_CHANGED)) != 0){
+ envDirty |= REEVALUATE_LIGHTS;
+ component &= ~(LightRetained.SCOPE_CHANGED |
+ LightRetained.BOUNDS_CHANGED |
+ LightRetained.BOUNDINGLEAF_CHANGED);
+ }
+ // This is a light that is not a part of any
+ // environment set, first check if it is enabled
+ // if it is then reEvaluate all renderAtoms in the
+ // scene, otherwise do nothing
+ if (component != 0) {
+ if (lt.nodeType == LightRetained.AMBIENTLIGHT) {
+ UnorderList list;
+ EnvironmentSet envsets[];
+ for (i = 0; i < mLts.length; i++) {
+ LightRetained lti = mLts[i];
+ list = lti.environmentSets;
+ synchronized (list) {
+ int size = list.size();
+ if (size > 0) {
+ envsets = (EnvironmentSet []) list.toArray(false);
+ for (j = 0; j < size; j++) {
+ e = envsets[j];
+ e.canvasDirty |= Canvas3D.AMBIENTLIGHT_DIRTY;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ }
+ } else {
+ if ((component & LightRetained.ENABLE_CHANGED) != 0) {
+ boolean value = lti.lightOn;
+ if (value) {
+ if (!changedLts.contains(lti))
+ changedLts.add(lti);
+ envDirty |= REEVALUATE_LIGHTS;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < mLts.length; i++) {
+ LightRetained lti = mLts[i];
+ if ((component & LightRetained.ENABLE_CHANGED) != 0) {
+ boolean value = ((Boolean)args[4]).booleanValue();
+ if (value) {
+ if (!changedLts.contains(lti))
+ changedLts.add(lti);
+
+ envDirty |= REEVALUATE_LIGHTS;
+ }
+ }
+ UnorderList list = lti.environmentSets;
+ EnvironmentSet envsets[];
+ synchronized (list) {
+ int size = list.size();
+ int lsize;
+ if (size > 0) {
+ envsets = (EnvironmentSet []) list.toArray(false);
+ if ((component & LightRetained.ENABLE_CHANGED) != 0) {
+ boolean value = ((Boolean)args[4]).booleanValue();
+ for (j = 0; j <size; j++) {
+ e = envsets[j];
+ lsize = e.lights.size();
+ for (k = 0; k < lsize; k++) {
+ if (e.lights.get(k) == lti) {
+ if (value == true)
+ e.enableMaskCache |= (1 << e.ltPos[k]);
+ else
+ e.enableMaskCache &= ~(1 << e.ltPos[k]);
+ break;
+ }
+ }
+ e.canvasDirty |= Canvas3D.LIGHTENABLES_DIRTY;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ }
+ } else {
+ for (j = 0; j < size; j++) {
+ e = envsets[j];
+ lsize = e.lights.size();
+ for (k = 0; k < lsize; k++) {
+ if (e.lights.get(k) == lti) {
+ e.lightBin.canvasDirty |= Canvas3D.LIGHTBIN_DIRTY;
+ e.lightBin.lightDirtyMaskCache |= (1 << e.ltPos[k]);
+ if (!e.lightBin.onUpdateList) {
+ e.lightBin.onUpdateList = true;
+ objUpdateList.add(e.lightBin);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ } // end sync.
+ }
+ }
+ }
+ msg.decRefcount();
+ }
+
+ }
+
+ void processGeometryAtom(GeometryAtom ga, long referenceTime) {
+ RenderAtom renderAtom;
+ RenderMolecule rm;
+
+ // System.out.println("+");
+
+
+ GeometryRetained geo = null;
+ for (int k = 0; (k < ga.geometryArray.length && geo == null); k++) {
+ geo = ga.geometryArray[k];
+ }
+ if (geo == null)
+ return;
+
+
+ renderAtom = ga.getRenderAtom(view);
+
+ if (renderAtom != null) {
+ renderAtom.lastVisibleTime = referenceTime;
+ }
+
+ if (renderAtom == null || renderAtom.inRenderBin()) {
+ return;
+ }
+
+
+ // If the geometry is all null , don't insert
+ // Make sure that there is atleast one geo that is non-null
+
+ if (renderAtom.geometryAtom.source.viewList != null) {
+ if (renderAtom.geometryAtom.source.viewList.contains(view)) {
+ // System.out.println("Inserting RenderAtom, ra = "+renderAtom);
+ // System.out.println("ga = "+renderAtom.geometryAtom+" renderAtom.geometryAtom.source.viewList = "+renderAtom.geometryAtom.source.viewList);
+ rm = insertRenderAtom(renderAtom);
+ }
+ }
+ // No view specific scpoing
+ else {
+ rm = insertRenderAtom(renderAtom);
+ }
+
+ }
+
+
+
+ void processBgGeometryAtoms(GeometryAtom[] nodes, long referenceTime) {
+ int i;
+ GeometryAtom ga;
+ RenderAtom renderAtom;
+ RenderMolecule rm;
+ RenderAtomListInfo ra;
+ GeometryRetained geo;
+
+ for (i=0; i<nodes.length; i++) {
+ ga = nodes[i];
+
+ // Make sure that there is atleast one geo that is non-null
+ geo = null;
+ for (int k = 0; (k < ga.geometryArray.length && geo == null); k++) {
+ geo = ga.geometryArray[k];
+ }
+ if (geo == null)
+ continue;
+
+
+ renderAtom = ga.getRenderAtom(view);
+ if (renderAtom == null)
+ return;
+
+ renderAtom.lastVisibleTime = referenceTime;
+ if (renderAtom.inRenderBin()) {
+ continue;
+ }
+
+
+ // This means that the renderAtom was not visible in the last
+ // frame ,so , no contention with the renderer ...
+ rm = insertRenderAtom(renderAtom);
+ }
+
+ }
+
+ /**
+ * This method looks through the list of RenderAtoms to see if
+ * compaction is needed.
+ */
+ void checkForCompaction() {
+ int i, numRas;
+ int numDead = 0;
+ int numAlive = 0;
+ RenderAtom ra;
+
+ if (!VirtualUniverse.mc.doCompaction) {
+ return;
+ }
+
+ numRas = renderAtoms.size();
+ for (i=0; i<numRas; i++) {
+ ra = (RenderAtom)renderAtoms.get(i);
+ // If the renderatom has not been visible for "notVisibleCount" then
+ // add it to the deadlist
+ if (ra.lastVisibleTime < removeCutoffTime) {
+ numDead++;
+ }
+
+ }
+ numAlive = numRas - numDead;
+ if (numAlive*2 < numDead) {
+ compact();
+ }
+ }
+
+ /**
+ * This sets the number of frames to render before changing the
+ * removeCutoffTime
+ */
+ void setFrameCountCutoff(int cutoff) {
+ frameCountCutoff = cutoff;
+ }
+
+ /**
+ * This method stores the timestamp of the frame frameCountCuttoff
+ * frames ago. It also does compaction if it is needed.
+ */
+ void compact() {
+ RenderAtom ra;
+
+ for (int i=0; i < renderAtoms.size();) {
+ ra = (RenderAtom)renderAtoms.get(i);
+ if (ra.lastVisibleTime < removeCutoffTime) {
+ renderAtoms.remove(i);
+ removeARenderAtom(ra);
+ continue;
+ }
+ i++;
+ }
+
+ }
+
+ void reEvaluateAlternateAppearance() {
+ AppearanceRetained app;
+ EnvironmentSet e;
+ Object[] retVal;
+ int sz = renderAtoms.size();
+
+ for (int n = 0; n < sz; n++) {
+ RenderAtom ra = (RenderAtom)renderAtoms.get(n);
+ if (!ra.inRenderBin() || !ra.geometryAtom.source.appearanceOverrideEnable)
+ continue;
+
+ retVal = universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+
+ if (app == ra.app)
+ continue;
+
+ if (ra.geometryAtom.source.otherAppearance != app) {
+ if (ra.geometryAtom.source.otherAppearance != null) {
+ ra.geometryAtom.source.otherAppearance.sgApp.removeAMirrorUser(ra.geometryAtom.source);
+ }
+ if (app != ra.geometryAtom.source.appearance) {
+ if (app != null) {
+ app.sgApp.addAMirrorUser(ra.geometryAtom.source);
+ }
+ ra.geometryAtom.source.otherAppearance = app;
+ }
+ else {
+ ra.geometryAtom.source.otherAppearance = null;
+ }
+ }
+ ra.app = app;
+ e = ra.envSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+
+ }
+
+ void reEvaluateAllRenderAtoms(boolean altAppDirty) {
+
+ int sz = renderAtoms.size();
+
+ for (int n = 0; n < sz; n++) {
+ LightRetained[] lights;
+ FogRetained newfog;
+ ModelClipRetained newModelClip;
+ AppearanceRetained app;
+ RenderAtom ra = (RenderAtom)renderAtoms.get(n);
+ Object[] retVal;
+
+ if (!ra.inRenderBin())
+ continue;
+
+ lights = universe.renderingEnvironmentStructure.getInfluencingLights(ra, view);
+ newfog = universe.renderingEnvironmentStructure.getInfluencingFog(ra, view);
+ newModelClip = universe.renderingEnvironmentStructure.getInfluencingModelClip(ra, view);
+
+
+ if (altAppDirty) {
+ if (ra.geometryAtom.source.appearanceOverrideEnable) {
+ retVal = universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+ }
+ else {
+ app = ra.app;
+ }
+
+ // If the lights/fog/model_clip of the render atom is the same
+ // as the old set of lights/fog/model_clip, then move on to the
+ // next renderAtom
+ // TODO: Should app test for equivalent?
+ if (ra.envSet.equals(ra, lights, newfog, newModelClip) &&
+ app == ra.app)
+ continue;
+
+ if (altAppDirty && ra.geometryAtom.source.appearanceOverrideEnable) {
+ if (app != ra.app) {
+ if (ra.geometryAtom.source.otherAppearance != app) {
+ if (ra.geometryAtom.source.otherAppearance != null)
+ ra.geometryAtom.source.otherAppearance.sgApp.removeAMirrorUser(ra.geometryAtom.source);
+ // If it is not the default appearance
+ if (app != ra.geometryAtom.source.appearance) {
+ if (app != null) {
+ app.sgApp.addAMirrorUser(ra.geometryAtom.source);
+ }
+ ra.geometryAtom.source.otherAppearance = app;
+ }
+ else {
+ ra.geometryAtom.source.otherAppearance = null;
+ }
+ }
+ }
+ }
+ getNewEnvironment(ra, lights, newfog, newModelClip, app);
+
+ }
+ }
+
+
+
+ void getNewEnvironment(RenderAtom ra, LightRetained[] lights,
+ FogRetained fog, ModelClipRetained modelClip,
+ AppearanceRetained app) {
+
+ LightBin currentBin, lightBin;
+ EnvironmentSet currentEnvSet, newBin;
+ EnvironmentSet eNew = null;
+ AttributeBin attributeBin;
+ TextureBin textureBin;
+ RenderMolecule renderMolecule;
+ FogRetained newfog;
+ LightBin addBin;
+ OrderedCollection oc = null;
+ int i;
+
+ // Remove this renderAtom from this render Molecule
+ ra.renderMolecule.removeRenderAtom(ra);
+
+ eNew = null;
+ if (ra.geometryAtom.source.geometryBackground == null) {
+ if (ra.geometryAtom.source.orderedPath != null) {
+ oc = findOrderedCollection(ra.geometryAtom, false);
+ currentBin = oc.nextFrameLightBin;
+ addBin = oc.addLightBins;
+ } else {
+ addBin = addOpaqueBin;
+ currentBin= opaqueBin;
+ }
+ } else {
+ if (ra.geometryAtom.source.orderedPath != null) {
+ oc = findOrderedCollection(ra.geometryAtom, true);
+ currentBin = oc.nextFrameLightBin;
+ addBin = oc.addLightBins;
+ } else {
+ addBin = bgAddOpaqueBin;
+ currentBin= bgOpaqueBin;
+ }
+ }
+ lightBin = currentBin;
+
+ while (currentBin != null && eNew == null) {
+
+ // this test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground) {
+
+ currentEnvSet = currentBin.environmentSetList;
+ while (currentEnvSet != null) {
+ if (currentEnvSet.equals(ra, lights, fog, modelClip)) {
+ eNew = currentEnvSet;
+ break;
+ }
+ currentEnvSet = currentEnvSet.next;
+ }
+ // If envSet set is not found
+ // Check the "to-be-added" list of environmentSets for a match
+ if (eNew == null) {
+ int size = currentBin.insertEnvSet.size();
+ for (i = 0; i < size; i++) {
+ newBin = (EnvironmentSet)currentBin.insertEnvSet.get(i);
+ if (newBin.equals(ra, lights, fog, modelClip)) {
+ eNew = newBin;
+ break;
+ }
+ }
+ }
+ }
+ currentBin = currentBin.next;
+ }
+
+ // Now check the to-be added lightbins
+ if (eNew == null) {
+ currentBin = addBin;
+ while (currentBin != null) {
+
+ // this test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground) {
+
+ // Check the "to-be-added" list of environmentSets for a match
+ int size = currentBin.insertEnvSet.size();
+ for (i = 0; i < size; i++) {
+ newBin = (EnvironmentSet)currentBin.insertEnvSet.get(i);
+ if (newBin.equals(ra, lights, fog, modelClip)) {
+ eNew = newBin;
+ break;
+ }
+ }
+ }
+ currentBin = currentBin.next;
+ }
+ }
+
+
+ if (eNew == null) {
+ // Need a new one
+ currentEnvSet = getEnvironmentSet(ra, lights, fog, modelClip);
+ // Find a lightbin that envSet fits into
+ currentBin = lightBin;
+ while (currentBin != null) {
+
+ // the first test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground &&
+ currentBin.willEnvironmentSetFit(currentEnvSet)) {
+
+ // there may be new lights define which needs to
+ // call native updateLight().
+ // When using existing lightBin we have to force
+ // reevaluate Light.
+ for (i=0; i < lights.length; i++) {
+ if (!changedLts.contains(lights[i]))
+ changedLts.add(lights[i]);
+ envDirty |= REEVALUATE_LIGHTS;
+
+ }
+ break;
+ }
+ currentBin = currentBin.next;
+ }
+
+ // Now check the to-be added lightbins
+ if (currentBin == null) {
+ currentBin = addBin;
+ while (currentBin != null) {
+
+ // the first test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground &&
+ currentBin.willEnvironmentSetFit(currentEnvSet)) {
+
+ // there may be new lights define which needs to
+ // call native updateLight().
+ // When using existing lightBin we have to force
+ // reevaluate Light.
+ for (i=0; i < lights.length; i++) {
+ if (!changedLts.contains(lights[i]))
+ changedLts.add(lights[i]);
+ envDirty |= REEVALUATE_LIGHTS;
+
+ }
+ break;
+ }
+ currentBin = currentBin.next;
+ }
+ }
+
+ if (currentBin == null) {
+ // Need a new lightbin
+ currentBin = getLightBin(maxLights,
+ ra.geometryAtom.source.geometryBackground, false);
+ if (addBin != null) {
+ currentBin.next = addBin;
+ addBin.prev = currentBin;
+ }
+ if (ra.geometryAtom.source.orderedPath != null) {
+ if (!oc.onUpdateList) {
+ objUpdateList.add(oc);
+ oc.onUpdateList = true;
+ }
+ oc.addLightBins = currentBin;
+ } else {
+ if (ra.geometryAtom.source.geometryBackground == null)
+ addOpaqueBin = currentBin;
+ else
+ bgAddOpaqueBin = currentBin;
+ }
+
+ }
+ eNew = currentEnvSet;
+ currentBin.addEnvironmentSet(eNew, this);
+
+ }
+ ra.fog = fog;
+ ra.lights = lights;
+ ra.modelClip = modelClip;
+ ra.app = app;
+ reInsertAttributeBin(eNew, ra);
+
+ }
+ void reInsertAttributeBin(EnvironmentSet e, RenderAtom ra) {
+ AttributeBin ab;
+ // Just go up to the environment and re-insert
+ ab = findAttributeBin(e, ra);
+ reInsertTextureBin(ab, ra);
+ }
+
+
+ void reInsertTextureBin(AttributeBin ab, RenderAtom ra) {
+ TextureBin tb;
+
+ tb = findTextureBin(ab, ra);
+ reInsertRenderAtom(tb, ra);
+ }
+
+ void reInsertRenderAtom(TextureBin tb, RenderAtom ra) {
+ RenderMolecule newRm;
+ // Just go up to the texture bin and re-insert
+ newRm = findRenderMolecule(tb, ra);
+ }
+
+ void computeViewFrustumBBox(BoundingBox viewFrustumBBox) {
+ //Initial view frustumBBox BBox
+ viewFrustumBBox.lower.x = Float.POSITIVE_INFINITY;
+ viewFrustumBBox.lower.y = Float.POSITIVE_INFINITY;
+ viewFrustumBBox.lower.z = Float.POSITIVE_INFINITY;
+ viewFrustumBBox.upper.x = Float.NEGATIVE_INFINITY;
+ viewFrustumBBox.upper.y = Float.NEGATIVE_INFINITY;
+ viewFrustumBBox.upper.z = Float.NEGATIVE_INFINITY;
+
+ Canvas3D canvases[] = view.getCanvases();
+ for (int i=0; i< canvases.length; i++) {
+ Canvas3D canvas = canvases[i];
+
+ //Initial view frustumBBox BBox
+ canvasFrustumBBox.lower.x = Float.POSITIVE_INFINITY;
+ canvasFrustumBBox.lower.y = Float.POSITIVE_INFINITY;
+ canvasFrustumBBox.lower.z = Float.POSITIVE_INFINITY;
+ canvasFrustumBBox.upper.x = Float.NEGATIVE_INFINITY;
+ canvasFrustumBBox.upper.y = Float.NEGATIVE_INFINITY;
+ canvasFrustumBBox.upper.z = Float.NEGATIVE_INFINITY;
+
+ canvas.updateViewCache(true, null, canvasFrustumBBox, false);
+
+ if(viewFrustumBBox.lower.x > canvasFrustumBBox.lower.x)
+ viewFrustumBBox.lower.x = canvasFrustumBBox.lower.x;
+ if(viewFrustumBBox.lower.y > canvasFrustumBBox.lower.y)
+ viewFrustumBBox.lower.y = canvasFrustumBBox.lower.y;
+ if(viewFrustumBBox.lower.z > canvasFrustumBBox.lower.z)
+ viewFrustumBBox.lower.z = canvasFrustumBBox.lower.z;
+
+ if(viewFrustumBBox.upper.x < canvasFrustumBBox.upper.x)
+ viewFrustumBBox.upper.x = canvasFrustumBBox.upper.x;
+ if(viewFrustumBBox.upper.y < canvasFrustumBBox.upper.y)
+ viewFrustumBBox.upper.y = canvasFrustumBBox.upper.y;
+ if(viewFrustumBBox.upper.z < canvasFrustumBBox.upper.z)
+ viewFrustumBBox.upper.z = canvasFrustumBBox.upper.z;
+ }
+ }
+
+
+
+ /**
+ * This inserts a RenderAtom into the appropriate bin.
+ */
+ RenderMolecule insertRenderAtom(RenderAtom ra) {
+ LightBin lightBin;
+ EnvironmentSet environmentSet;
+ AttributeBin attributeBin;
+ TextureBin textureBin;
+ RenderMolecule renderMolecule;
+ OrderedCollection oc;
+ AppearanceRetained app;
+ Object[] retVal;
+ GeometryAtom ga = ra.geometryAtom;
+
+ // System.out.println("insertRenderAtom ga " + ra.geometryAtom);
+ // determine if a separate copy of localeVwcBounds is needed
+ // based on the locale info
+
+ if (ra.localeVwcBounds == null) {
+ // Handle multiple locales
+ if (!locale.hiRes.equals(ga.source.locale.hiRes)) {
+ ga.source.locale.hiRes.difference(locale.hiRes,
+ localeTranslation);
+ ra.localeVwcBounds = new BoundingBox();
+ ra.localeVwcBounds.translate(ga.source.vwcBounds,
+ localeTranslation);
+ ra.dirtyMask |= RenderAtom.HAS_SEPARATE_LOCALE_VWC_BOUNDS;
+ }
+ else {
+ ra.dirtyMask &= ~RenderAtom.HAS_SEPARATE_LOCALE_VWC_BOUNDS;
+ ra.localeVwcBounds = ga.source.vwcBounds;
+ }
+ }
+
+
+ // If the appearance is overrideable, then get the
+ // applicable appearance
+ if (ga.source.appearanceOverrideEnable) {
+ retVal = universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ // If its a valid alternate appaearance
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ ra.app = app;
+ if (ga.source.otherAppearance != app) {
+ if (ga.source.otherAppearance != null)
+ ga.source.otherAppearance.sgApp.
+ removeAMirrorUser(ga.source);
+ ga.source.otherAppearance = app;
+ if (app != null)
+ ra.app.sgApp.addAMirrorUser(ga.source);
+ }
+ }
+ else {
+ ra.app = ga.source.appearance;
+
+ }
+ } else {
+ ra.app = ga.source.appearance;
+ }
+ // Call environment set, only after the appearance has been
+ // determined
+ environmentSet = findEnvironmentSet(ra);
+ attributeBin = findAttributeBin(environmentSet, ra);
+ textureBin = findTextureBin(attributeBin, ra);
+ renderMolecule = findRenderMolecule(textureBin, ra);
+ ra.setRenderBin(true);
+ renderAtoms.add(ra);
+
+ if (ga.source instanceof OrientedShape3DRetained) {
+ // dirty initially
+ dirtyOrientedRAs.add(ra);
+ ra.dirtyMask |= RenderAtom.IN_DIRTY_ORIENTED_RAs;
+ ra.dirtyMask |= RenderAtom.IS_ORIENTED;
+ for(int k = 0; k < ra.rListInfo.length; k++) {
+ if (ra.rListInfo[k].localToVworld == null) {
+ ra.rListInfo[k].localToVworld = VirtualUniverse.mc.getTransform3D(null);
+ }
+ }
+ }
+
+ if (renderMolecule.primaryMoleculeType ==
+ RenderMolecule.TEXT3D_MOLECULE) {
+ if (!ra.onUpdateList()) {
+ ra.dirtyMask |= RenderAtom.ON_UPDATELIST;
+ objUpdateList.add(ra);
+ }
+ }
+
+ // ra.needSeparateLocaleVwcBounds flag is determined in
+ // RenderMolecule.addRenderAtom based on the render method type.
+ // That's why the localeVwcBounds has to be reevaluated here again
+ // If after compaction being added in, then we just need to
+ // set the updated vwcBounds, there is no need to allocate
+ if (ra.needSeparateLocaleVwcBounds()) {
+ if (!ra.hasSeparateLocaleVwcBounds()) {
+ ra.dirtyMask |= RenderAtom.HAS_SEPARATE_LOCALE_VWC_BOUNDS;
+ ra.localeVwcBounds = new BoundingBox(ga.source.vwcBounds);
+ ra.dirtyMask |= ra.ON_LOCALE_VWC_BOUNDS_UPDATELIST;
+ raLocaleVwcBoundsUpdateList.add(ra);
+ }
+ else {
+ ra.localeVwcBounds.set(ga.source.vwcBounds);
+ ra.dirtyMask |= ra.ON_LOCALE_VWC_BOUNDS_UPDATELIST;
+ raLocaleVwcBoundsUpdateList.add(ra);
+ }
+ }
+ return (renderMolecule);
+ }
+
+ OrderedCollection findOrderedCollection(GeometryAtom ga,
+ boolean doBackground) {
+ int i, n;
+ int oi; // an id which identifies a children of the orderedGroup
+ int ci; // child index of the ordered group
+ int index;
+ ArrayList list = null;
+ int val;
+
+ OrderedGroupRetained og;
+ OrderedCollection oc = null;
+ ArrayList ocs;
+ ArrayList parentChildOrderedBins;
+ OrderedBin parentOrderedBin;
+ int parentOrderedChildId;
+ OrderedBin ob;
+ OrderedPathElement ope;
+
+ // Since the table has been incremented, in response to OG addition,
+ // but the ordered collecyions has not been added yet, we need to
+ // check what the original index into the ordered collection
+ // should be
+ int adjustment;
+
+ if (doBackground) {
+ parentChildOrderedBins = bgOrderedBins;
+ } else {
+ parentChildOrderedBins = orderedBins;
+ }
+
+ parentOrderedBin = null;
+ parentOrderedChildId = -1;
+
+ for (i=0; i<ga.source.orderedPath.pathElements.size(); i++) {
+ ope = (OrderedPathElement)ga.source.orderedPath.pathElements.get(i);
+ og = ope.orderedGroup;
+ oi = ope.childId.intValue();
+
+ ob = og.getOrderedBin(view.viewIndex);
+ if (ob == null) {
+ // create ordered bin tree
+ ob = new OrderedBin(og.childCount, og);
+ og.setOrderedBin(ob, view.viewIndex);
+
+ index = -1;
+ for (n = 0; n < orderedBinsList.size(); n++) {
+ if (parentChildOrderedBins == orderedBinsList.get(n)) {
+ index = n;
+ break;
+ }
+
+ }
+ if (index == -1) {
+ orderedBinsList.add(parentChildOrderedBins);
+ list = new ArrayList(5);
+ list.add(ob);
+ toBeAddedBinList.add(list);
+ }
+ else {
+ list = (ArrayList)toBeAddedBinList.get(index);
+ list.add(ob);
+ }
+ }
+ ocs = ob.orderedCollections;
+ OrderedChildInfo cinfo = ob.lastChildInfo;
+ boolean found = false;
+ // Check if a oc is already creates for this oi
+ // Start from the last child that was added and work backwards,
+ // in case the child
+ // was added and removed and then added back the same frame, we get the
+ // correct oc
+ while (cinfo != null && !found) {
+ if (cinfo.type == OrderedChildInfo.ADD) {
+ if (cinfo.orderedId == oi) {
+ oc = cinfo.value;
+ if (oc == null) {
+ oc = new OrderedCollection();
+ cinfo.value = oc;
+ }
+ found = true;
+ }
+ }
+ cinfo = cinfo.prev;
+ }
+ // If we are in the update_view case then check the oi
+ // exists in the setOCForOI list ..
+ for (n = 0; n < ob.setOCForOI.size(); n++) {
+ val = ((Integer)ob.setOCForOI.get(n)).intValue();
+ if (oi == val) {
+ oc = (OrderedCollection)ob.valueOfSetOCForOI.get(n);
+ found = true;
+ }
+ }
+ // The list is not going to be modified by any additions
+ // that have happened ...
+ // Then this child must exists from the previous frame, so
+ // get the location
+ if (!found) {
+ // The case below happens when there have been some insert
+ // ordered nodes, but update_view happens later and
+ // so the earlier insert ordered nodes are not
+ // seen by renderBin!
+ if (og.orderedChildIdTable == null || oi >= og.orderedChildIdTable.length) {
+ // Create a new category that adds Info based only on oi
+ // which will be added to the orderedBin after the
+ // idTable reflects the correct childId for the next frame
+ ob.setOCForOI.add(new Integer(oi));
+ oc = new OrderedCollection();
+ ob.valueOfSetOCForOI.add(oc);
+ if (!ob.onUpdateList) {
+ obList.add(ob);
+ ob.onUpdateList = true;
+ }
+ }
+ else {
+ ci = og.orderedChildIdTable[oi];
+
+ for (n = 0; n < ob.setOCForCI.size(); n++) {
+ val = ((Integer)ob.setOCForCI.get(n)).intValue();
+ if (val == ci) {
+
+ oc=(OrderedCollection)ob.valueOfSetOCForCI.get(n);
+ if (oc == null) {
+ oc = new OrderedCollection();
+ ob.valueOfSetOCForCI.set(n, oc);
+ }
+
+ break;
+ }
+ }
+ if (n == ob.setOCForCI.size()) {
+ oc = (OrderedCollection)ocs.get(ci);
+ if (oc == null) {
+ oc = new OrderedCollection();
+ ob.setOCForCI.add(new Integer(ci));
+ ob.valueOfSetOCForCI.add(oc);
+ if (!ob.onUpdateList) {
+ obList.add(ob);
+ ob.onUpdateList = true;
+ }
+ }
+ }
+ }
+ }
+ if (oc.nextFrameLightBin == null) {
+ oc.nextFrameLightBin = getLightBin(maxLights,
+ ga.source.geometryBackground, false);
+ oc.nextFrameLightBin.setOrderedInfo(oc);
+
+ if (!oc.onUpdateList) {
+ objUpdateList.add(oc);
+ oc.onUpdateList = true;
+ }
+ }
+
+ parentChildOrderedBins = oc.childOrderedBins;
+ parentOrderedBin = ob;
+ parentOrderedChildId = oi;
+ }
+ return (oc);
+ }
+
+ void removeOrderedHeadLightBin(LightBin lightBin) {
+ int i, k;
+ int oi; // an id which identifies a children of the orderedGroup
+ int ci; // child index of the ordered group
+ ArrayList ocs;
+ OrderedCollection oc;
+ OrderedBin ob, savedOb;
+ int n, val;
+
+
+
+ oc = lightBin.orderedCollection;
+
+ oc.lightBin = lightBin.next;
+ oc.nextFrameLightBin = oc.lightBin;
+
+
+
+ if (oc.lightBin != null) {
+ // Make this lightBin the head of the lightBin;
+ oc.lightBin.prev = null;
+ oc.lightBin.orderedCollection = oc;
+ }
+
+
+ }
+
+
+ /**
+ * This gets a new EnviornmentSet. It creates one if there are none
+ * on the freelist.
+ */
+ EnvironmentSet getEnvironmentSet(RenderAtom ra, LightRetained[] lights,
+ FogRetained fog, ModelClipRetained modelClip) {
+ EnvironmentSet envSet;
+
+ if (envSetFreelist.size() > 0) {
+ envSet = (EnvironmentSet)envSetFreelist.remove(
+ envSetFreelist.size()-1);
+ envSet.reset(ra, lights, fog, modelClip);
+ } else {
+ envSet = new EnvironmentSet(ra, lights, fog, modelClip, this);
+ }
+ return (envSet);
+ }
+ /**
+ * This finds or creates an AttributeBin for a given RenderAtom.
+ */
+ AttributeBin findAttributeBin(EnvironmentSet envSet, RenderAtom ra) {
+ int i;
+ AttributeBin currentBin;
+ RenderingAttributesRetained renderingAttributes;
+ if (ra.app == null) {
+ renderingAttributes = null;
+ } else {
+ renderingAttributes = ra.app.renderingAttributes;
+ }
+
+
+ currentBin = envSet.attributeBinList;
+ while (currentBin != null) {
+ if (currentBin.equals(renderingAttributes, ra)) {
+ return(currentBin);
+ }
+ currentBin = currentBin.next;
+ }
+
+ // Check the "to-be-added" list of attributeBins for a match
+ for (i = 0; i < envSet.addAttributeBins.size(); i++) {
+ currentBin = (AttributeBin)envSet.addAttributeBins.get(i);
+ if (currentBin.equals(renderingAttributes, ra)) {
+ return(currentBin);
+ }
+ }
+
+ currentBin = getAttributeBin(ra.app, renderingAttributes);
+ envSet.addAttributeBin(currentBin, this);
+ return(currentBin);
+ }
+
+ /**
+ * This finds or creates a TextureBin for a given RenderAtom.
+ */
+ TextureBin findTextureBin(AttributeBin attributeBin, RenderAtom ra) {
+
+
+ int i;
+ TextureBin currentBin;
+ TextureRetained texture;
+ TextureUnitStateRetained texUnitState[];
+
+ RenderingAttributesRetained rAttrs =
+ (ra.geometryAtom.source.appearance == null)? null:
+ ra.geometryAtom.source.appearance.renderingAttributes;
+
+ if (ra.app == null) {
+ texUnitState = null;
+ } else {
+ texUnitState = ra.app.texUnitState;
+ }
+
+ currentBin = attributeBin.textureBinList;
+ while (currentBin != null) {
+ if (currentBin.equals(texUnitState, ra)) {
+ //System.out.println("1: Equal");
+ return(currentBin);
+ }
+ currentBin = currentBin.next;
+ }
+ // Check the "to-be-added" list of TextureBins for a match
+ for (i = 0; i < attributeBin.addTBs.size(); i++) {
+ currentBin = (TextureBin)attributeBin.addTBs.get(i);
+ if (currentBin.equals(texUnitState, ra)) {
+ //System.out.println("2: Equal");
+ return(currentBin);
+ }
+ }
+ // get a new texture bin for this texture unit state
+ currentBin = getTextureBin(texUnitState, ra.app);
+ attributeBin.addTextureBin(currentBin, this, ra);
+ return(currentBin);
+ }
+
+ /**
+ * This finds or creates a RenderMolecule for a given RenderAtom.
+ */
+ RenderMolecule findRenderMolecule(TextureBin textureBin,
+ RenderAtom ra) {
+
+ RenderMolecule currentBin;
+ PolygonAttributesRetained polygonAttributes;
+ LineAttributesRetained lineAttributes;
+ PointAttributesRetained pointAttributes;
+ MaterialRetained material;
+ ColoringAttributesRetained coloringAttributes;
+ TransparencyAttributesRetained transparencyAttributes;
+ int i;
+ ArrayList list;
+ TextureUnitStateRetained texUnitState[];
+ RenderingAttributesRetained renderingAttributes;
+ HashMap rmap = null, addmap = null;
+
+ if (ra.app == null) {
+ polygonAttributes = null;
+ lineAttributes = null;
+ pointAttributes = null;
+ material = null;
+ coloringAttributes = null;
+ transparencyAttributes = null;
+ renderingAttributes = null;
+ texUnitState = null;
+ } else {
+ polygonAttributes = ra.app.polygonAttributes;
+ lineAttributes = ra.app.lineAttributes;
+ pointAttributes = ra.app.pointAttributes;
+ material = ra.app.material;
+ coloringAttributes = ra.app.coloringAttributes;
+ transparencyAttributes = ra.app.transparencyAttributes;
+ renderingAttributes = ra.app.renderingAttributes;
+ texUnitState = ra.app.texUnitState;
+ }
+
+ // Get the renderMoleculelist for this xform
+ if (ra.isOpaque()) {
+ rmap = textureBin.opaqueRenderMoleculeMap;
+ addmap = textureBin.addOpaqueRMs;
+ }
+ else {
+ rmap = textureBin.transparentRenderMoleculeMap;
+ addmap = textureBin.addTransparentRMs;
+ }
+ currentBin = (RenderMolecule)rmap.get(ra.geometryAtom.source.localToVworld[0]);
+
+ while (currentBin != null) {
+ if (currentBin.equals(ra,
+ polygonAttributes, lineAttributes,
+ pointAttributes, material,
+ coloringAttributes,
+ transparencyAttributes,
+ ra.geometryAtom.source.localToVworld[0])) {
+
+ currentBin.addRenderAtom(ra, this);
+ ra.envSet = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ // If the locale has changed for an existing renderMolecule
+ // handle the RmlocaleToVworld
+ return(currentBin);
+ }
+ currentBin = currentBin.next;
+ }
+ // Check the "to-be-added" list of renderMolecules for a match
+ if ((list = (ArrayList)addmap.get(ra.geometryAtom.source.localToVworld[0])) != null) {
+ for (i = 0; i < list.size(); i++) {
+ currentBin = (RenderMolecule)list.get(i);
+ if (currentBin.equals(ra,
+ polygonAttributes, lineAttributes,
+ pointAttributes, material,
+ coloringAttributes,
+ transparencyAttributes,
+ ra.geometryAtom.source.localToVworld[0])) {
+ currentBin.addRenderAtom(ra, this);
+ return(currentBin);
+ }
+ }
+ }
+
+
+ currentBin = getRenderMolecule(ra.geometryAtom,
+ polygonAttributes,
+ lineAttributes,
+ pointAttributes,
+ material,
+ coloringAttributes,
+ transparencyAttributes,
+ renderingAttributes,
+ texUnitState,
+ ra.geometryAtom.source.localToVworld[0],
+ ra.geometryAtom.source.localToVworldIndex[0]);
+ textureBin.addRenderMolecule(currentBin, this);
+ currentBin.addRenderAtom(ra, this);
+ return(currentBin);
+ }
+
+
+ /**
+ * This gets a new AttributeBin. It creates one if there are none
+ * on the freelist.
+ */
+ AttributeBin getAttributeBin(AppearanceRetained app, RenderingAttributesRetained ra) {
+ AttributeBin attrBin;
+ if (attrBinFreelist.size() > 0) {
+ attrBin = (AttributeBin)attrBinFreelist.remove(
+ attrBinFreelist.size()-1);
+ attrBin.reset(app, ra, this);
+ } else {
+
+
+ attrBin = new AttributeBin(app, ra, this);
+ }
+ return (attrBin);
+ }
+
+ /**
+ * This gets a new LightBin. It creates one if there are none
+ * on the freelist.
+ */
+ LightBin getLightBin(int maxLights, BackgroundRetained bg, boolean inOpaque) {
+ LightBin lightBin;
+
+ if (lightBinFreelist.size() > 0) {
+ lightBin = (LightBin)lightBinFreelist.remove(
+ lightBinFreelist.size()-1);
+ lightBin.reset(inOpaque);
+ } else {
+ lightBin = new LightBin(maxLights, this, inOpaque);
+ }
+ lightBin.geometryBackground = bg;
+ return (lightBin);
+ }
+
+ /**
+ * This gets a new TextureBin. It creates one if there are none
+ * on the freelist.
+ */
+ TextureBin getTextureBin(TextureUnitStateRetained texUnitState[],
+ AppearanceRetained app) {
+ TextureBin textureBin;
+
+ if (textureBinFreelist.size() > 0) {
+ textureBin = (TextureBin)textureBinFreelist.remove(
+ textureBinFreelist.size()-1);
+ textureBin.reset(texUnitState, app);
+ } else {
+ textureBin = new TextureBin(texUnitState, app, this);
+ }
+
+ return (textureBin);
+ }
+
+ /**
+ * This gets a new RenderMolecule. It creates one if there are none
+ * on the freelist.
+ */
+ RenderMolecule getRenderMolecule(GeometryAtom ga,
+ PolygonAttributesRetained polya,
+ LineAttributesRetained linea,
+ PointAttributesRetained pointa,
+ MaterialRetained material,
+ ColoringAttributesRetained cola,
+ TransparencyAttributesRetained transa,
+ RenderingAttributesRetained ra,
+ TextureUnitStateRetained[] texUnits,
+ Transform3D[] transform,
+ int[] transformIndex) {
+ RenderMolecule renderMolecule;
+
+ if (renderMoleculeFreelist.size() > 0) {
+ renderMolecule = (RenderMolecule)renderMoleculeFreelist.remove(
+ renderMoleculeFreelist.size()-1);
+ renderMolecule.reset(ga, polya, linea, pointa, material,
+ cola, transa, ra, texUnits, transform, transformIndex);
+ } else {
+ renderMolecule = new RenderMolecule(ga, polya, linea, pointa,
+ material, cola, transa, ra,
+ texUnits,
+ transform, transformIndex, this);
+ }
+ return (renderMolecule);
+ }
+
+
+ /**
+ * This finds or creates an EnviornmentSet for a given RenderAtom.
+ * This also deals with empty LightBin lists.
+ */
+ EnvironmentSet findEnvironmentSet(RenderAtom ra) {
+ LightBin currentBin, lightBin ;
+ EnvironmentSet currentEnvSet, newBin;
+ int i;
+ LightBin addBin = null;
+ OrderedCollection oc = null;
+
+ if (ra.geometryAtom.source.geometryBackground == null) {
+ if (ra.geometryAtom.source.orderedPath != null) {
+ oc = findOrderedCollection(ra.geometryAtom, false);
+ currentBin = oc.nextFrameLightBin;
+ addBin = oc.addLightBins;
+ } else {
+ currentBin = opaqueBin;
+ addBin = addOpaqueBin;
+ }
+ } else {
+ if (ra.geometryAtom.source.orderedPath != null) {
+ oc = findOrderedCollection(ra.geometryAtom, true);
+ currentBin = oc.nextFrameLightBin;
+ addBin = oc.addLightBins;
+ } else {
+ currentBin = bgOpaqueBin;
+ addBin = bgAddOpaqueBin;
+
+ }
+ }
+ lightBin = currentBin;
+
+
+ ra.lights = universe.renderingEnvironmentStructure.
+ getInfluencingLights(ra, view);
+ ra.fog = universe.renderingEnvironmentStructure.
+ getInfluencingFog(ra, view);
+ ra.modelClip = universe.renderingEnvironmentStructure.
+ getInfluencingModelClip(ra, view);
+
+ while (currentBin != null) {
+ // this test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground) {
+
+ currentEnvSet = currentBin.environmentSetList;
+ while (currentEnvSet != null) {
+ if (currentEnvSet.equals(ra, ra.lights, ra.fog, ra.modelClip)) {
+ return(currentEnvSet);
+ }
+ currentEnvSet = currentEnvSet.next;
+ }
+ // Check the "to-be-added" list of environmentSets for a match
+ for (i = 0; i < currentBin.insertEnvSet.size(); i++) {
+ newBin = (EnvironmentSet)currentBin.insertEnvSet.get(i);
+ if (newBin.equals(ra, ra.lights, ra.fog, ra.modelClip)) {
+ return(newBin);
+ }
+ }
+ }
+ currentBin = currentBin.next;
+ }
+
+ // Now check the to-be added lightbins
+ currentBin = addBin;
+ while (currentBin != null) {
+
+ // this following test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground) {
+
+ // Check the "to-be-added" list of environmentSets for a match
+ for (i = 0; i < currentBin.insertEnvSet.size(); i++) {
+ newBin = (EnvironmentSet)currentBin.insertEnvSet.get(i);
+ if (newBin.equals(ra, ra.lights, ra.fog, ra.modelClip)) {
+ return(newBin);
+ }
+ }
+ }
+ currentBin = currentBin.next;
+ }
+
+
+ // Need a new one
+ currentEnvSet = getEnvironmentSet(ra, ra.lights, ra.fog, ra.modelClip);
+ currentBin = lightBin;
+
+ // Find a lightbin that envSet fits into
+ while (currentBin != null) {
+
+ // the first test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground &&
+ currentBin.willEnvironmentSetFit(currentEnvSet)) {
+ break;
+ }
+ currentBin = currentBin.next;
+ }
+
+ // Now check the to-be added lightbins
+ if (currentBin == null) {
+ currentBin = addBin;
+ while (currentBin != null) {
+
+ // the first test is always true for non-backgroundGeo bins
+ if (currentBin.geometryBackground ==
+ ra.geometryAtom.source.geometryBackground &&
+ currentBin.willEnvironmentSetFit(currentEnvSet)) {
+
+ break;
+ }
+ currentBin = currentBin.next;
+ }
+ }
+
+ if (currentBin == null) {
+ // Need a new lightbin
+ currentBin = getLightBin(maxLights,
+ ra.geometryAtom.source.geometryBackground, false);
+ if (addBin != null) {
+ currentBin.next = addBin;
+ addBin.prev = currentBin;
+ }
+ if (ra.geometryAtom.source.orderedPath != null) {
+ if (!oc.onUpdateList) {
+ objUpdateList.add(oc);
+ oc.onUpdateList = true;
+ }
+ oc.addLightBins = currentBin;
+ } else {
+ if (ra.geometryAtom.source.geometryBackground == null)
+ addOpaqueBin = currentBin;
+ else
+ bgAddOpaqueBin = currentBin;
+ }
+ }
+
+ currentBin.addEnvironmentSet(currentEnvSet, this);
+ return (currentEnvSet);
+ }
+
+ void removeLightBin(LightBin lbin) {
+ if (lbin.prev == null) { // At the head of the list
+
+ if (lbin.orderedCollection != null)
+ removeOrderedHeadLightBin(lbin);
+
+ if (lbin.geometryBackground == null) {
+ if (opaqueBin == lbin) {
+ opaqueBin = lbin.next;
+ }
+ } else {
+ if (bgOpaqueBin == lbin) {
+ bgOpaqueBin = lbin.next;
+ }
+ }
+ if (lbin.next != null) {
+ lbin.next.prev = null;
+ }
+ } else { // In the middle or at the end.
+ lbin.prev.next = lbin.next;
+ if (lbin.next != null) {
+ lbin.next.prev = lbin.prev;
+ }
+ }
+ Canvas3D canvases[] = view.getCanvases();
+ for (int i = 0; i < canvases.length; i++) {
+ // Mark the environmentSet cached by all the canvases as null
+ // to force to reEvaluate when it comes back from the freelist
+ // During LightBin::render(), we only check for the pointers not
+ // being the same, so we need to take care of the env set
+ // gotten from the freelist from one frame to another
+ canvases[i].lightBin = null;
+ }
+ lightBinFreelist.add(lbin);
+ lbin.prev = null;
+ lbin.next = null;
+ }
+
+ void addDisplayListResourceFreeList(RenderMolecule rm) {
+ displayListResourceFreeList.add(rm.displayListIdObj);
+ }
+
+ /**
+ * This renders the background scene graph.
+ */
+ void renderBackground(Canvas3D cv) {
+ LightBin currentBin;
+ boolean savedDepthBufferWriteEnable;
+
+ cv.setDepthBufferWriteEnableOverride(true);
+ savedDepthBufferWriteEnable = cv.depthBufferWriteEnable;
+ cv.setDepthBufferWriteEnable(false);
+ // render background opaque
+ currentBin = bgOpaqueBin;
+ while (currentBin != null) {
+ if (currentBin.geometryBackground == geometryBackground)
+ currentBin.render(cv);
+ currentBin = currentBin.next;
+ }
+
+ // render background ordered
+ if (bgOrderedBins.size() > 0) {
+ renderOrderedBins(cv, bgOrderedBins, true);
+ }
+
+ TransparentRenderingInfo tinfo = bgTransparentInfo;
+ while (tinfo != null) {
+ tinfo.render(cv);
+ tinfo = tinfo.next;
+ }
+ cv.setDepthBufferWriteEnableOverride(false);
+ cv.setDepthBufferWriteEnable(savedDepthBufferWriteEnable);
+ }
+
+ /**
+ * This renders the opaque objects
+ */
+ void renderOpaque(Canvas3D cv) {
+ LightBin currentBin = opaqueBin;
+ // System.out.println("========> renderOpaque");
+ while (currentBin != null) {
+ // System.out.println("====> rendering Opaque Bin ");
+ currentBin.render(cv);
+ currentBin = currentBin.next;
+ }
+
+ }
+
+ /**
+ * This renders the transparent objects
+ */
+ void renderTransparent(Canvas3D cv) {
+ boolean savedDepthBufferWriteEnable = true;
+
+ TransparentRenderingInfo tinfo = transparentInfo;
+ if (tinfo != null) {
+ // System.out.println("====> rendering transparent Bin");
+
+ if (cv.view.depthBufferFreezeTransparent) {
+ cv.setDepthBufferWriteEnableOverride(true);
+ savedDepthBufferWriteEnable = cv.depthBufferWriteEnable;
+ cv.setDepthBufferWriteEnable(false);
+ }
+
+ if (transpSortMode == View.TRANSPARENCY_SORT_NONE) {
+ while (tinfo != null) {
+ tinfo.render(cv);
+ tinfo = tinfo.next;
+ }
+ }
+ else if (transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) {
+ while (tinfo != null ) {
+ tinfo.sortRender(cv);
+ tinfo = tinfo.next;
+ }
+ }
+ if (cv.view.depthBufferFreezeTransparent) {
+ cv.setDepthBufferWriteEnableOverride(false);
+ cv.setDepthBufferWriteEnable(savedDepthBufferWriteEnable);
+ }
+ }
+ }
+
+ /**
+ * This renders the ordered objects
+ */
+ void renderOrdered(Canvas3D cv) {
+ // System.out.println("******renderOrdered, orderedBins.size() = "+orderedBins.size()+" RenderBin = "+this);
+ if (orderedBins.size() > 0)
+ renderOrderedBins(cv, orderedBins, false);
+ }
+
+ void renderOrderedBins(Canvas3D cv, ArrayList bins, boolean doInfinite) {
+ int sz = bins.size();
+
+ for (int i=0; i <sz; i++) {
+ renderOrderedBin(cv,
+ (OrderedBin) bins.get(i),
+ doInfinite);
+ }
+ }
+
+ void renderOrderedBin(Canvas3D cv, OrderedBin orderedBin,
+ boolean doInfinite) {
+ int i, index;
+ LightBin currentBin;
+ OrderedCollection oc;
+ boolean depthBufferEnable = true;
+ OrderedGroupRetained og = orderedBin.source;
+ boolean isDecal = (og instanceof DecalGroupRetained) &&
+ ((cv.extensionsSupported & Canvas3D.STENCIL_BUFFER) != 0);
+
+ int size = orderedBin.orderedCollections.size();
+
+ // System.out.println("RB : orderedBin.orderedCollections.size() " + size);
+ for (i=0; i<size; i++) {
+ if((og != null) && (og.childIndexOrder != null)) {
+ index = og.childIndexOrder[i];
+ }
+ else {
+ index = i;
+ }
+ oc = (OrderedCollection)orderedBin.orderedCollections.get(index);
+ if (isDecal) {
+ if (index==0) { // first child
+ cv.setDepthBufferEnableOverride(true);
+ depthBufferEnable = cv.decal1stChildSetup(cv.ctx);
+ } else if (index==1) { // second child
+ // decalNthChildSetup will disable depth test
+ cv.decalNthChildSetup(cv.ctx);
+ }
+ }
+ if (oc != null) {
+ currentBin = oc.lightBin;
+ while (currentBin != null) {
+ if (!doInfinite ||
+ currentBin.geometryBackground == geometryBackground) {
+ currentBin.render(cv);
+ }
+ currentBin = currentBin.next;
+ }
+ renderOrderedBins(cv, oc.childOrderedBins, doInfinite);
+ }
+ }
+ if (isDecal) { // reset
+ cv.decalReset(cv.ctx, depthBufferEnable);
+ cv.setDepthBufferEnableOverride(false);
+ }
+ }
+
+
+ /**
+ * Sets the new background color.
+ */
+ void setBackground(BackgroundRetained back) {
+
+ boolean cvDirty = false;
+ BackgroundRetained oldGeomBack = geometryBackground;
+ geometryBackground = null;
+
+ if (back != null) {
+ background.initColor(back.color);
+ background.initImageScaleMode(back.imageScaleMode);
+ background.geometryBranch = back.geometryBranch;
+ if (background.geometryBranch != null) {
+ geometryBackground = back;
+ }
+ if (background.image != null) {
+ if (background.image.isByReference())
+ removeNodeComponent(background.image);
+ }
+ if (back.image != null) {
+ // May need to optimize later
+ background.initImage((ImageComponent2D)back.image.source);
+ if (back.image.isByReference()) {
+ addNodeComponent(back.image);
+ }
+ } else {
+ background.initImage(null);
+ }
+ if (oldGeomBack == null) {
+ cvDirty = true;
+ }
+ } else {
+ background.initColor(black);
+ background.geometryBranch = null;
+ background.initImage(null);
+ if (oldGeomBack != null) {
+ cvDirty = true;
+ }
+ }
+
+ // Need to reEvaluate View cache since doInfinite
+ // flag is changed in Renderer.updateViewCache()
+ Canvas3D canvases[] = view.getCanvases();
+ for (int i=0; i< canvases.length; i++) {
+ Canvas3D canvas = canvases[i];
+ if(cvDirty)
+ canvas.cvDirtyMask |= Canvas3D.BACKGROUND_DIRTY;
+ canvas.cvDirtyMask |= Canvas3D.BACKGROUND_IMAGE_DIRTY;
+ }
+ }
+
+
+ void reEvaluateFog(ArrayList fogs, boolean updateDirty,
+ boolean altAppDirty) {
+ EnvironmentSet e;
+ RenderAtom ra;
+ FogRetained newfog;
+ int i, j, n;
+ AppearanceRetained app;
+ Object[] retVal;
+
+ int sz = renderAtoms.size();
+ for (i = 0; i < sz; i++) {
+ ra = (RenderAtom)renderAtoms.get(i);
+ if (!ra.inRenderBin())
+ continue;
+
+ newfog = universe.renderingEnvironmentStructure.getInfluencingFog(ra, view);
+ // If the fog of the render atom is the same
+ // as the old fog, then move on to the
+ // next renderAtom
+ if (altAppDirty&&ra.geometryAtom.source.appearanceOverrideEnable) {
+ retVal = universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+
+ if (app == ra.app) {
+ if (ra.envSet.fog == newfog)
+ continue;
+ else {
+ getNewEnvironment(ra, ra.lights, newfog, ra.modelClip, ra.app);
+ }
+ }
+ else {
+ if (ra.geometryAtom.source.otherAppearance != app) {
+ if (ra.geometryAtom.source.otherAppearance != null)
+ ra.geometryAtom.source.otherAppearance.sgApp.removeAMirrorUser(ra.geometryAtom.source);
+ if (app != ra.geometryAtom.source.appearance) {
+ if (app != null) {
+ app.sgApp.addAMirrorUser(ra.geometryAtom.source);
+ }
+ ra.geometryAtom.source.otherAppearance = app;
+ }
+ else {
+ ra.geometryAtom.source.otherAppearance = null;
+ }
+ }
+
+ if (ra.envSet.fog == newfog) {
+ ra.app = app;
+ e = ra.envSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ else {
+ getNewEnvironment(ra, ra.lights, newfog, ra.modelClip,
+ app);
+ }
+ }
+ }
+ else {
+ if (ra.envSet.fog == newfog)
+ continue;
+ getNewEnvironment(ra, ra.lights, newfog, ra.modelClip, ra.app);
+ };
+ }
+
+ // Only done for new fogs added to the system
+ if (updateDirty)
+ updateCanvasForDirtyFog(fogs);
+ }
+
+
+ void updateCanvasForDirtyFog(ArrayList fogs) {
+ int i, j;
+ EnvironmentSet e;
+ UnorderList list;
+ EnvironmentSet envsets[];
+ int envsize;
+ int sz = fogs.size();
+
+ for (i = 0; i < sz; i++) {
+ FogRetained fog = (FogRetained)fogs.get(i);
+ list = fog.environmentSets;
+ synchronized (list) {
+ envsize = list.size();
+ envsets = (EnvironmentSet []) list.toArray(false);
+ for (j = 0; j < envsize; j++) {
+ e = envsets[j];
+ e.canvasDirty |= Canvas3D.FOG_DIRTY;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ }
+ }
+ }
+ }
+
+ void reEvaluateModelClip(ArrayList modelClips,
+ boolean updateDirty,
+ boolean altAppDirty) {
+ EnvironmentSet e;
+ RenderAtom ra;
+ ModelClipRetained newModelClip;
+ int i, j, n;
+ AppearanceRetained app;
+ Object[] retVal;
+ int sz = renderAtoms.size();
+ for (i = 0; i < sz; i++) {
+ ra = (RenderAtom)renderAtoms.get(i);
+ if (!ra.inRenderBin())
+ continue;
+
+ newModelClip =
+ universe.renderingEnvironmentStructure.getInfluencingModelClip(ra, view);
+
+ // If the model clip of the render atom is the same
+ // as the old model clip, then move on to the
+ // next renderAtom
+ if (altAppDirty&&ra.geometryAtom.source.appearanceOverrideEnable) {
+ retVal = universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+
+ if (app == ra.app) {
+ if (ra.envSet.modelClip == newModelClip)
+ continue;
+ else {
+ getNewEnvironment(ra, ra.lights, ra.fog,
+ ra.envSet.modelClip, ra.app);
+ }
+ }
+ else {
+ if (ra.geometryAtom.source.otherAppearance != app) {
+ if (ra.geometryAtom.source.otherAppearance != null)
+ ra.geometryAtom.source.otherAppearance.sgApp.removeAMirrorUser(ra.geometryAtom.source);
+ if (app != ra.geometryAtom.source.appearance) {
+ if (app != null) {
+ app.sgApp.addAMirrorUser(ra.geometryAtom.source);
+ }
+ ra.geometryAtom.source.otherAppearance = app;
+ }
+ else {
+ ra.geometryAtom.source.otherAppearance = null;
+ }
+ }
+ if (ra.envSet.modelClip == newModelClip) {
+ ra.app = app;
+ e = ra.envSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ else {
+
+ getNewEnvironment(ra, ra.lights, ra.fog, newModelClip,
+ app);
+ }
+ }
+ }
+ else {
+ if (ra.envSet.modelClip == newModelClip)
+ continue;
+ getNewEnvironment(ra, ra.lights, ra.fog, newModelClip, ra.app);
+ };
+ }
+
+ // Only done for new modelClip added to the system
+ if (updateDirty)
+ updateCanvasForDirtyModelClip(modelClips);
+ }
+
+
+ void updateCanvasForDirtyModelClip(ArrayList modelClips) {
+ int i, j;
+ EnvironmentSet e;
+ int enableMCMaskCache = 0;
+ UnorderList list;
+ EnvironmentSet envsets[];
+ int sz = modelClips.size();
+ int envsize;
+
+ for (i = 0; i < sz; i++) {
+ ModelClipRetained modelClip = (ModelClipRetained)modelClips.get(i);
+
+ // evaluate the modelClip enable mask
+ enableMCMaskCache = 0;
+ for (j = 0; j < 6; j++) {
+ if (modelClip.enables[j])
+ enableMCMaskCache |= 1 << j;
+ }
+ list = modelClip.environmentSets;
+ synchronized (list) {
+ envsize = list.size();
+ envsets = (EnvironmentSet []) list.toArray(false);
+ for (j = 0; j < envsize; j++) {
+ e = envsets[j];
+ e.canvasDirty |= Canvas3D.MODELCLIP_DIRTY;
+ e.enableMCMaskCache = enableMCMaskCache;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ }
+ }
+ }
+ }
+
+ void reEvaluateLights(boolean altAppDirty) {
+ EnvironmentSet e;
+ RenderAtom ra;
+ LightRetained[] lights;
+ int i, n;
+ AppearanceRetained app;
+ Object[] retVal;
+ int sz = renderAtoms.size();
+ for (i = 0; i < sz; i++) {
+ ra = (RenderAtom)renderAtoms.get(i);
+ if (!ra.inRenderBin())
+ continue;
+
+ lights = universe.renderingEnvironmentStructure.getInfluencingLights(ra, view);
+ // If the lights of the render atom is the same
+ // as the old set of lights, then move on to the
+ // next renderAtom
+ if (altAppDirty&&ra.geometryAtom.source.appearanceOverrideEnable) {
+ retVal = universe.renderingEnvironmentStructure.getInfluencingAppearance(ra, view);
+ if (retVal[0] == Boolean.TRUE) {
+ app = (AppearanceRetained)retVal[1];
+ }
+ else {
+ app = ra.geometryAtom.source.appearance;
+ }
+
+ if (app == ra.app) {
+ if (ra.lights == lights || ra.envSet.equalLights(lights))
+ continue;
+ else {
+ getNewEnvironment(ra, lights, ra.fog, ra.modelClip, ra.app);
+ }
+ }
+ else {
+ if (ra.geometryAtom.source.otherAppearance != app) {
+ if (ra.geometryAtom.source.otherAppearance != null)
+ ra.geometryAtom.source.otherAppearance.sgApp.removeAMirrorUser(ra.geometryAtom.source);
+ if (app != ra.geometryAtom.source.appearance) {
+ if (app != null) {
+ app.sgApp.addAMirrorUser(ra.geometryAtom.source);
+ }
+ ra.geometryAtom.source.otherAppearance = app;
+ }
+ else {
+ ra.geometryAtom.source.otherAppearance = null;
+ }
+ }
+ if (ra.lights == lights || ra.envSet.equalLights(lights)) {
+ ra.app = app;
+ e = ra.envSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ else {
+ getNewEnvironment(ra, lights, ra.fog, ra.modelClip, app);
+ }
+ }
+ }
+ else {
+ if (ra.lights == lights || ra.envSet.equalLights(lights))
+ continue;
+ getNewEnvironment(ra, lights, ra.fog, ra.modelClip, ra.app);
+ }
+ }
+ // Only done for new lights added to the system
+ if (changedLts.size() > 0)
+ updateCanvasForDirtyLights(changedLts);
+
+ }
+
+ void updateCanvasForDirtyLights(ArrayList mLts) {
+ int n, i, j, lmask;
+ EnvironmentSet e;
+ UnorderList list;
+ EnvironmentSet envsets[];
+ int sz = mLts.size();
+ int envsize;
+ int ltsize;
+
+ for (n = 0; n < sz; n++) {
+ LightRetained lt = (LightRetained)mLts.get(n);
+ list = lt.environmentSets;
+ synchronized (list) {
+ envsets = (EnvironmentSet []) list.toArray(false);
+ envsize = list.size();
+
+ if (lt.nodeType == LightRetained.AMBIENTLIGHT) {
+ for (i = 0; i < envsize; i++) {
+ e = envsets[i];
+ e.canvasDirty |= Canvas3D.AMBIENTLIGHT_DIRTY;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ }
+ } else {
+ for (i = 0; i < envsize; i++) {
+ e = envsets[i];
+ lmask = 0;
+ ltsize = e.lights.size();
+ for (j = 0; j < ltsize; j++) {
+ LightRetained curLt = (LightRetained)e.lights.get(j);
+ if (lt == curLt) {
+ lmask = (1 << e.ltPos[j]);
+ if (curLt.lightOn == true) {
+ e.enableMaskCache |= (1 << e.ltPos[j]);
+ }
+ else {
+ e.enableMaskCache &= (1 << e.ltPos[j]);
+ }
+ break;
+ }
+ }
+ e.canvasDirty |= Canvas3D.LIGHTENABLES_DIRTY;
+ if (!e.onUpdateList) {
+ objUpdateList.add(e);
+ e.onUpdateList = true;
+ }
+ if(e.lightBin != null) {
+ e.lightBin.canvasDirty |= Canvas3D.LIGHTBIN_DIRTY;
+ e.lightBin.lightDirtyMaskCache |= lmask;
+ if (!e.lightBin.onUpdateList) {
+ e.lightBin.onUpdateList = true;
+ objUpdateList.add(e.lightBin);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void addTextureResourceFreeList(TextureRetained tex) {
+ toBeAddedTextureResourceFreeList.add(tex);
+ }
+
+
+ void reEvaluateEnv(ArrayList mLts, ArrayList fogs,
+ ArrayList modelClips,
+ boolean updateDirty,
+ boolean altAppDirty) {
+
+ reEvaluateAllRenderAtoms(altAppDirty);
+
+ // Done only for xform changes, not for bounding leaf change
+ if (updateDirty) {
+ // Update canvases for dirty lights and fog
+ if (mLts.size()> 0)
+ updateCanvasForDirtyLights(mLts);
+ if (fogs.size() > 0)
+ updateCanvasForDirtyFog(fogs);
+ if (modelClips.size() > 0)
+ updateCanvasForDirtyModelClip(modelClips);
+ }
+
+ }
+
+ void updateInfVworldToVpc() {
+ vworldToVpc.getRotation(infVworldToVpc);
+ }
+
+
+ // Lock all geometry before rendering into the any canvas
+ // in the case of display list, for each renderer,
+ // release after building the display list (which happens
+ // for the first canvas rendered)
+ void lockGeometry() {
+ GeometryRetained geo;
+ int i, size;
+
+
+ // Vertex array is locked for every time renderer is run
+ size = lockGeometryList.size();
+ for (i = 0; i < size; i++) {
+ geo = (GeometryRetained) lockGeometryList.get(i);
+ geo.geomLock.getLock();
+
+ }
+
+ // dlist is locked only when they are rebuilt
+ size = dlistLockList.size();
+ for (i = 0; i < size ; i++) {
+ geo = (GeometryRetained) dlistLockList.get(i);
+ geo.geomLock.getLock();
+
+ }
+
+ // Lock all the by reference image components
+ size = nodeComponentList.size();
+ for (i = 0; i < size; i++) {
+ ImageComponentRetained nc = (ImageComponentRetained)nodeComponentList.get(i);
+ nc.geomLock.getLock();
+ }
+ }
+
+ // Release all geometry after rendering to the last canvas
+ void releaseGeometry() {
+ GeometryRetained geo;
+ int i, size;
+
+ size = lockGeometryList.size();
+ for (i = 0; i < size; i++) {
+ geo = (GeometryRetained) lockGeometryList.get(i);
+ geo.geomLock.unLock();
+ }
+
+ size = dlistLockList.size();
+ for (i = 0; i < size; i++) {
+ geo = (GeometryRetained) dlistLockList.get(i);
+ geo.geomLock.unLock();
+ }
+ // Clear the display list clear list
+ dlistLockList.clear();
+ // Lock all the by reference image components
+ size = nodeComponentList.size();
+ for (i = 0; i < size; i++) {
+ ImageComponentRetained nc = (ImageComponentRetained)nodeComponentList.get(i);
+ nc.geomLock.unLock();
+ }
+ }
+
+ void addGeometryToLockList(Object geo) {
+ // just add it to the list, if its a shared geometry
+ // it may be added more than once, thats OK since we
+ // now have nested locks!
+ lockGeometryList.add(geo);
+ }
+
+ void removeGeometryFromLockList(Object geo) {
+ lockGeometryList.remove(geo);
+
+ }
+
+
+ void addDirtyReferenceGeometry(Object geo) {
+ // just add it to the list, if its a shared geometry
+ // it may be added more than once, thats OK since we
+ // now have nested locks!
+ dirtyReferenceGeomList.add(geo);
+ }
+
+
+ void addNodeComponent(Object nc) {
+ newNodeComponentList.add(nc);
+ }
+
+ void removeNodeComponent (Object nc) {
+ removeNodeComponentList.add(nc);
+ }
+
+ void addDirtyNodeComponent(Object nc) {
+ dirtyNodeComponentList.add(nc);
+ }
+
+
+ void clearDirtyOrientedRAs() {
+ int i, nRAs;
+ Canvas3D cv;
+ RenderAtom ra;
+ OrientedShape3DRetained os;
+ nRAs = dirtyOrientedRAs.size();
+
+ // clear the dirtyMask
+ for(i=0; i<nRAs; i++) {
+ ra = (RenderAtom)dirtyOrientedRAs.get(i);
+ ra.dirtyMask &= ~RenderAtom.IN_DIRTY_ORIENTED_RAs;
+ }
+ dirtyOrientedRAs.clear();
+ }
+
+ // Called from MasterControl when viewCache changes or if there are
+ // dirtyOrientedShapes
+ void updateOrientedRAs() {
+ int i, nRAs;
+ Canvas3D cv;
+ RenderAtom ra;
+ OrientedShape3DRetained os;
+
+ cv = (Canvas3D)view.getCanvas3D(0);
+ if (view.viewCache.vcDirtyMask != 0) {
+ nRAs = orientedRAs.size();
+
+ // Update ra's localToVworld given orientedTransform
+ // Mark Oriented shape as dirty, since multiple ra could point
+ // to the same OrientShape3D, compute the xform only once
+ for(i=0; i<nRAs; i++) {
+ ra = (RenderAtom)orientedRAs.get(i);
+ os = (OrientedShape3DRetained)ra.geometryAtom.source;
+ os.orientedTransformDirty = true;
+ }
+ // Update ra's localToVworld given orientedTransform
+ for(i=0; i<nRAs; i++) {
+ ra = (RenderAtom)orientedRAs.get(i);
+ os = (OrientedShape3DRetained)ra.geometryAtom.source;
+ if (os.orientedTransformDirty) {
+ os.updateOrientedTransform(cv, view.viewIndex);
+ os.orientedTransformDirty = false;
+ }
+ ra.updateOrientedTransform();
+ }
+ } else {
+ nRAs = cachedDirtyOrientedRAs.size();
+ // Update ra's localToVworld given orientedTransform
+ // Mark Oriented shape as dirty, since multiple ra could point
+ // to the same OrientShape3D, compute the xform only once
+ for(i=0; i<nRAs; i++) {
+ ra = (RenderAtom)cachedDirtyOrientedRAs.get(i);
+ os = (OrientedShape3DRetained)ra.geometryAtom.source;
+ os.orientedTransformDirty = true;
+ }
+ // Update ra's localToVworld given orientedTransform
+ for(i=0; i<nRAs; i++) {
+ ra = (RenderAtom)cachedDirtyOrientedRAs.get(i);
+ os = (OrientedShape3DRetained)ra.geometryAtom.source;
+ if (os.orientedTransformDirty) {
+ os.updateOrientedTransform(cv, view.viewIndex);
+ os.orientedTransformDirty = false;
+
+ }
+ ra.updateOrientedTransform();
+ }
+ }
+ cachedDirtyOrientedRAs.clear();
+
+ }
+
+
+ // This removes a renderAtom and also does the necessary changes
+ // for a orientShape3D
+ void removeARenderAtom(RenderAtom ra) {
+ // System.out.println("===> remove ga = "+ra.geometryAtom);
+ ra.setRenderBin(false);
+ ra.renderMolecule.removeRenderAtom(ra);
+ if (ra.inDirtyOrientedRAs()) {
+ dirtyOrientedRAs.remove(dirtyOrientedRAs.indexOf(ra));
+ ra.dirtyMask &= ~RenderAtom.IN_DIRTY_ORIENTED_RAs;
+ }
+ // TODO: Should I remove the ra from dirtyDepthSortRenderAtom?
+ if (ra.inDepthSortList()) {
+ dirtyDepthSortRenderAtom.remove(dirtyDepthSortRenderAtom.indexOf(ra));
+ ra.dirtyMask &= ~RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST;
+ numDirtyTinfo -= ra.rListInfo.length;
+ }
+ }
+
+ void removeAllRenderAtoms() {
+ int i;
+ J3dMessage m;
+ RenderAtom ra;
+ RenderMolecule rm;
+ int sz = renderAtoms.size();
+
+ for (i = 0; i < sz; i++) {
+ ra = (RenderAtom) renderAtoms.get(i);
+ rm = ra.renderMolecule;
+ removeARenderAtom(ra);
+ rm.updateRemoveRenderAtoms();
+ }
+ renderAtoms.clear();
+
+ clearAllUpdateObjectState();
+
+ // Clear the arrayList that are kept from one frame to another
+ renderMoleculeList.clear();
+ sharedDList.clear();
+ lockGeometryList.clear();
+ // clear out this orderedBin's entry in the orderedGroup
+ for (i = 0; i < orderedBins.size(); i++) {
+ removeOrderedBin((OrderedBin) orderedBins.get(i));
+ }
+ orderedBins.clear();
+ bgOrderedBins.clear();
+ nodeComponentList.clear();
+ orientedRAs.clear();
+ bhTreesArrList.clear();
+ lightBinFreelist.clear();
+ envSetFreelist.clear();
+ attrBinFreelist.clear();
+ textureBinFreelist.clear();
+ renderMoleculeFreelist.clear();
+
+ // clean up any messages that are queued up, since they are
+ // irrelevant
+ // clearMessages();
+ geometryBackground = null;
+ }
+
+ void removeOrderedBin(OrderedBin ob) {
+ int i, k;
+ for (i = 0; i < ob.orderedCollections.size(); i++) {
+ OrderedCollection oc = (OrderedCollection) ob.orderedCollections.get(i);
+ if (oc == null)
+ continue;
+
+ for (k = 0; k < oc.childOrderedBins.size(); k++) {
+ removeOrderedBin((OrderedBin)(oc.childOrderedBins.get(k)));
+ }
+ }
+ if (ob.source != null) {
+ ob.source.setOrderedBin(null, view.viewIndex);
+ ob.source = null;
+ }
+ }
+
+
+ void removeGeometryDlist(RenderAtomListInfo ra) {
+ removeDlist.add(ra);
+ }
+
+
+ void addGeometryDlist(RenderAtomListInfo ra) {
+ addDlist.add(ra);
+ }
+
+
+ void dumpBin(LightBin bin) {
+ LightBin obin = bin;
+ while (obin != null) {
+ System.out.println("LightBin = "+obin);
+ EnvironmentSet envSet = obin.environmentSetList;
+ while (envSet != null) {
+ System.out.println(" EnvSet = "+envSet);
+ AttributeBin abin = envSet.attributeBinList;
+ while (abin != null) {
+ System.out.println(" ABin = "+abin);
+ TextureBin tbin = abin.textureBinList;
+ while (tbin != null) {
+ System.out.println(" Tbin = "+tbin);
+ RenderMolecule rm = tbin.opaqueRMList;
+ System.out.println("===> Begin Dumping OpaqueBin");
+ dumpRM(rm);
+ System.out.println("===> End Dumping OpaqueBin");
+ rm = tbin.transparentRMList;
+ System.out.println("===> Begin Dumping transparentBin");
+ dumpRM(rm);
+ System.out.println("===> End Dumping transparentBin");
+ tbin = tbin.next;
+ }
+ abin = abin.next;
+ }
+
+ envSet = envSet.next;
+ }
+ obin = obin.next;
+ }
+
+ }
+
+ void dumpRM(RenderMolecule rm) {
+ while (rm != null) {
+ System.out.println(" rm = "+rm+" numRAs = "+rm.numRenderAtoms);
+ System.out.println(" primaryRenderAtomList = "+
+ rm.primaryRenderAtomList);
+ RenderAtomListInfo rinfo = rm.primaryRenderAtomList;
+ while (rinfo != null) {
+ System.out.println(" rinfo = "+rinfo);
+ System.out.println(" rinfo.ra.localeVwcBounds = "
+ + rinfo.renderAtom.localeVwcBounds);
+ System.out.println(" rinfo.ra.ga.so.vwcBounds = "
+ + rinfo.renderAtom.geometryAtom.source.vwcBounds);
+ System.out.println(" geometry = "+rinfo.geometry());
+
+ rinfo = rinfo.next;
+ }
+ System.out.println(" separateDlistRenderAtomList = "+
+ rm.separateDlistRenderAtomList);
+ rinfo = rm.separateDlistRenderAtomList;
+ while (rinfo != null) {
+ System.out.println(" rinfo = "+rinfo);
+ System.out.println(" rinfo.ra.localeVwcBounds = "
+ + rinfo.renderAtom.localeVwcBounds);
+ System.out.println(" rinfo.ra.ga.so.vwcBounds = "
+ + rinfo.renderAtom.geometryAtom.source.vwcBounds);
+ System.out.println(" geometry = "+rinfo.geometry());
+ rinfo = rinfo.next;
+ }
+ System.out.println(" vertexArrayRenderAtomList = "+
+ rm.vertexArrayRenderAtomList);
+ if (rm.next == null) {
+ rm= rm.nextMap;
+ }
+ else {
+ rm = rm.next;
+ }
+ }
+ }
+
+ void removeTransparentObject (Object obj) {
+ // System.out.println("&&&&&&&&&&&&removeTransparentObject r = "+obj);
+ if (obj instanceof TextureBin) {
+ TextureBin tb = (TextureBin) obj;
+ if (tb.attributeBin.environmentSet.lightBin.geometryBackground != null) {
+ TransparentRenderingInfo t = tb.parentTInfo;
+
+ // Remove the element from the transparentInfo struct
+ if (t == bgTransparentInfo) {
+ bgTransparentInfo = bgTransparentInfo.next;
+ if (bgTransparentInfo != null)
+ bgTransparentInfo.prev = null;
+ }
+ else {
+ t.prev.next = t.next;
+ if (t.next != null)
+ t.next.prev = t.prev;
+ }
+ t.prev = null;
+ t.next = null;
+ transparentInfoFreeList.add(t);
+ tb.parentTInfo = null;
+ }
+ else {
+ int index = allTransparentObjects.indexOf(obj);
+ if (index == -1) {
+ // System.out.println("==> DEBUG1: Should never come here!");
+ return;
+ }
+ allTransparentObjects.remove(index);
+
+ TransparentRenderingInfo t = tb.parentTInfo;
+
+ // Remove the element from the transparentInfo struct
+ if (t == transparentInfo) {
+ transparentInfo = transparentInfo.next;
+ if (transparentInfo != null)
+ transparentInfo.prev = null;
+ }
+ else {
+ t.prev.next = t.next;
+ if (t.next != null)
+ t.next.prev = t.prev;
+ }
+ t.prev = null;
+ t.next = null;
+ transparentInfoFreeList.add(t);
+ tb.parentTInfo = null;
+ }
+
+ }
+ else {
+ int index = allTransparentObjects.indexOf(obj);
+ if (index == -1) {
+ // System.out.println("==> DEBUG2: Should never come here!");
+ return;
+ }
+
+ allTransparentObjects.remove(index);
+ RenderAtom r = (RenderAtom)obj;
+ for (int i = 0; i < r.parentTInfo.length; i++) {
+ // Remove the element from the transparentInfo struct
+ TransparentRenderingInfo t = r.parentTInfo[i];
+ // This corresponds to null geometry
+ if (t == null)
+ continue;
+
+ // Remove the element from the transparentInfo struct
+ if (t == transparentInfo) {
+ transparentInfo = transparentInfo.next;
+ if (transparentInfo != null)
+ transparentInfo.prev = null;
+ }
+ else {
+ t.prev.next = t.next;
+ if (t.next != null)
+ t.next.prev = t.prev;
+ }
+ t.prev = null;
+ t.next = null;
+ transparentInfoFreeList.add(t);
+ nElements--;
+ r.parentTInfo[i] = null;
+ }
+ }
+
+ }
+
+ void updateTransparentInfo(RenderAtom r) {
+ // System.out.println("===> update transparent Info");
+ for (int i = 0; i < r.parentTInfo.length; i++) {
+
+ if (r.parentTInfo[i] == null)
+ continue;
+ /*
+ r.parentTInfo[i].lightBin = r.envSet.lightBin;
+ r.parentTInfo[i].envSet = r.envSet;
+ r.parentTInfo[i].aBin = r.renderMolecule.textureBin.attributeBin;
+ */
+ r.parentTInfo[i].rm = r.renderMolecule;
+ }
+ }
+
+ void addTransparentObject (Object obj) {
+ // System.out.println("&&&&&&&&&&&&addTransparentObject r = "+obj);
+ if (obj instanceof TextureBin) {
+ TextureBin tb = (TextureBin) obj;
+ // Background geometry
+ if (tb.attributeBin.environmentSet.lightBin.geometryBackground != null) {
+ bgTransparentInfo = computeDirtyAcrossTransparentBins(tb, bgTransparentInfo);
+ }
+ else {
+ allTransparentObjects.add(obj);
+ transparentInfo = computeDirtyAcrossTransparentBins(tb, transparentInfo);
+ }
+ }
+ else {
+ allTransparentObjects.add(obj);
+ RenderAtom r = (RenderAtom)obj;
+ if (r.parentTInfo == null) {
+ r.parentTInfo = new TransparentRenderingInfo[r.rListInfo.length];
+ }
+ computeDirtyAcrossTransparentBins(r);
+ // System.out.println("update Centroid 2, ga = "+r.geometryAtom);
+ r.geometryAtom.updateCentroid();
+ dirtyDepthSortRenderAtom.add(r);
+ r.dirtyMask |= RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST;
+ numDirtyTinfo += r.rListInfo.length;
+ // System.out.println("transparentInfo ="+transparentInfo);
+ }
+ }
+
+ TransparentRenderingInfo getTransparentInfo() {
+ TransparentRenderingInfo tinfo;
+
+ if (transparentInfoFreeList.size() > 0) {
+ tinfo = (TransparentRenderingInfo)transparentInfoFreeList.remove(transparentInfoFreeList.size()-1);
+ } else {
+ tinfo = new TransparentRenderingInfo();
+ }
+ return (tinfo);
+
+ }
+
+ TransparentRenderingInfo computeDirtyAcrossTransparentBins(TextureBin tb, TransparentRenderingInfo startinfo) {
+ TransparentRenderingInfo tinfo = getTransparentInfo();
+ /*
+ tinfo.lightBin = tb.attributeBin.environmentSet.lightBin;
+ tinfo.envSet = tb.attributeBin.environmentSet;
+ tinfo.aBin = tb.attributeBin;
+ */
+ tinfo.rm = tb.transparentRMList;
+ tb.parentTInfo = tinfo;
+ if (startinfo == null) {
+ startinfo = tinfo;
+ tinfo.prev = null;
+ tinfo.next = null;
+
+ }
+ else {
+ tinfo.next = startinfo;
+ startinfo.prev = tinfo;
+ startinfo = tinfo;
+ }
+ return startinfo;
+ }
+ void computeDirtyAcrossTransparentBins(RenderAtom r) {
+
+ for (int i = 0; i < r.parentTInfo.length; i++) {
+ if (r.rListInfo[i].geometry() == null) {
+ r.parentTInfo[i] = null;
+ continue;
+ }
+ nElements++;
+ TransparentRenderingInfo tinfo = getTransparentInfo();
+ /*
+ tinfo.lightBin = r.envSet.lightBin;
+ tinfo.envSet = r.envSet;
+ tinfo.aBin = r.renderMolecule.textureBin.attributeBin;
+ */
+ tinfo.rm = r.renderMolecule;
+ tinfo.rInfo = r.rListInfo[i];
+ r.parentTInfo[i] = tinfo;
+ if (transparentInfo == null) {
+ transparentInfo = tinfo;
+ tinfo.prev = null;
+ tinfo.next = null;
+ }
+ else {
+ tinfo.prev = null;
+ tinfo.next = transparentInfo;
+ transparentInfo.prev = tinfo;
+ transparentInfo = tinfo;
+ }
+
+ }
+
+ }
+
+ void processRenderAtomTransparentInfo(RenderAtomListInfo rinfo, ArrayList newList) {
+ while (rinfo != null) {
+ // If either the renderAtom has never been in transparent mode
+ // or if it was previously in that mode and now going back
+ // to that mode
+ if (rinfo.renderAtom.parentTInfo == null) {
+ rinfo.renderAtom.parentTInfo = new TransparentRenderingInfo[rinfo.renderAtom.rListInfo.length];
+ computeDirtyAcrossTransparentBins(rinfo.renderAtom);
+ rinfo.renderAtom.geometryAtom.updateCentroid();
+ newList.add(rinfo.renderAtom);
+ }
+ else {
+ GeometryRetained geo = null;
+ int i = 0;
+ while (geo == null && i < rinfo.renderAtom.rListInfo.length) {
+ geo = rinfo.renderAtom.rListInfo[i].geometry();
+ i++;
+ }
+ // If there is atleast one non-null geometry in this renderAtom
+ if (geo != null) {
+ if (rinfo.renderAtom.parentTInfo[i-1] == null) {
+ computeDirtyAcrossTransparentBins(rinfo.renderAtom);
+ rinfo.renderAtom.geometryAtom.updateCentroid();
+ newList.add(rinfo.renderAtom);
+ }
+ }
+ }
+ rinfo = rinfo.next;
+
+ }
+ }
+
+ void convertTransparentRenderingStruct(int oldMode, int newMode) {
+ int i, size;
+ ArrayList newList = new ArrayList(5);
+ RenderAtomListInfo rinfo;
+ // Reset the transparentInfo;
+ transparentInfo = null;
+ if (oldMode == View.TRANSPARENCY_SORT_NONE && newMode == View.TRANSPARENCY_SORT_GEOMETRY) {
+ size = allTransparentObjects.size();
+
+ for (i = 0; i < size; i++) {
+ TextureBin tb = (TextureBin)allTransparentObjects.get(i);
+ transparentInfoFreeList.add(tb.parentTInfo);
+ tb.parentTInfo = null;
+ RenderMolecule r = tb.transparentRMList;
+ // For each renderMolecule
+ while (r != null) {
+ // If this was a dlist molecule, since we will be rendering
+ // as separate dlist per rinfo, destroy the display list
+ if ((r.primaryMoleculeType &RenderMolecule.DLIST_MOLECULE) != 0) {
+ // System.out.println("&&&&&&&&& changing from dlist to dlist_per_rinfo");
+ addDisplayListResourceFreeList(r);
+ removeDirtyRenderMolecule(r);
+
+ r.vwcBounds.set(null);
+ r.displayListId = 0;
+ r.displayListIdObj = null;
+ // Change the group type for all the rlistInfo in the primaryList
+ rinfo = r.primaryRenderAtomList;
+ while (rinfo != null) {
+ rinfo.groupType = RenderAtom.SEPARATE_DLIST_PER_RINFO;
+ if (rinfo.renderAtom.dlistIds == null) {
+ rinfo.renderAtom.dlistIds = new int[rinfo.renderAtom.rListInfo.length];
+
+ for (int k = 0; k < rinfo.renderAtom.dlistIds.length; k++) {
+ rinfo.renderAtom.dlistIds[k] = -1;
+ }
+ }
+ if (rinfo.renderAtom.dlistIds[rinfo.index] == -1) {
+ rinfo.renderAtom.dlistIds[rinfo.index] = VirtualUniverse.mc.getDisplayListId().intValue();
+ addDlistPerRinfo.add(rinfo);
+ }
+ rinfo = rinfo.next;
+ }
+ r.primaryMoleculeType = RenderMolecule.SEPARATE_DLIST_PER_RINFO_MOLECULE;
+ }
+ // Get all the renderAtoms in the list
+ processRenderAtomTransparentInfo(r.primaryRenderAtomList, newList);
+ processRenderAtomTransparentInfo(r.vertexArrayRenderAtomList, newList);
+ processRenderAtomTransparentInfo(r.separateDlistRenderAtomList, newList);
+ if (r.next == null) {
+ r = r.nextMap;
+ }
+ else {
+ r = r.next;
+ }
+ }
+ }
+ allTransparentObjects = newList;
+ }
+ else if (oldMode == View.TRANSPARENCY_SORT_GEOMETRY && newMode == View.TRANSPARENCY_SORT_NONE) {
+ // System.out.println("oldMode = TRANSPARENCY_SORT_GEOMETRY, newMode = TRANSPARENCY_SORT_NONE");
+ size = allTransparentObjects.size();
+ for (i = 0; i < size; i++) {
+ RenderAtom r= (RenderAtom)allTransparentObjects.get(i);
+ r.dirtyMask &= ~RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST;
+ for (int j = 0; j < r.parentTInfo.length; j++) {
+ // Corresponds to null geometry
+ if (r.parentTInfo[j] == null)
+ continue;
+
+ transparentInfoFreeList.add(r.parentTInfo[j]);
+ r.parentTInfo[j] = null;
+ }
+ if (r.renderMolecule.textureBin.parentTInfo == null) {
+ transparentInfo = computeDirtyAcrossTransparentBins(r.renderMolecule.textureBin, transparentInfo);
+ newList.add(r.renderMolecule.textureBin);
+ }
+ }
+ allTransparentObjects = newList;
+ dirtyDepthSortRenderAtom.clear();
+ numDirtyTinfo = 0;
+ }
+ }
+
+ TransparentRenderingInfo mergeDepthSort(TransparentRenderingInfo oldList, TransparentRenderingInfo newList) {
+ TransparentRenderingInfo input1 = oldList , input2 = newList, nextN;
+ TransparentRenderingInfo lastInput1 = oldList;
+ double zval1, zval2;
+ // System.out.println("&&&&&&&&mergeDepthSort");
+ /*
+ TransparentRenderingInfo t = oldList;
+ System.out.println("");
+ while (t != null) {
+ System.out.println("==> old t = "+t);
+ t = t.next;
+ }
+ System.out.println("");
+ t = newList;
+ while (t != null) {
+ System.out.println("==> new t = "+t);
+ t = t.next;
+ }
+ */
+ while (input1 != null && input2 != null) {
+ lastInput1 = input1;
+ nextN = input2.next;
+ zval1 = input1.zVal;
+ zval2 = input2.zVal;
+ // Put the newList before the current one
+ if (zval2 > zval1) {
+ // System.out.println("===> path1");
+ if (input1.prev == null) {
+ input1.prev = input2;
+ input2.prev = null;
+ input2.next = oldList;
+ oldList = input2;
+ }
+ else {
+ // System.out.println("===> path2");
+ input2.prev = input1.prev;
+ input1.prev.next = input2;
+ input2.next = input1;
+ input1.prev = input2;
+ }
+ input2 = nextN;
+ }
+ else {
+ // System.out.println("===> path3");
+ input1 = input1.next;
+ }
+
+
+ }
+ if (input1 == null && input2 != null) {
+ // add at then end
+ if (lastInput1 == null) {
+ oldList = input2;
+ input2.prev = null;
+ }
+ else {
+ lastInput1.next = input2;
+ input2.prev = lastInput1;
+ }
+ }
+ return oldList;
+ }
+
+ void insertDepthSort(RenderAtom r) {
+ TransparentRenderingInfo tinfo = null;
+ // System.out.println("&&&&&&&&insertDepthSort");
+ for (int i = 0; i < r.rListInfo.length; i++) {
+ if (r.parentTInfo[i] == null)
+ continue;
+
+ if (transparentInfo == null) {
+ transparentInfo = r.parentTInfo[i];
+ transparentInfo.prev = null;
+ transparentInfo.next = null;
+ }
+ else {
+ tinfo = transparentInfo;
+ TransparentRenderingInfo prevInfo = transparentInfo;
+ while (tinfo != null&& r.parentTInfo[i].zVal < tinfo.zVal) {
+ prevInfo = tinfo;
+ tinfo = tinfo.next;
+ }
+ r.parentTInfo[i].prev = prevInfo;
+ if (prevInfo.next != null) {
+ prevInfo.next.prev = r.parentTInfo[i];
+ }
+ r.parentTInfo[i].next = prevInfo.next;
+ prevInfo.next = r.parentTInfo[i];
+
+ }
+
+ }
+ }
+
+
+
+ TransparentRenderingInfo collectDirtyTRInfo( TransparentRenderingInfo dirtyList,
+ RenderAtom r) {
+
+ for (int i = 0; i < r.rListInfo.length; i++) {
+ TransparentRenderingInfo t = r.parentTInfo[i];
+ if (t == null)
+ continue;
+ if (t == transparentInfo) {
+ transparentInfo = transparentInfo.next;
+ if (transparentInfo != null)
+ transparentInfo.prev = null;
+ }
+ else {
+ t.prev.next = t.next;
+ if (t.next != null)
+ t.next.prev = t.prev;
+ }
+ if (dirtyList == null) {
+ dirtyList = t;
+ t.prev = null;
+ t.next = null;
+ } else {
+ t.next = dirtyList;
+ t.prev = null;
+ dirtyList.prev = t;
+ dirtyList = t;
+ }
+ }
+
+ return dirtyList;
+ }
+
+
+ TransparentRenderingInfo depthSortAll(TransparentRenderingInfo startinfo) {
+ TransparentRenderingInfo tinfo, previnfo, nextinfo;
+ double curZ;
+ // System.out.println("&&&&&&&&&&&depthSortAll");
+ // Do insertion sort
+ /*
+ tinfo = startinfo;
+ while (tinfo != null) {
+ System.out.println("Soreted tinfo= "+tinfo+" tinfo.prev = "+tinfo.prev+" tinfo.next = "+tinfo.next);
+ tinfo = tinfo.next;
+ }
+ */
+ tinfo = startinfo.next;
+ while (tinfo != null) {
+ // System.out.println("====> Doing tinfo = "+tinfo);
+ nextinfo = tinfo.next;
+ curZ = tinfo.zVal;
+ previnfo = tinfo.prev;
+ // Find the correct location for tinfo
+
+ while (previnfo != null && previnfo.zVal < curZ) {
+ previnfo = previnfo.prev;
+
+ }
+ if (tinfo.prev != previnfo) {
+ if (previnfo == null) {
+ if (tinfo.next != null) {
+ tinfo.next.prev = tinfo.prev;
+ }
+ // tinfo.prev is not null
+ tinfo.prev.next = tinfo.next;
+ tinfo.next = startinfo;
+ startinfo.prev = tinfo;
+ startinfo = tinfo;
+ tinfo.prev = null;
+ }
+ else {
+ if (tinfo.next != null) {
+ tinfo.next.prev = tinfo.prev;
+ }
+ if (tinfo.prev != null) {
+ tinfo.prev.next = tinfo.next;
+ }
+ tinfo.next = previnfo.next;
+ if (previnfo.next != null)
+ previnfo.next.prev = tinfo;
+ tinfo.prev = previnfo;
+ previnfo.next = tinfo;
+ // System.out.println("path2, tinfo.prev = "+tinfo.prev);
+ // System.out.println("path2, tinfo.next = "+tinfo.next);
+ }
+
+ }
+ /*
+ TransparentRenderingInfo tmp = startinfo;
+ while (tmp != null) {
+ System.out.println("Soreted tmp= "+tmp+" tmp.prev = "+tmp.prev+" tmp.next = "+tmp.next);
+ tmp = tmp.next;
+ }
+ */
+
+ tinfo = nextinfo;
+
+ }
+ /*
+ tinfo = startinfo;
+ double prevZ = 0.0;
+ while (tinfo != null) {
+ tinfo.render = false;
+ curZ = ((double[])distMap.get(tinfo.rInfo.renderAtom))[tinfo.rInfo.index];
+ nextinfo = tinfo.next;
+ if (nextinfo != null) {
+ double nextZ = ((double[])distMap.get(nextinfo.rInfo.renderAtom))[tinfo.rInfo.index];
+ if (Math.abs(curZ - nextZ) < 1.0e-6 && curZ < 400) {
+ tinfo.render = true;
+ }
+ }
+
+ if (Math.abs(curZ - prevZ) < 1.0e-6 && curZ < 400) {
+ tinfo.render = true;
+ }
+
+ prevZ = curZ;
+ tinfo = tinfo.next;
+
+ }
+ tinfo = startinfo;
+ while (tinfo != null) {
+ System.out.println("z = "+((double[])distMap.get(tinfo.rInfo.renderAtom))[tinfo.rInfo.index]+" ga = "+tinfo.rInfo.renderAtom.geometryAtom);
+ tinfo = tinfo.next;
+ }
+ System.out.println("\n\n");
+ tinfo = startinfo;
+ while (tinfo != null) {
+ if (tinfo.render) {
+ System.out.println("same z = "+((double[])distMap.get(tinfo.rInfo.renderAtom))[tinfo.rInfo.index]+" ga = "+tinfo.rInfo.renderAtom.geometryAtom);
+ GeometryAtom ga = tinfo.rInfo.renderAtom.geometryAtom;
+ System.out.println("ga.geometryArray.length = "+ga.geometryArray.length);
+ for (int k = 0; k < ga.geometryArray.length; k++) {
+ System.out.println("geometry "+k+" = "+ga.geometryArray[k]);
+ if (ga.geometryArray[k] != null) {
+ System.out.println(" vcount = "+((GeometryArrayRetained)ga.geometryArray[k]).getVertexCount());
+ ((GeometryArrayRetained)ga.geometryArray[k]).printCoordinates();
+ }
+ }
+ }
+ tinfo = tinfo.next;
+ }
+ */
+ return startinfo;
+ }
+
+ void processViewSpecificGroupChanged(J3dMessage m) {
+ int component = ((Integer)m.args[0]).intValue();
+ Object[] objAry = (Object[])m.args[1];
+ if (((component & ViewSpecificGroupRetained.ADD_VIEW) != 0) ||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ View v = (View)objAry[0];
+ ArrayList leafList = (ArrayList)objAry[2];
+ // View being added is this view
+ if (v == view) {
+ int size = leafList.size();
+ for (i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof LightRetained) {
+ envDirty |= REEVALUATE_LIGHTS;
+ if (!changedLts.contains(obj))
+ changedLts.add(obj);
+ }
+ else if (obj instanceof FogRetained) {
+ envDirty |= REEVALUATE_FOG;
+ if (!changedFogs.contains(obj))
+ changedFogs.add(obj);
+ }
+ else if (obj instanceof AlternateAppearanceRetained) {
+ altAppearanceDirty = true;
+
+ }
+ else if (obj instanceof ModelClipRetained) {
+ envDirty |= REEVALUATE_MCLIP;
+ if (!changedModelClips.contains(obj))
+ changedModelClips.add(obj);
+ }
+ else if (obj instanceof BackgroundRetained) {
+ reEvaluateBg = true;
+ }
+
+ else if (obj instanceof ClipRetained) {
+ reEvaluateClip = true;
+
+ } else if (obj instanceof GeometryAtom) {
+ visGAIsDirty = true;
+ visQuery = true;
+ }
+ }
+
+ }
+
+ }
+ if (((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0)||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ ArrayList leafList;
+ View v;
+
+ if ((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0) {
+ v = (View)objAry[0];
+ leafList = (ArrayList)objAry[2];
+ }
+ else {
+ v = (View)objAry[4];
+ leafList = (ArrayList)objAry[6];
+ }
+ if (v == view) {
+ int size = leafList.size();
+ for (i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof GeometryAtom) {
+ RenderAtom ra = ((GeometryAtom)obj).getRenderAtom(view);
+ if (ra != null && ra.inRenderBin()) {
+ renderAtoms.remove(renderAtoms.indexOf(ra));
+ removeARenderAtom(ra);
+ }
+ }
+ else if (obj instanceof LightRetained) {
+ envDirty |= REEVALUATE_LIGHTS;
+ }
+ else if (obj instanceof FogRetained) {
+ envDirty |= REEVALUATE_FOG;
+ }
+ else if (obj instanceof AlternateAppearanceRetained) {
+ altAppearanceDirty = true;
+
+ }
+ else if (obj instanceof ModelClipRetained) {
+ envDirty |= REEVALUATE_MCLIP;
+
+ }
+ else if (obj instanceof BackgroundRetained) {
+ reEvaluateBg = true;
+ }
+
+ else if (obj instanceof ClipRetained) {
+ reEvaluateClip = true;
+
+ }
+ }
+ }
+ }
+
+ }
+
+ void insertNodes(J3dMessage m) {
+ Object nodes[];
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ int i, j;
+ Object n;
+ nodes = (Object[])m.args[0];
+ for (j = 0; j < nodes.length; j++) {
+ if (nodes[j] instanceof LightRetained) {
+ envDirty |= REEVALUATE_LIGHTS;
+ if (!changedLts.contains(nodes[j]))
+ changedLts.add(nodes[j]);
+ } else if (nodes[j] instanceof FogRetained) {
+ envDirty |= REEVALUATE_FOG;
+ if (!changedFogs.contains(nodes[j]))
+ changedFogs.add(nodes[j]);
+ } else if (nodes[j] instanceof BackgroundRetained) {
+ // If a new background is inserted, then
+ // re_evaluate to determine if this background
+ // should be used
+ reEvaluateBg = true;
+ } else if (nodes[j] instanceof ClipRetained) {
+ reEvaluateClip = true;
+ } else if (nodes[j] instanceof ModelClipRetained) {
+ envDirty |= REEVALUATE_MCLIP;
+ if (!changedModelClips.contains(nodes[j]))
+ changedModelClips.add(nodes[j]);
+ } else if (nodes[j] instanceof GeometryAtom) {
+ visGAIsDirty = true;
+ visQuery = true;
+ } else if (nodes[j] instanceof AlternateAppearanceRetained) {
+ altAppearanceDirty = true;
+ }
+ }
+
+ // Handle ViewScoped Nodes
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+ for (i = 0; i < size; i++) {
+ n = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ // If the node object is scoped to this view, then ..
+ if (vl.contains(view)) {
+ if (n instanceof LightRetained) {
+ envDirty |= REEVALUATE_LIGHTS;
+ if (!changedLts.contains(n))
+ changedLts.add(n);
+ } else if (n instanceof FogRetained) {
+ envDirty |= REEVALUATE_FOG;
+ if (!changedFogs.contains(n))
+ changedFogs.add(n);
+ } else if (n instanceof BackgroundRetained) {
+ // If a new background is inserted, then
+ // re_evaluate to determine if this backgrouns
+ // should be used
+ reEvaluateBg = true;
+ } else if (n instanceof ClipRetained) {
+ reEvaluateClip = true;
+ } else if (n instanceof ModelClipRetained) {
+ envDirty |= REEVALUATE_MCLIP;
+ if (!changedModelClips.contains(n))
+ changedModelClips.add(n);
+ } else if (n instanceof AlternateAppearanceRetained) {
+ altAppearanceDirty = true;
+ }
+ }
+ // Note: geometryAtom is not part of viewScopedNodes
+ // Its a part of orginal nodes even if scoped
+
+ }
+ }
+ }
+
+
+ void removeNodes(J3dMessage m) {
+ Object[] nodes;
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ int i, j;
+ nodes = (Object[])m.args[0];
+ for (int n = 0; n < nodes.length; n++) {
+ if (nodes[n] instanceof GeometryAtom) {
+ visGAIsDirty = true;
+ visQuery = true;
+ RenderAtom ra =
+ ((GeometryAtom)nodes[n]).getRenderAtom(view);
+ if (ra != null && ra.inRenderBin()) {
+ renderAtoms.remove(renderAtoms.indexOf(ra));
+ removeARenderAtom(ra);
+ }
+ }
+ else if (nodes[n] instanceof AlternateAppearanceRetained) {
+ altAppearanceDirty = true;
+ }
+ else if (nodes[n] instanceof BackgroundRetained) {
+ reEvaluateBg = true;
+ }
+ else if (nodes[n] instanceof ClipRetained) {
+ reEvaluateClip = true;
+ } else if (nodes[n] instanceof ModelClipRetained) {
+ envDirty |= REEVALUATE_MCLIP;
+ } else if (nodes[n] instanceof FogRetained) {
+ envDirty |= REEVALUATE_FOG;
+ }
+ if (nodes[n] instanceof LightRetained) {
+ envDirty |= REEVALUATE_LIGHTS;
+ }
+ }
+ // Handle ViewScoped Nodes
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+ Object node;
+ for (i = 0; i < size; i++) {
+ node = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ // If the node object is scoped to this view, then ..
+ if (vl.contains(view)) {
+ if (node instanceof LightRetained) {
+ envDirty |= REEVALUATE_LIGHTS;
+ } else if (node instanceof FogRetained) {
+ envDirty |= REEVALUATE_FOG;
+ } else if (node instanceof BackgroundRetained) {
+ // If a new background is inserted, then
+ // re_evaluate to determine if this backgrouns
+ // should be used
+ reEvaluateBg = true;
+ } else if (node instanceof ClipRetained) {
+ reEvaluateClip = true;
+ } else if (node instanceof ModelClipRetained) {
+ envDirty |= REEVALUATE_MCLIP;
+
+ } else if (node instanceof AlternateAppearanceRetained) {
+ altAppearanceDirty = true;
+ }
+ // Note: geometryAtom is not part of viewScopedNodes
+ // Its a part of orginal nodes even if scoped
+ }
+
+ }
+ }
+ }
+
+ void cleanup() {
+ releaseAllDisplayListID();
+ removeAllRenderAtoms();
+ }
+
+
+ void freeAllDisplayListResources(Canvas3D cv) {
+ int i;
+ int size = renderMoleculeList.size();
+ Renderer rdr = cv.screen.renderer;
+
+ if (size > 0) {
+ RenderMolecule[] rmArr = (RenderMolecule[])
+ renderMoleculeList.toArray(false);
+
+ for (i = 0 ; i < size; i++) {
+ rmArr[i].releaseAllPrimaryDisplayListResources(cv);
+ }
+ }
+
+ size = sharedDList.size();
+ if (size > 0) {
+ RenderAtomListInfo arr[] =
+ (RenderAtomListInfo []) sharedDList.toArray(false);
+
+ GeometryArrayRetained geo;
+ int mask = (cv.useSharedCtx ? rdr.rendererBit : cv.canvasBit);
+
+ for (i = 0; i < size; i++) {
+ geo = (GeometryArrayRetained)arr[i].geometry();
+ if (geo.dlistId > 0) {
+ if (!cv.useSharedCtx) {
+ cv.freeDisplayList(cv.ctx, geo.dlistId);
+ }
+
+ if (geo.decrDlistRefCount(mask) == 0) {
+ geo.resourceCreationMask &= ~mask;
+ if (cv.useSharedCtx) {
+ cv.freeDisplayList(cv.ctx, geo.dlistId);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ // put displayListID back to MC
+ void releaseAllDisplayListID() {
+ int i;
+ int size = renderMoleculeList.size();
+
+ if (size > 0) {
+ RenderMolecule[] rmArr = (RenderMolecule[])
+ renderMoleculeList.toArray(false);
+
+ for (i = 0 ; i < size; i++) {
+ rmArr[i].releaseAllPrimaryDisplayListID();
+ }
+ }
+
+ size = sharedDList.size();
+ if (size > 0) {
+ RenderAtomListInfo arr[] =
+ (RenderAtomListInfo []) sharedDList.toArray(false);
+ GeometryArrayRetained geo;
+
+ for (i = 0; i < size; i++) {
+ geo = (GeometryArrayRetained)arr[i].geometry();
+ if (geo.resourceCreationMask == 0) {
+ geo.freeDlistId();
+ }
+ }
+ }
+ }
+
+
+ /*
+ void handleFrequencyBitChanged(J3dMessage m) {
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ GeometryAtom[] gaArr = (GeometryAtom[])m.args[3];
+ int i;
+ RenderAtom ra;
+ Boolean value = (Boolean)m.args[1];
+ int mask = ((Integer)m.args[2]).intValue();
+
+ // Currently, we do not handle the case of
+ // going from frequent to infrequent
+ if (value == Boolean.FALSE)
+ return;
+
+ ra = null;
+ // Get the first ra that is visible
+ for (i = 0; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+ }
+
+ if (ra == null)
+ return;
+
+ int start = i;
+ // Check if the removed renderAtom is already in
+ // a separate bin - this is to handle the case
+ // when it has been changed to frequent, then to
+ // infrequent and then to frequent again!
+ if ((nc instanceof MaterialRetained && ra.renderMolecule.definingMaterial != ra.renderMolecule.material) ||
+ (nc instanceof AppearanceRetained && ((ra.renderMolecule.soleUser & AppearanceRetained.MATERIAL) == 0))) {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ }
+ }
+ else if ((nc instanceof PolygonAttributesRetained && ra.renderMolecule.definingPolygonAttributes != ra.renderMolecule.polygonAttributes) ||
+ (nc instanceof AppearanceRetained && ((ra.renderMolecule.soleUser & AppearanceRetained.POLYGON) == 0))) {
+ // Check if the removed renderAtom is already in
+ // a separate bin - this is to handle the case
+ // when it has been changed to frequent, then to
+ // infrequent and then to frequent again!
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ }
+ }
+ else if ((nc instanceof PointAttributesRetained && ra.renderMolecule.definingPointAttributes != ra.renderMolecule.pointAttributes) ||
+ (nc instanceof AppearanceRetained && ((ra.renderMolecule.soleUser & AppearanceRetained.POINT) == 0))) {
+ // Check if the removed renderAtom is already in
+ // a separate bin - this is to handle the case
+ // when it has been changed to frequent, then to
+ // infrequent and then to frequent again!
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ }
+ }
+ else if ((nc instanceof LineAttributesRetained && ra.renderMolecule.definingLineAttributes != ra.renderMolecule.lineAttributes) ||
+ (nc instanceof AppearanceRetained && ((ra.renderMolecule.soleUser & AppearanceRetained.LINE) == 0))) {
+ // Check if the removed renderAtom is already in
+ // a separate bin - this is to handle the case
+ // when it has been changed to frequent, then to
+ // infrequent and then to frequent again!
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ }
+ }
+ else if((nc instanceof TransparencyAttributesRetained&& ra.renderMolecule.definingTransparency != ra.renderMolecule.transparency) ||
+ (nc instanceof AppearanceRetained && ((ra.renderMolecule.soleUser & AppearanceRetained.TRANSPARENCY) == 0))) {
+ // Check if the removed renderAtom is already in
+ // a separate bin - this is to handle the case
+ // when it has been changed to frequent, then to
+ // infrequent and then to frequent again!
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ }
+ }
+ else if ((nc instanceof ColoringAttributesRetained&& ra.renderMolecule.definingColoringAttributes != ra.renderMolecule.coloringAttributes) ||
+ (nc instanceof AppearanceRetained && ((ra.renderMolecule.soleUser & AppearanceRetained.COLOR) == 0))) {
+ // Check if the removed renderAtom is already in
+ // a separate bin - this is to handle the case
+ // when it has been changed to frequent, then to
+ // infrequent and then to frequent again!
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ TextureBin tb = ra.renderMolecule.textureBin;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertRenderAtom(tb, ra);
+ }
+ }
+ else if ((nc instanceof RenderingAttributesRetained && ra.renderMolecule.textureBin.attributeBin.definingRenderingAttributes != ra.renderMolecule.textureBin.attributeBin.renderingAttrs) ||
+ (nc instanceof AppearanceRetained && ((ra.renderMolecule.textureBin.attributeBin.soleUser & AppearanceRetained.RENDER) == 0))) {
+ for (i = start; i < gaArr.length; i++) {
+ ra = gaArr[i].getRenderAtom(view);
+ if (ra== null || !ra.inRenderBin())
+ continue;
+
+ EnvironmentSet e= ra.renderMolecule.textureBin.attributeBin.environmentSet;
+ ra.renderMolecule.removeRenderAtom(ra);
+ reInsertAttributeBin(e, ra);
+ }
+ }
+ else {
+
+ // TODO: handle texture
+ }
+
+
+ }
+ */
+
+}
diff --git a/src/classes/share/javax/media/j3d/RenderMethod.java b/src/classes/share/javax/media/j3d/RenderMethod.java
new file mode 100644
index 0000000..2febc16
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderMethod.java
@@ -0,0 +1,27 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The RenderMethod interface is used to create various ways to render
+ * different geometries.
+ */
+
+interface RenderMethod {
+
+ /**
+ * The actual rendering code for this RenderMethod
+ */
+ abstract boolean render(RenderMolecule rm, Canvas3D cv, int pass,
+ RenderAtomListInfo ra, int dirtyBits);
+}
diff --git a/src/classes/share/javax/media/j3d/RenderMolecule.java b/src/classes/share/javax/media/j3d/RenderMolecule.java
new file mode 100644
index 0000000..7812862
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderMolecule.java
@@ -0,0 +1,3122 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+/**
+ * The RenderMolecule manages a collection of RenderAtoms.
+ */
+
+class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponentUpdate {
+
+
+ // different types of IndexedUnorderedSet that store RenderMolecule
+ static final int REMOVE_RENDER_ATOM_IN_RM_LIST = 0;
+ static final int RENDER_MOLECULE_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ /**
+ * Values for the geometryType field
+ */
+ static final int POINT = 0x01;
+ static final int LINE = 0x02;
+ static final int SURFACE = 0x04;
+ static final int RASTER = 0x08;
+ static final int COMPRESSED = 0x10;
+
+ static int RM_COMPONENTS = (AppearanceRetained.POLYGON |
+ AppearanceRetained.LINE |
+ AppearanceRetained.POINT |
+ AppearanceRetained.MATERIAL |
+ AppearanceRetained.TRANSPARENCY|
+ AppearanceRetained.COLOR);
+
+ // TODO: use definingMaterial etc. instead of these
+ // when sole user is completely implement
+ PolygonAttributesRetained polygonAttributes = null;
+ LineAttributesRetained lineAttributes = null;
+ PointAttributesRetained pointAttributes = null;
+ MaterialRetained material = null;
+ ColoringAttributesRetained coloringAttributes = null;
+ TransparencyAttributesRetained transparency = null;
+
+ // Use Object instead of AppearanceRetained class for
+ // state caching optimation memory performance
+
+ boolean normalPresent = true;
+
+
+ // Equivalent bits
+ static final int POINTATTRS_DIRTY = AppearanceRetained.POINT;
+ static final int LINEATTRS_DIRTY = AppearanceRetained.LINE;
+ static final int POLYGONATTRS_DIRTY = AppearanceRetained.POLYGON;
+ static final int MATERIAL_DIRTY = AppearanceRetained.MATERIAL;
+ static final int TRANSPARENCY_DIRTY = AppearanceRetained.TRANSPARENCY;
+ static final int COLORINGATTRS_DIRTY = AppearanceRetained.COLOR;
+
+ static final int ALL_DIRTY_BITS = POINTATTRS_DIRTY | LINEATTRS_DIRTY | POLYGONATTRS_DIRTY | MATERIAL_DIRTY | TRANSPARENCY_DIRTY | COLORINGATTRS_DIRTY;
+
+ /**
+ * bit mask of all attr fields that are equivalent across
+ * renderMolecules
+ */
+ int dirtyAttrsAcrossRms = ALL_DIRTY_BITS;
+
+
+ // Mask set to true is any of the component have changed
+ int soleUserCompDirty = 0;
+
+ /**
+ * The PolygonAttributes for this RenderMolecule
+ */
+ PolygonAttributesRetained definingPolygonAttributes = null;
+
+ /**
+ * The LineAttributes for this RenderMolecule
+ */
+ LineAttributesRetained definingLineAttributes = null;
+
+ /**
+ * The PointAttributes for this RenderMolecule
+ */
+ PointAttributesRetained definingPointAttributes = null;
+
+ /**
+ * The TextureBin that this RenderMolecule resides
+ */
+ TextureBin textureBin = null;
+
+ /**
+ * The localToVworld for this RenderMolecule
+ */
+ Transform3D[] localToVworld = null;
+ int[] localToVworldIndex = null;
+
+ /**
+ * The Material reference for this RenderMolecule
+ */
+ MaterialRetained definingMaterial = null;
+
+
+ /**
+ * The ColoringAttribute reference for this RenderMolecule
+ */
+ ColoringAttributesRetained definingColoringAttributes = null;
+
+
+ /**
+ * The Transparency reference for this RenderMolecule
+ */
+ TransparencyAttributesRetained definingTransparency = null;
+
+ /**
+ * Transform3D - point to the right one based on bg or not
+ */
+ Transform3D[] trans = null;
+
+
+ /**
+ * specify whether scale is nonuniform
+ */
+ boolean isNonUniformScale = false;
+
+ /**
+ * number of renderAtoms to be rendered in this RenderMolecule
+ */
+ int numRenderAtoms = 0;
+
+ /**
+ * number of render atoms, used during the renderBin update time
+ */
+ int numEditingRenderAtoms = 0;
+
+ RenderAtom addRAs = null;
+ RenderAtom removeRAs = null;
+
+ /**
+ * The cached ColoringAttributes color value. It is
+ * 1.0, 1.0, 1.0 if there is no ColoringAttributes.
+ */
+ float red = 1.0f;
+ float green = 1.0f;
+ float blue = 1.0f;
+
+
+ /**
+ * Cached diffuse color value
+ */
+ float dRed = 1.0f;
+ float dGreen = 1.0f;
+ float dBlue = 1.0f;
+
+
+
+ /**
+ * The cached TransparencyAttributes transparency value. It is
+ * 0.0 if there is no TransparencyAttributes.
+ */
+ float alpha = 0.0f;
+
+ /**
+ * The geometry type for this RenderMolecule
+ */
+ int geometryType = -1;
+
+ /**
+ * A boolean indicating whether or not lighting should be on.
+ */
+ boolean enableLighting = false;
+
+ /**
+ * A boolean indicating whether or not this molecule rendered Text3D
+ */
+
+ int primaryMoleculeType = 0;
+ static int COMPRESSED_MOLECULE = 0x1;
+ static int TEXT3D_MOLECULE = 0x2;
+ static int DLIST_MOLECULE = 0x4;
+ static int RASTER_MOLECULE = 0x8;
+ static int ORIENTEDSHAPE3D_MOLECULE = 0x10;
+ static int SEPARATE_DLIST_PER_RINFO_MOLECULE = 0x20;
+
+
+ /**
+ * Cached values for polygonMode, line antialiasing, and point antialiasing
+ */
+ int polygonMode = PolygonAttributes.POLYGON_FILL;
+ boolean lineAA = false;
+ boolean pointAA = false;
+
+ /**
+ * The vertex format for this RenderMolecule. Only looked
+ * at for GeometryArray and CompressedGeometry objects.
+ */
+ int vertexFormat = -1;
+
+ /**
+ * The texCoordSetMap length for this RenderMolecule.
+ */
+ int texCoordSetMapLen = 0;
+
+ /**
+ * The primary renderMethod object for this RenderMolecule
+ * this is either a Text3D, display list, or compressed geometry renderer.
+ */
+ RenderMethod primaryRenderMethod = null;
+
+ /**
+ * The secondary renderMethod object for this RenderMolecule
+ * this is used for geometry that is shared
+ */
+ RenderMethod secondaryRenderMethod = null;
+
+ /**
+ * The RenderBino for this molecule
+ */
+ RenderBin renderBin = null;
+
+ /**
+ * The references to the next and previous RenderMolecule in the
+ * list.
+ */
+ RenderMolecule next = null;
+ RenderMolecule prev = null;
+
+
+ /**
+ * The list of RenderAtoms in this RenderMolecule that are not using
+ * vertex arrays.
+ */
+ RenderAtomListInfo primaryRenderAtomList = null;
+
+
+ /**
+ * The list of RenderAtoms in this RenderMolecule that are using
+ * separte dlist .
+ */
+ RenderAtomListInfo separateDlistRenderAtomList = null;
+
+
+ /**
+ * The list of RenderAtoms in this RenderMolecule that are using vertex
+ * arrays.
+ */
+ RenderAtomListInfo vertexArrayRenderAtomList = null;
+
+ /**
+ * This BoundingBox is used for View Frustum culling on the primary
+ * list
+ */
+ BoundingBox vwcBounds = null;
+
+
+ /**
+ * If this is end of the linked list for this xform, then
+ * this field is non-null, if there is a map after this
+ */
+ RenderMolecule nextMap = null;
+ RenderMolecule prevMap = null;
+
+ /**
+ * If the any of the node component of the appearance in RM will be changed
+ * frequently, then confine it to a separate bin
+ */
+ boolean soleUser = false;
+ Object appHandle = null;
+
+
+ VertexArrayRenderMethod cachedVertexArrayRenderMethod =
+ (VertexArrayRenderMethod)
+ VirtualUniverse.mc.getVertexArrayRenderMethod();
+
+ // In D3D separate Quad/Triangle Geometry with others in RenderMolecule
+ // Since we need to dynamically switch whether to use DisplayList
+ // or not in render() as a group.
+ boolean isQuadGeometryArray = false;
+ boolean isTriGeometryArray = false;
+
+ // display list id, valid id starts from 1
+ int displayListId = 0;
+ Integer displayListIdObj = null;
+
+ int onUpdateList = 0;
+ static int NEW_RENDERATOMS_UPDATE = 0x1;
+ static int BOUNDS_RECOMPUTE_UPDATE = 0x2;
+ static int LOCALE_TRANSLATION = 0x4;
+ static int UPDATE_BACKGROUND_TRANSFORM = 0x8;
+ static int IN_DIRTY_RENDERMOLECULE_LIST = 0x10;
+ static int LOCALE_CHANGED = 0x20;
+ static int ON_UPDATE_CHECK_LIST = 0x40;
+
+
+ // background geometry rendering
+ boolean doInfinite;
+ Transform3D[] infLocalToVworld;
+
+ // Whether alpha is used in this renderMolecule
+ boolean useAlpha = false;
+
+ // Support for multiple locales
+ Locale locale = null;
+
+ // Transform when locale is different from the view's locale
+ Transform3D[] localeLocalToVworld = null;
+
+ // Vector used for locale translation
+ Vector3d localeTranslation = null;
+
+ boolean primaryChanged = false;
+
+ boolean isOpaqueOrInOG = true;
+ boolean inOrderedGroup = false;
+
+
+ // closest switch parent
+ SwitchRetained closestSwitchParent = null;
+
+ // the child index from the closest switch parent
+ int closestSwitchIndex = -1;
+
+
+ RenderMolecule(GeometryAtom ga,
+ PolygonAttributesRetained polygonAttributes,
+ LineAttributesRetained lineAttributes,
+ PointAttributesRetained pointAttributes,
+ MaterialRetained material,
+ ColoringAttributesRetained coloringAttributes,
+ TransparencyAttributesRetained transparency,
+ RenderingAttributesRetained renderAttrs,
+ TextureUnitStateRetained[] texUnits,
+ Transform3D[] transform, int[] transformIndex,
+ RenderBin rb) {
+ renderBin = rb;
+ IndexedUnorderSet.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+
+ reset(ga, polygonAttributes, lineAttributes, pointAttributes,
+ material, coloringAttributes, transparency, renderAttrs,
+ texUnits, transform,
+ transformIndex);
+ }
+
+ void reset(GeometryAtom ga,
+ PolygonAttributesRetained polygonAttributes,
+ LineAttributesRetained lineAttributes,
+ PointAttributesRetained pointAttributes,
+ MaterialRetained material,
+ ColoringAttributesRetained coloringAttributes,
+ TransparencyAttributesRetained transparency,
+ RenderingAttributesRetained renderAttrs,
+ TextureUnitStateRetained[] texUnits,
+ Transform3D[] transform, int[] transformIndex) {
+ primaryMoleculeType = 0;
+ numRenderAtoms = 0;
+ numEditingRenderAtoms = 0;
+ onUpdateList = 0;
+ dirtyAttrsAcrossRms = ALL_DIRTY_BITS;
+ primaryRenderMethod = null;
+ isNonUniformScale = false;
+ primaryChanged = false;
+ this.material = material;
+ this.polygonAttributes = polygonAttributes;
+ this.lineAttributes = lineAttributes;
+ this.pointAttributes = pointAttributes;
+ this.coloringAttributes = coloringAttributes;
+ this.transparency = transparency;
+
+ closestSwitchParent = ga.source.closestSwitchParent;
+ closestSwitchIndex = ga.source.closestSwitchIndex;
+
+ int i1;
+ // Find the first non-null geometey
+ GeometryRetained geo = null;
+ int k = 0;
+ isOpaqueOrInOG = true;
+ inOrderedGroup = false;
+ while (geo == null && (k < ga.geometryArray.length)) {
+ geo = ga.geometryArray[k];
+ k++;
+ }
+
+ if (ga.source.appearance != null) {
+ soleUser = ((ga.source.appearance.changedFrequent & RM_COMPONENTS) != 0);
+ }
+ else {
+ soleUser = false;
+ }
+ // Set the appearance only for soleUser case
+ if (soleUser)
+ appHandle = ga.source.appearance;
+ else
+ appHandle = (Object)this;
+
+ // If its of type GeometryArrayRetained
+ if (ga.geoType <= GeometryRetained.GEO_TYPE_GEOMETRYARRAY ||
+ ga.geoType == GeometryRetained.GEO_TYPE_TEXT3D) {
+
+ if (ga.source instanceof OrientedShape3DRetained) {
+ primaryRenderMethod =
+ VirtualUniverse.mc.getOrientedShape3DRenderMethod();
+ primaryMoleculeType = ORIENTEDSHAPE3D_MOLECULE;
+ } else if (ga.geoType == GeometryRetained.GEO_TYPE_TEXT3D) {
+ primaryRenderMethod =
+ VirtualUniverse.mc.getText3DRenderMethod();
+ primaryMoleculeType = TEXT3D_MOLECULE;
+ } else {
+ // Make determination of dlist or not during addRenderAtom
+ secondaryRenderMethod = cachedVertexArrayRenderMethod;
+ }
+ }
+ else {
+ if (ga.geoType == GeometryRetained.GEO_TYPE_COMPRESSED) {
+ primaryRenderMethod =
+ VirtualUniverse.mc.getCompressedGeometryRenderMethod();
+ primaryMoleculeType = COMPRESSED_MOLECULE;
+ }
+ else if (geo instanceof RasterRetained) {
+ primaryRenderMethod =
+ VirtualUniverse.mc.getDefaultRenderMethod();
+ primaryMoleculeType = RASTER_MOLECULE;
+ }
+ }
+
+ prev = null;
+ next = null;
+ prevMap = null;
+ nextMap = null;
+
+ primaryRenderAtomList = null;
+ vertexArrayRenderAtomList = null;
+
+
+
+ switch (ga.geoType) {
+ case GeometryRetained.GEO_TYPE_POINT_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET:
+ this.geometryType = POINT;
+ break;
+ case GeometryRetained.GEO_TYPE_LINE_SET:
+ case GeometryRetained.GEO_TYPE_LINE_STRIP_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ this.geometryType = LINE;
+ break;
+ case GeometryRetained.GEO_TYPE_RASTER:
+ this.geometryType = RASTER;
+ break;
+ case GeometryRetained.GEO_TYPE_COMPRESSED:
+ this.geometryType = COMPRESSED;
+
+ switch (((CompressedGeometryRetained)geo).getBufferType()) {
+ case CompressedGeometryHeader.POINT_BUFFER:
+ this.geometryType |= POINT ;
+ break ;
+ case CompressedGeometryHeader.LINE_BUFFER:
+ this.geometryType |= LINE ;
+ break ;
+ default:
+ case CompressedGeometryHeader.TRIANGLE_BUFFER:
+ this.geometryType |= SURFACE ;
+ if (polygonAttributes != null) {
+ if (polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT) {
+ this.geometryType |= POINT;
+ } else if (polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE) {
+ this.geometryType |= LINE;
+ }
+ }
+ break ;
+ }
+ break;
+ default:
+ this.geometryType = SURFACE;
+ if (polygonAttributes != null) {
+ if (polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT) {
+ this.geometryType |= POINT;
+ } else if (polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE) {
+ this.geometryType |= LINE;
+ }
+ }
+ break;
+ }
+
+ isQuadGeometryArray = (geo.getClassType() ==
+ GeometryRetained.QUAD_TYPE);
+ isTriGeometryArray = (geo.getClassType() ==
+ GeometryRetained.TRIANGLE_TYPE);
+
+ this.localToVworld = transform;
+ this.localToVworldIndex = transformIndex;
+ doInfinite = ga.source.inBackgroundGroup;
+ if (doInfinite) {
+ if (infLocalToVworld == null) {
+ infLocalToVworld = new Transform3D[2];
+ infLocalToVworld[0] = infLocalToVworld[1] = new Transform3D();
+ }
+ localToVworld[0].getRotation(infLocalToVworld[0]);
+ }
+ int mask = 0;
+ if (polygonAttributes != null) {
+ if (polygonAttributes.changedFrequent != 0) {
+ definingPolygonAttributes = polygonAttributes;
+
+ mask |= POLYGONATTRS_DIRTY;
+ }
+ else {
+ if (definingPolygonAttributes != null) {
+ definingPolygonAttributes.set(polygonAttributes);
+ }
+ else {
+ definingPolygonAttributes = (PolygonAttributesRetained)polygonAttributes.clone();
+ }
+ }
+ polygonMode = definingPolygonAttributes.polygonMode;
+ } else {
+ polygonMode = PolygonAttributes.POLYGON_FILL;
+ definingPolygonAttributes = null;
+ }
+
+ if (lineAttributes != null) {
+ if (lineAttributes.changedFrequent != 0) {
+ definingLineAttributes = lineAttributes;
+ mask |= LINEATTRS_DIRTY;
+ }
+ else {
+ if (definingLineAttributes != null) {
+ definingLineAttributes.set(lineAttributes);
+ }
+ else {
+ definingLineAttributes = (LineAttributesRetained)lineAttributes.clone();
+ }
+ }
+ lineAA = definingLineAttributes.lineAntialiasing;
+ } else {
+ lineAA = false;
+ definingLineAttributes = null;
+ }
+
+ if (pointAttributes != null) {
+ if (pointAttributes.changedFrequent != 0) {
+ definingPointAttributes = pointAttributes;
+ mask |= POINTATTRS_DIRTY;
+
+ }
+ else {
+ if (definingPointAttributes != null) {
+ definingPointAttributes.set(pointAttributes);
+ }
+ else {
+ definingPointAttributes = (PointAttributesRetained)pointAttributes.clone();
+ }
+ }
+ pointAA = definingPointAttributes.pointAntialiasing;
+ } else {
+ pointAA = false;
+ definingPointAttributes = null;
+ }
+
+ normalPresent = true;
+ if (geo instanceof GeometryArrayRetained) {
+ GeometryArrayRetained gr = (GeometryArrayRetained)geo;
+ this.vertexFormat = gr.vertexFormat;
+
+ if (gr.texCoordSetMap != null) {
+ this.texCoordSetMapLen = gr.texCoordSetMap.length;
+ } else {
+ this.texCoordSetMapLen = 0;
+ }
+
+ // Throw an exception if lighting is enabled, but no normals defined
+ if ((vertexFormat & GeometryArray.NORMALS) == 0) {
+ // Force lighting to false
+ normalPresent = false;
+ }
+
+ }
+ else if (geo instanceof CompressedGeometryRetained) {
+ this.vertexFormat =
+ ((CompressedGeometryRetained)geo).getVertexFormat();
+ // Throw an exception if lighting is enabled, but no normals defined
+ if ((vertexFormat & GeometryArray.NORMALS) == 0) {
+ // Force lighting to false
+ normalPresent = false;
+ }
+
+ this.texCoordSetMapLen = 0;
+
+ } else {
+ this.vertexFormat = -1;
+ this.texCoordSetMapLen = 0;
+ }
+
+ if (material != null) {
+ if (material.changedFrequent != 0) {
+ definingMaterial = material;
+ mask |= MATERIAL_DIRTY;
+ }
+ else {
+ if (definingMaterial != null)
+ definingMaterial.set(material);
+ else {
+ definingMaterial = (MaterialRetained)material.clone();
+ }
+ }
+
+ }
+ else {
+ definingMaterial = null;
+ }
+ evalMaterialCachedState();
+ if (coloringAttributes != null) {
+ if (coloringAttributes.changedFrequent != 0) {
+ definingColoringAttributes = coloringAttributes;
+ mask |= COLORINGATTRS_DIRTY;
+ }
+ else {
+ if (definingColoringAttributes != null) {
+ definingColoringAttributes.set(coloringAttributes);
+ }
+ else {
+ definingColoringAttributes = (ColoringAttributesRetained)coloringAttributes.clone();
+ }
+ }
+ red = coloringAttributes.color.x;
+ green = coloringAttributes.color.y;
+ blue = coloringAttributes.color.z;
+ } else {
+ red = 1.0f;
+ green = 1.0f;
+ blue = 1.0f;
+ definingColoringAttributes = null;
+ }
+
+ if (transparency != null) {
+
+ if (transparency.changedFrequent != 0) {
+ definingTransparency = transparency;
+ mask |= TRANSPARENCY_DIRTY;
+ }
+ else {
+ if (definingTransparency != null) {
+ definingTransparency.set(transparency);
+ }
+ else {
+ definingTransparency =
+ (TransparencyAttributesRetained)transparency.clone();
+ }
+ }
+ alpha = 1.0f - transparency.transparency;
+
+ } else {
+ alpha = 1.0f;
+ definingTransparency = null;
+
+ }
+
+ locale = ga.source.locale;
+ if (locale != renderBin.locale) {
+ if (localeLocalToVworld == null) {
+ localeLocalToVworld = new Transform3D[2];
+ }
+ localeLocalToVworld[0] = VirtualUniverse.mc.getTransform3D(null);
+ localeLocalToVworld[1] = VirtualUniverse.mc.getTransform3D(null);
+ localeTranslation = new Vector3d();
+ ga.locale.hiRes.difference(renderBin.locale.hiRes, localeTranslation);
+ translate();
+ }
+ else {
+ localeLocalToVworld = localToVworld;
+ }
+
+ if (doInfinite) {
+ trans = infLocalToVworld;
+ }
+ else {
+ trans = localeLocalToVworld;
+ }
+
+ evalAlphaUsage(renderAttrs, texUnits);
+ isOpaqueOrInOG = isOpaque() || (ga.source.orderedPath != null);
+ inOrderedGroup = (ga.source.orderedPath != null);
+ // System.out.println("isOpaque = "+isOpaque() +" OrInOG = "+isOpaqueOrInOG);
+ if (mask != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= mask;
+ }
+ }
+
+
+ /**
+ * This tests if the given attributes matches this TextureBin
+ */
+ boolean equals(RenderAtom ra,
+ PolygonAttributesRetained polygonAttributes,
+ LineAttributesRetained lineAttributes,
+ PointAttributesRetained pointAttributes,
+ MaterialRetained material,
+ ColoringAttributesRetained coloringAttributes,
+ TransparencyAttributesRetained transparency,
+ Transform3D[] transform) {
+ int geoType = 0;
+ GeometryAtom ga = ra.geometryAtom;
+ int eAttrs = 0;
+
+ if (this.localToVworld != transform) {
+ return (false);
+ }
+
+ if (locale != ra.geometryAtom.source.locale) {
+ return (false);
+ }
+
+ if (ra.geometryAtom.source.closestSwitchParent != closestSwitchParent ||
+ ra.geometryAtom.source.closestSwitchIndex != closestSwitchIndex) {
+ return (false);
+ }
+
+ // Find the first non-null geometey
+ GeometryRetained geo = null;
+ int k = 0;
+ while (geo == null && (k < ga.geometryArray.length)) {
+ geo = ga.geometryArray[k];
+ k++;
+ }
+
+ // TODO: Add tags
+ switch (ga.geoType) {
+ case GeometryRetained.GEO_TYPE_POINT_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET:
+ geoType = POINT;
+ break;
+ case GeometryRetained.GEO_TYPE_LINE_SET:
+ case GeometryRetained.GEO_TYPE_LINE_STRIP_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET:
+ case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ geoType = LINE;
+ break;
+ case GeometryRetained.GEO_TYPE_RASTER:
+ geoType = RASTER;
+ break;
+ case GeometryRetained.GEO_TYPE_COMPRESSED:
+ geoType = COMPRESSED;
+ switch (((CompressedGeometryRetained)geo).getBufferType()) {
+ case CompressedGeometryHeader.POINT_BUFFER:
+ geoType |= POINT ;
+ break ;
+ case CompressedGeometryHeader.LINE_BUFFER:
+ geoType |= LINE ;
+ break ;
+ default:
+ case CompressedGeometryHeader.TRIANGLE_BUFFER:
+ geoType |= SURFACE ;
+ break ;
+ }
+ break;
+ default:
+ geoType = SURFACE;
+ if (polygonAttributes != null) {
+ if (polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT) {
+ geoType |= POINT;
+ } else if (polygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE) {
+ geoType |= LINE;
+ }
+ }
+ break;
+ }
+
+ if (this.geometryType != geoType) {
+ return (false);
+ }
+ /*
+ // TODO : Check this
+ if (useDisplayList &&
+ (ga.geometry.isEditable ||
+ ga.geometry.refCount > 1 ||
+ ((GroupRetained)ga.source.parent).switchLevel >= 0 ||
+ ga.alphaEditable)) {
+ return (false);
+ }
+ */
+ if (ga.geoType == GeometryRetained.GEO_TYPE_TEXT3D &&
+ primaryMoleculeType != 0 &&
+ ((primaryMoleculeType & TEXT3D_MOLECULE) == 0)) {
+ return (false);
+ }
+
+
+ if(!(ra.geometryAtom.source instanceof OrientedShape3DRetained)
+ && ((primaryMoleculeType & ORIENTEDSHAPE3D_MOLECULE) != 0)) {
+ //System.out.println("RA's NOT a OrientedShape3DRetained and RM is a ORIENTEDSHAPE3D_MOLECULE ");
+
+ return (false);
+ }
+
+ // TODO: Its is necessary to have same vformat for dl,
+ // Howabout iteration, should we have 2 vformats in rm?
+ if (geo instanceof GeometryArrayRetained) {
+ GeometryArrayRetained gr = (GeometryArrayRetained)geo;
+ if (this.vertexFormat != gr.vertexFormat) {
+ return (false);
+ }
+
+
+ // we are requiring that texCoordSetMap length to be the same
+ // so that we can either put all multi-tex ga to a display list,
+ // or punt all to vertex array. And we don't need to worry
+ // about some of the ga can be in display list for this canvas,
+ // and some other can be in display list for the other canvas.
+ if (((gr.texCoordSetMap != null) &&
+ (this.texCoordSetMapLen != gr.texCoordSetMap.length)) ||
+ ((gr.texCoordSetMap == null) && (this.texCoordSetMapLen != 0))) {
+ return (false);
+ }
+
+ if (VirtualUniverse.mc.isD3D() &&
+ (((geo.getClassType() == GeometryRetained.QUAD_TYPE)
+ && !isQuadGeometryArray) ||
+ ((geo.getClassType() == GeometryRetained.TRIANGLE_TYPE)
+ && !isTriGeometryArray))) {
+ return false;
+ }
+
+ } else if (geo instanceof CompressedGeometryRetained) {
+ if (this.vertexFormat !=
+ ((CompressedGeometryRetained)geo).getVertexFormat()) {
+ return (false);
+ }
+ } else {
+ //TODO: compare isEditable
+ if (this.vertexFormat != -1) {
+ return (false);
+ }
+ }
+
+ // If the any reference to the appearance components that is cached renderMolecule
+ // can change frequently, make a separate bin
+ if (soleUser || (ra.geometryAtom.source.appearance != null &&
+ ((ra.geometryAtom.source.appearance.changedFrequent & RM_COMPONENTS) != 0))) {
+ if (appHandle == (Object)ra.geometryAtom.source.appearance) {
+
+ // if this RenderMolecule is currently on a zombie state,
+ // we'll need to put it on the update list to reevaluate
+ // the state, because while it is on a zombie state,
+ // state could have been changed. Example,
+ // application could have detached an appearance,
+ // made changes to the appearance state, and then
+ // reattached the appearance. In this case, the
+ // changes would not have reflected to the RenderMolecule
+
+ if (numEditingRenderAtoms == 0) {
+
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= ALL_DIRTY_BITS;
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+
+ }
+ // Assign the cloned value as the original value
+
+ // Either a changedFrequent or a null case
+ // and the incoming one is not equal or null
+ // then return;
+ // This check also handles null == null case
+ if (definingPolygonAttributes != null) {
+ if ((this.definingPolygonAttributes.changedFrequent != 0) ||
+ (polygonAttributes !=null && polygonAttributes.changedFrequent != 0))
+ if (definingPolygonAttributes == polygonAttributes) {
+ if (definingPolygonAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= POLYGONATTRS_DIRTY;
+ }
+ }
+ else {
+ return false;
+ }
+ else if (!definingPolygonAttributes.equivalent(polygonAttributes)) {
+ return false;
+ }
+ }
+ else if (polygonAttributes != null) {
+ return false;
+ }
+
+ if (definingLineAttributes != null) {
+ if ((this.definingLineAttributes.changedFrequent != 0) ||
+ (lineAttributes !=null && lineAttributes.changedFrequent != 0))
+ if (definingLineAttributes == lineAttributes) {
+ if (definingLineAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= LINEATTRS_DIRTY;
+ }
+ }
+ else {
+ return false;
+ }
+ else if (!definingLineAttributes.equivalent(lineAttributes)) {
+ return false;
+ }
+ }
+ else if (lineAttributes != null) {
+ return false;
+ }
+
+
+ if (definingPointAttributes != null) {
+ if ((this.definingPointAttributes.changedFrequent != 0) ||
+ (pointAttributes !=null && pointAttributes.changedFrequent != 0))
+ if (definingPointAttributes == pointAttributes) {
+ if (definingPointAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= POINTATTRS_DIRTY;
+ }
+ }
+ else {
+ return false;
+ }
+ else if (!definingPointAttributes.equivalent(pointAttributes)) {
+ return false;
+ }
+ }
+ else if (pointAttributes != null) {
+ return false;
+ }
+
+
+
+
+ if (definingMaterial != null) {
+ if ((this.definingMaterial.changedFrequent != 0) ||
+ (material !=null && material.changedFrequent != 0))
+ if (definingMaterial == material) {
+ if (definingMaterial.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= MATERIAL_DIRTY;
+ }
+ }
+ else {
+ return false;
+ }
+ else if (!definingMaterial.equivalent(material)) {
+ return false;
+ }
+ }
+ else if (material != null) {
+ return false;
+ }
+
+
+
+ if (definingColoringAttributes != null) {
+ if ((this.definingColoringAttributes.changedFrequent != 0) ||
+ (coloringAttributes !=null && coloringAttributes.changedFrequent != 0))
+ if (definingColoringAttributes == coloringAttributes) {
+ if (definingColoringAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= COLORINGATTRS_DIRTY;
+ }
+ }
+ else {
+ return false;
+ }
+ else if (!definingColoringAttributes.equivalent(coloringAttributes)) {
+ return false;
+ }
+ }
+ else if (coloringAttributes != null) {
+ return false;
+ }
+
+ // if the definingTransparency is a non cloned values and the incoming
+ // one is equivalent, then check if the component is dirty
+ // this happens when all the RAs from this RM have been removed
+ // but new ones are not added yet (rbin visibility) not run yet
+ // and when there is a change in nc based on the new RA, we wil;
+ // miss the change, doing this check will catch the change durin
+ // new RAs insertRenderAtom
+ if (definingTransparency != null) {
+ if ((this.definingTransparency.changedFrequent != 0) ||
+ (transparency !=null && transparency.changedFrequent != 0))
+ if (definingTransparency == transparency) {
+ if (definingTransparency.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= TRANSPARENCY_DIRTY;
+ }
+ }
+ else {
+ return false;
+ }
+ else if (!definingTransparency.equivalent(transparency)) {
+ return false;
+ }
+ }
+ else if (transparency != null) {
+ return false;
+ }
+
+ return (true);
+ }
+
+ public void updateRemoveRenderAtoms() {
+ int i;
+ RenderAtom r;
+ RenderAtomListInfo rinfo;
+
+ // Check if this renderMolecule was created and destroyed this frame.
+ // so, no display list was created
+ if (numRenderAtoms == 0 && removeRAs == null && addRAs == null) {
+ textureBin.removeRenderMolecule(this);
+ return;
+ }
+
+ while (removeRAs != null) {
+ r = (RenderAtom)removeRAs;
+ r.removed = null;
+ numRenderAtoms--;
+
+ // Loop thru all geometries in the renderAtom, they could
+ // potentially be in different buckets in the rendermoleulce
+ for (int index = 0; index < r.rListInfo.length; index++) {
+ rinfo = r.rListInfo[index];
+ // Don't remove null geo
+ if (rinfo.geometry() == null)
+ continue;
+
+ if ((rinfo.groupType & RenderAtom.PRIMARY) != 0) {
+ primaryChanged = true;
+ if (rinfo.prev == null) { // At the head of the list
+ primaryRenderAtomList = rinfo.next;
+ if (rinfo.next != null) {
+ rinfo.next.prev = null;
+ }
+ } else { // In the middle or at the end.
+ rinfo.prev.next = rinfo.next;
+ if (rinfo.next != null) {
+ rinfo.next.prev = rinfo.prev;
+ }
+ }
+ // If this renderAtom has localTransform,
+ // return transform to freelist
+ if (primaryMoleculeType == RenderMolecule.TEXT3D_MOLECULE) {
+ if (!rinfo.renderAtom.inRenderBin()) {
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, rinfo.localToVworld);
+
+ }
+ }
+ // If the molecule type is Raster, then add it to the lock list
+ else if (primaryMoleculeType == RASTER) {
+ RasterRetained geo = (RasterRetained)rinfo.geometry();
+ renderBin.removeGeometryFromLockList(geo);
+ if (geo.image != null)
+ renderBin.removeNodeComponent(geo.image);
+
+ }
+ else if ((rinfo.groupType & RenderAtom.SEPARATE_DLIST_PER_RINFO) != 0) {
+ if (!rinfo.renderAtom.inRenderBin()) {
+ renderBin.removeDlistPerRinfo.add(rinfo);
+ }
+ }
+ }
+ else if ((rinfo.groupType & RenderAtom.SEPARATE_DLIST_PER_GEO) != 0) {
+ if (rinfo.prev == null) { // At the head of the list
+ separateDlistRenderAtomList = rinfo.next;
+ if (rinfo.next != null) {
+ rinfo.next.prev = null;
+ }
+ } else { // In the middle or at the end.
+ rinfo.prev.next = rinfo.next;
+ if (rinfo.next != null) {
+ rinfo.next.prev = rinfo.prev;
+ }
+ }
+ renderBin.removeGeometryDlist(rinfo);
+
+ }
+ else {
+ if (rinfo.prev == null) { // At the head of the list
+ vertexArrayRenderAtomList = rinfo.next;
+ if (rinfo.next != null) {
+ rinfo.next.prev = null;
+ }
+ } else { // In the middle or at the end.
+ rinfo.prev.next = rinfo.next;
+ if (rinfo.next != null) {
+ rinfo.next.prev = rinfo.prev;
+ }
+ }
+ // For indexed geometry there is no need to lock since
+ // the mirror is changed only when the renderer is not
+ // running
+ // For indexed geometry, if use_coord is set, then either we
+ // are using the index geometry as is or we will be unindexifying
+ // on the fly, so its better to lock
+ GeometryArrayRetained geo = (GeometryArrayRetained)rinfo.geometry();
+ if (!(geo instanceof IndexedGeometryArrayRetained) ||
+ ((geo.vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0)) {
+ renderBin.removeGeometryFromLockList(geo);
+ }
+ }
+ rinfo.prev = null;
+ rinfo.next = null;
+ }
+ removeRAs = removeRAs.nextRemove;
+ r.nextRemove = null;
+ r.prevRemove = null;
+ if (r.isOriented()) {
+ renderBin.orientedRAs.remove(renderBin.orientedRAs.indexOf(r));
+ }
+
+ if ((textureBin.attributeBin.environmentSet.lightBin.geometryBackground == null) &&
+ !isOpaqueOrInOG && renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) {
+ renderBin.removeTransparentObject(r);
+ }
+ }
+ // If this renderMolecule will not be touched for adding new RenderAtoms
+ // then ..
+ if (addRAs == null) {
+ // If there are no more renderAtoms and there will be no more
+ // renderatoms added to this renderMolecule , then remove
+ if (numRenderAtoms == 0) {
+ // If both lists are empty remove this renderMolecule
+ if ((primaryMoleculeType &DLIST_MOLECULE) != 0) {
+ renderBin.addDisplayListResourceFreeList(this);
+ vwcBounds.set(null);
+ displayListId = 0;
+ displayListIdObj = null;
+ }
+ // If the locale is different, return xform to freelist
+ if (locale != renderBin.locale) {
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ localeLocalToVworld[0]);
+ localeLocalToVworld = null;
+ }
+ textureBin.removeRenderMolecule(this);
+ } else {
+ if ((primaryMoleculeType &DLIST_MOLECULE) != 0 && primaryChanged) {
+
+ // If a renderAtom is added to the display list
+ // structure then add this to the dirty list of rm
+ // for which the display list needs to be recreated
+ renderBin.addDirtyRenderMolecule(this);
+ vwcBounds.set(null);
+ rinfo = primaryRenderAtomList;
+ while (rinfo != null) {
+ vwcBounds.combine(rinfo.renderAtom.localeVwcBounds);
+ rinfo = rinfo.next;
+ }
+ primaryChanged = false;
+ }
+ }
+ }
+ numEditingRenderAtoms = numRenderAtoms;
+ }
+
+ public void updateObject() {
+ int i;
+ RenderAtom renderAtom;
+ RenderAtomListInfo r;
+ if (textureBin == null) {
+ return;
+ }
+
+ if (addRAs != null) {
+ while (addRAs != null) {
+
+ numRenderAtoms++;
+ renderAtom = (RenderAtom)addRAs;
+ renderAtom.renderMolecule = this;
+ renderAtom.added = null;
+ for (int j = 0; j < renderAtom.rListInfo.length; j++) {
+ r = (RenderAtomListInfo)renderAtom.rListInfo[j];
+ // Don't add null geo
+ if (r.geometry() == null)
+ continue;
+ r.groupType = evalRinfoGroupType(r);
+ if ((r.groupType & RenderAtom.PRIMARY) != 0) {
+ if ((r.groupType & RenderAtom.DLIST) != 0 && primaryRenderMethod == null) {
+ primaryMoleculeType = DLIST_MOLECULE;
+ renderBin.renderMoleculeList.add(this);
+
+ if (vwcBounds == null)
+ vwcBounds = new BoundingBox((BoundingBox)null);
+ primaryRenderMethod =
+ VirtualUniverse.mc.getDisplayListRenderMethod();
+ // Assign a displayListId for this renderMolecule
+ if (displayListId == 0) {
+ displayListIdObj = VirtualUniverse.mc.getDisplayListId();
+ displayListId = displayListIdObj.intValue();
+ }
+ }
+ else if ((r.groupType & RenderAtom.SEPARATE_DLIST_PER_RINFO) != 0 &&
+ primaryRenderMethod == null) {
+ primaryMoleculeType = SEPARATE_DLIST_PER_RINFO_MOLECULE;
+ renderBin.renderMoleculeList.add(this);
+ primaryRenderMethod =
+ VirtualUniverse.mc.getDisplayListRenderMethod();
+
+ }
+ primaryChanged = true;
+ if (primaryRenderAtomList == null) {
+ primaryRenderAtomList = r;
+ }
+ else {
+ r.next = primaryRenderAtomList;
+ primaryRenderAtomList.prev = r;
+ primaryRenderAtomList = r;
+ }
+ if (primaryMoleculeType == SEPARATE_DLIST_PER_RINFO_MOLECULE) {
+ if (r.renderAtom.dlistIds == null) {
+ r.renderAtom.dlistIds = new int[r.renderAtom.rListInfo.length];
+
+ for (int k = 0; k < r.renderAtom.dlistIds.length; k++) {
+ r.renderAtom.dlistIds[k] = -1;
+ }
+ }
+ if (r.renderAtom.dlistIds[r.index] == -1) {
+ r.renderAtom.dlistIds[r.index] = VirtualUniverse.mc.getDisplayListId().intValue();
+ renderBin.addDlistPerRinfo.add(r);
+ }
+ }
+
+ // If the molecule type is Raster, then add it to the lock list
+ if (primaryMoleculeType == RASTER) {
+ RasterRetained geo = (RasterRetained)r.geometry();
+ renderBin.addGeometryToLockList(geo);
+ if (geo.image != null)
+ renderBin.addNodeComponent(geo.image);
+ }
+ }
+ else if ((r.groupType & RenderAtom.SEPARATE_DLIST_PER_GEO) != 0) {
+ if (separateDlistRenderAtomList == null) {
+ separateDlistRenderAtomList = r;
+ }
+ else {
+ r.next = separateDlistRenderAtomList;
+ separateDlistRenderAtomList.prev = r;
+ separateDlistRenderAtomList = r;
+ }
+ ((GeometryArrayRetained)r.geometry()).assignDlistId();
+ renderBin.addGeometryDlist(r);
+ }
+ else {
+ if (secondaryRenderMethod == null)
+ secondaryRenderMethod = cachedVertexArrayRenderMethod;
+ if (vertexArrayRenderAtomList == null) {
+ vertexArrayRenderAtomList = r;
+ }
+ else {
+ r.next = vertexArrayRenderAtomList;
+ vertexArrayRenderAtomList.prev = r;
+ vertexArrayRenderAtomList = r;
+ }
+ // For indexed geometry there is no need to lock since
+ // the mirror is changed only when the renderer is not
+ // running
+ // For indexed geometry, if use_coord is set, then either we
+ // are using the index geometry as is or we will be unindexifying
+ // on the fly, so its better to loc
+ GeometryArrayRetained geo = (GeometryArrayRetained)r.geometry();
+ if (!(geo instanceof IndexedGeometryArrayRetained) ||
+ ((geo.vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0)) {
+ renderBin.addGeometryToLockList(geo);
+ // Add the geometry to the dirty list only if the geometry is by
+ // refernce and there is color and we need to use alpha and its
+ // not multiScreen
+ if ((( geo.vertexFormat & GeometryArray.BY_REFERENCE)!=0) &&
+ (geo.c4fAllocated == 0) &&
+ ((geo.vertexFormat & GeometryArray.COLOR) != 0) &&
+ useAlpha &&
+ !renderBin.multiScreen) {
+ renderBin.addDirtyReferenceGeometry(geo);
+ }
+ }
+ }
+
+ }
+ addRAs = addRAs.nextAdd;
+ renderAtom.nextAdd = null;
+ renderAtom.prevAdd = null;
+ if (renderAtom.isOriented()) {
+ renderBin.orientedRAs.add(renderAtom);
+
+ }
+ // If transparent and not in bg geometry and is depth sorted transparency
+ if (!isOpaqueOrInOG && (textureBin.attributeBin.environmentSet.lightBin.geometryBackground == null)&&
+ (renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY)) {
+ GeometryRetained geo = null;
+ int k = 0;
+ while (geo == null && k < renderAtom.rListInfo.length) {
+ geo = renderAtom.rListInfo[k].geometry();
+ k++;
+ }
+ if (geo != null) {
+ if (renderAtom.parentTInfo != null && renderAtom.parentTInfo[k-1] != null) {
+ renderBin.updateTransparentInfo(renderAtom);
+ }
+ // Newly added renderAtom
+ else {
+ renderBin.addTransparentObject(renderAtom);
+ }
+ }
+ // Moving within the renderBin
+
+ }
+ }
+
+ if ((primaryMoleculeType &DLIST_MOLECULE) != 0 && primaryChanged) {
+
+ // If a renderAtom is added to the display list
+ // structure then add this to the dirty list of rm
+ // for which the display list needs to be recreated
+ renderBin.addDirtyRenderMolecule(this);
+ vwcBounds.set(null);
+ r = primaryRenderAtomList;
+ while (r != null) {
+ vwcBounds.combine(r.renderAtom.localeVwcBounds);
+ r = r.next;
+ }
+ primaryChanged = false;
+ }
+
+
+ if ((onUpdateList & LOCALE_CHANGED) != 0) {
+ handleLocaleChange();
+ }
+
+ if (locale != renderBin.locale) {
+ translate();
+ }
+ }
+ else {
+ // The flag LOCALE_CHANGED only gets sets when there is a new additon
+ // There are cases when RM updateObject() get called (due to addition
+ // in renderBin - see processTransformChanged()), we need to
+ // evaluate locale change for this case as well
+ if (renderBin.localeChanged) {
+ handleLocaleChange();
+ }
+
+ if (locale != renderBin.locale) {
+ translate();
+ }
+
+ if ((onUpdateList & UPDATE_BACKGROUND_TRANSFORM) != 0) {
+ i = localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD];
+ localeLocalToVworld[i].getRotation(infLocalToVworld[i]);
+ }
+
+ // No new renderAtoms were added, but need to
+ // recompute vwcBounds in response to xform change
+ if ((onUpdateList & BOUNDS_RECOMPUTE_UPDATE) != 0) {
+ vwcBounds.set(null);
+ r = primaryRenderAtomList;
+ while (r != null) {
+ vwcBounds.combine(r.renderAtom.localeVwcBounds);
+ r = r.next;
+ }
+ }
+ }
+
+ // Clear all bits except the IN_DIRTY_LIST
+ onUpdateList &= IN_DIRTY_RENDERMOLECULE_LIST;
+
+ numEditingRenderAtoms = numRenderAtoms;
+ }
+
+ boolean canBeInDisplayList(GeometryRetained geo, GeometryAtom ga) {
+ boolean inDL = false;
+ inDL = geo.canBeInDisplayList(ga.alphaEditable);
+ // If can not in DL, then check if all the attrs affecting
+ // it are infrequently changing, if yes then put it
+ // Exclude Morph and indexed-by-ref-use-index-coord only for now
+ // in displayList if OptimizeForSpace if false
+
+ // System.out.println("inDL = "+inDL);
+ // System.out.println("geo.cachedChangedFrequent = "+geo.cachedChangedFrequent);
+ // System.out.println("inDL = "+inDL);
+ // System.out.println("COLOR = "+((((GeometryArrayRetained)geo).vertexFormat&
+ // GeometryArray.COLOR) != 0));
+ // System.out.println("Before: inDL = "+inDL);
+ // System.out.println("VirtualUniverse.mc.buildDisplayListIfPossible = "+VirtualUniverse.mc.buildDisplayListIfPossible);
+ if (VirtualUniverse.mc.buildDisplayListIfPossible &&
+ !inDL &&
+ !(ga.source.sourceNode instanceof MorphRetained ||
+ (geo instanceof GeometryArrayRetained &&
+ ((((GeometryArrayRetained)geo).vertexFormat & (GeometryArray.USE_NIO_BUFFER|GeometryArray.INTERLEAVED)) ==(GeometryArray.USE_NIO_BUFFER|GeometryArray.INTERLEAVED))) ||
+ (geo instanceof IndexedGeometryArrayRetained &&
+ ((((IndexedGeometryArrayRetained)geo).vertexFormat & (GeometryArray.BY_REFERENCE|GeometryArray.USE_COORD_INDEX_ONLY)) == (GeometryArray.BY_REFERENCE|GeometryArray.USE_COORD_INDEX_ONLY))))) {
+ // Check if geometry is frequentlyEditable
+ boolean alphaFreqEditable = ga.source.isAlphaFrequentlyEditable(geo);
+ inDL = !((geo.cachedChangedFrequent != 0) ||
+ ((!(geo instanceof GeometryArrayRetained) && alphaFreqEditable)||
+ (alphaFreqEditable && ((((GeometryArrayRetained)geo).vertexFormat&
+ GeometryArray.COLOR) != 0))));
+ }
+ // System.out.println("After: inDL = "+inDL);
+ return inDL;
+ }
+
+ // If dlist will be altered due to alpha or ignoreVertexColors, then don't
+ // put in a separate dlist that can be shared ...
+ final boolean geoNotAltered(GeometryArrayRetained geo) {
+ return !(((geo.vertexFormat & GeometryArray.COLOR) != 0) &&
+ (textureBin.attributeBin.ignoreVertexColors || useAlpha));
+ }
+
+ int evalRinfoGroupType(RenderAtomListInfo r) {
+ int groupType = 0;
+
+ GeometryRetained geo = r.geometry();
+ if (geo == null)
+ return groupType;
+
+ if ((primaryMoleculeType & (COMPRESSED_MOLECULE |
+ RASTER_MOLECULE |
+ TEXT3D_MOLECULE |
+ ORIENTEDSHAPE3D_MOLECULE)) != 0) {
+ groupType = RenderAtom.OTHER;
+ }
+ else if (canBeInDisplayList(geo, r.renderAtom.geometryAtom)) {
+ // if geometry is under share group we immediate set the
+ // dlistID to something other than -1
+ if ( !((GeometryArrayRetained)geo).isShared ||
+ // if we do a compiled and push the transform down to
+ // Geometry, we can't share the displayList
+ (r.renderAtom.geometryAtom.source.staticTransform != null)) {
+ // If the molecule is already defined to be SEPARATE_DLIST_PER_RINFO_MOLECULE
+ // continue adding in that mode even if it was switched back to
+ // no depth sorted mode
+ // System.out.println("isOpaqueOrInOG ="+isOpaqueOrInOG+" primaryMoleculeType ="+primaryMoleculeType+" renderBin.transpSortMode ="+renderBin.transpSortMode);
+ if (primaryMoleculeType == SEPARATE_DLIST_PER_RINFO_MOLECULE) {
+ groupType = RenderAtom.SEPARATE_DLIST_PER_RINFO;
+ }
+ else {
+ if (isOpaqueOrInOG ||
+ renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE) {
+ groupType = RenderAtom.DLIST;
+ }
+ else {
+ groupType = RenderAtom.SEPARATE_DLIST_PER_RINFO;
+ }
+ }
+
+ } else if (geoNotAltered((GeometryArrayRetained)r.geometry()) ) {
+ groupType = RenderAtom.SEPARATE_DLIST_PER_GEO;
+ }
+ else {
+ groupType = RenderAtom.VARRAY;
+ }
+ }
+ else {
+ groupType = RenderAtom.VARRAY;
+ }
+ return groupType;
+ }
+
+ /**
+ * Adds the given RenderAtom to this RenderMolecule.
+ */
+ void addRenderAtom(RenderAtom renderAtom, RenderBin rb) {
+ int i, n;
+ RenderAtomListInfo r;
+ int index;
+
+ renderAtom.envSet = textureBin.attributeBin.environmentSet;
+ renderAtom.renderMolecule = this;
+ renderAtom.dirtyMask &= ~RenderAtom.NEED_SEPARATE_LOCALE_VWC_BOUNDS;
+
+ AppearanceRetained raApp = renderAtom.geometryAtom.source.appearance;
+
+ MaterialRetained mat = (raApp == null)? null : raApp.material;
+ if (!soleUser && material != mat) {
+ // no longer sole user
+ material = definingMaterial;
+ }
+
+ if ((geometryType & SURFACE) != 0) {
+ PolygonAttributesRetained pgAttrs =
+ (raApp == null)? null : raApp.polygonAttributes;
+ if (!soleUser && polygonAttributes != pgAttrs) {
+ // no longer sole user
+ polygonAttributes = definingPolygonAttributes;
+ }
+
+ }
+ if ((geometryType & LINE) != 0) {
+ LineAttributesRetained lnAttrs =
+ (raApp == null)? null : raApp.lineAttributes;
+ if (!soleUser && lineAttributes != lnAttrs) {
+ // no longer sole user
+ lineAttributes = definingLineAttributes;
+ }
+
+ }
+ if ((geometryType & POINT) != 0) {
+ PointAttributesRetained pnAttrs =
+ (raApp == null)? null : raApp.pointAttributes;
+ if (!soleUser && pointAttributes != pnAttrs) {
+ // no longer sole user
+ pointAttributes = definingPointAttributes;
+ }
+ }
+
+ ColoringAttributesRetained coAttrs =
+ (raApp == null)? null : raApp.coloringAttributes;
+ if (!soleUser && coloringAttributes != coAttrs) {
+ // no longer sole user
+ coloringAttributes = definingColoringAttributes;
+ }
+
+ TransparencyAttributesRetained trAttrs =
+ (raApp == null)? null : raApp.transparencyAttributes;
+ if (!soleUser && transparency != trAttrs) {
+ // no longer sole user
+ transparency = definingTransparency;
+ }
+
+
+
+ // If the renderAtom is being inserted first time, then evaluate
+ // the groupType to determine if need separate localeVwcBounds
+ if (!renderAtom.inRenderBin()) {
+ for (i = 0; i < renderAtom.rListInfo.length; i++) {
+ if (renderAtom.rListInfo[i].geometry() == null)
+ continue;
+ int groupType = evalRinfoGroupType(renderAtom.rListInfo[i]);
+ if (groupType != RenderAtom.DLIST) {
+ renderAtom.dirtyMask |= RenderAtom.NEED_SEPARATE_LOCALE_VWC_BOUNDS;
+ }
+ }
+ }
+ if (renderAtom.removed == this) {
+ // Remove the renderAtom from the list of removeRAs
+ // If this is at the head of the list
+ if (renderAtom == removeRAs) {
+ removeRAs = renderAtom.nextRemove;
+ if (removeRAs != null)
+ removeRAs.prevRemove = null;
+ renderAtom.nextRemove = null;
+ renderAtom.prevRemove = null;
+ }
+ // Somewhere in the middle
+ else {
+ renderAtom.prevRemove.nextRemove = renderAtom.nextRemove;
+ if (renderAtom.nextRemove != null)
+ renderAtom.nextRemove.prevRemove = renderAtom.prevRemove;
+ renderAtom.nextRemove = null;
+ renderAtom.prevRemove = null;
+ }
+
+ renderAtom.removed = null;
+ // Redo any dlist etc, because it has been added
+ for ( i = 0; i < renderAtom.rListInfo.length; i++) {
+ if (renderAtom.rListInfo[i].geometry() == null)
+ continue;
+ if ((renderAtom.rListInfo[i].groupType & RenderAtom.DLIST) != 0)
+ renderBin.addDirtyRenderMolecule(this);
+ else if ((renderAtom.rListInfo[i].groupType & RenderAtom.SEPARATE_DLIST_PER_RINFO) != 0) {
+ renderBin.addDlistPerRinfo.add(renderAtom.rListInfo[i]);
+ }
+ else if ((renderAtom.rListInfo[i].groupType & RenderAtom.SEPARATE_DLIST_PER_GEO) != 0)
+ renderBin.addGeometryDlist(renderAtom.rListInfo[i]);
+
+ }
+ if (removeRAs == null)
+ rb.removeRenderAtomInRMList.remove(this);
+ }
+ else {
+ // Add this renderAtom to the addList
+ if (addRAs == null) {
+ addRAs = renderAtom;
+ renderAtom.nextAdd = null;
+ renderAtom.prevAdd = null;
+ }
+ else {
+ renderAtom.nextAdd = addRAs;
+ renderAtom.prevAdd = null;
+ addRAs.prevAdd = renderAtom;
+ addRAs = renderAtom;
+ }
+ renderAtom.added = this;
+ if (onUpdateList == 0)
+ rb.objUpdateList.add(this);
+ onUpdateList |= NEW_RENDERATOMS_UPDATE;
+
+ }
+ if (renderBin.localeChanged && !doInfinite) {
+ if (onUpdateList == 0)
+ rb.objUpdateList.add(this);
+ onUpdateList |= LOCALE_CHANGED;
+ }
+
+ // inform the texture bin that this render molecule is no longer
+ // in zombie state
+
+ if (numEditingRenderAtoms == 0) {
+ textureBin.incrActiveRenderMolecule();
+ }
+ numEditingRenderAtoms++;
+ }
+
+ /**
+ * Removes the given RenderAtom from this RenderMolecule.
+ */
+ void removeRenderAtom(RenderAtom r) {
+ int index;
+
+ r.renderMolecule = null;
+ if (r.added == this) {
+ //Remove this renderAtom from the addRAs list
+
+ // If this is at the head of the list
+ if (r == addRAs) {
+ addRAs = r.nextAdd;
+ if (addRAs != null)
+ addRAs.prevAdd = null;
+ r.nextAdd = null;
+ r.prevAdd = null;
+ }
+ // Somewhere in the middle
+ else {
+ r.prevAdd.nextAdd = r.nextAdd;
+ if (r.nextAdd != null)
+ r.nextAdd.prevAdd = r.prevAdd;
+ r.nextAdd = null;
+ r.prevAdd = null;
+ }
+
+ r.added = null;
+ r.envSet = null;
+ // If the number of renderAtoms is zero, and it is on the
+ // update list for adding new renderatroms only (not for
+ // bounds update), then remove this rm from the update list
+
+ // Might be expensive to remove this entry from the renderBin
+ // objUpdateList, just let it call the renderMolecule
+ /*
+ if (addRAs == null) {
+ if (onUpdateList == NEW_RENDERATOMS_UPDATE){
+ renderBin.objUpdateList.remove(renderBin.objUpdateList.indexOf(this));
+ }
+ onUpdateList &= ~NEW_RENDERATOMS_UPDATE;
+ }
+ */
+
+ }
+ else {
+ // Add this renderAtom to the remove list
+ if (removeRAs == null) {
+ removeRAs = r;
+ r.nextRemove = null;
+ r.prevRemove = null;
+ }
+ else {
+ r.nextRemove = removeRAs;
+ r.prevRemove = null;
+ removeRAs.prevRemove = r;
+ removeRAs = r;
+ }
+ r.removed = this;
+ }
+
+ // Add it to the removeRenderAtom List , in case the renderMolecule
+ // needs to be removed
+ if (!renderBin.removeRenderAtomInRMList.contains(this)) {
+ renderBin.removeRenderAtomInRMList.add(this);
+ }
+
+ // decrement the number of editing render atoms in this render molecule
+ numEditingRenderAtoms--;
+
+ // if there is no more editing render atoms, inform the texture bin
+ // that this render molecule is going to zombie state
+
+ if (numEditingRenderAtoms == 0) {
+ textureBin.decrActiveRenderMolecule();
+ }
+ }
+
+ /**
+ * Recalculates the vwcBounds for a RenderMolecule
+ */
+ void recalcBounds() {
+ RenderAtomListInfo ra;
+
+ if (primaryRenderMethod ==
+ VirtualUniverse.mc.getDisplayListRenderMethod()) {
+ vwcBounds.set(null);
+ ra = primaryRenderAtomList;
+ while (ra != null) {
+ vwcBounds.combine(ra.renderAtom.localeVwcBounds);
+ ra = ra.next;
+ }
+ }
+ }
+
+ void evalAlphaUsage(RenderingAttributesRetained renderAttrs,
+ TextureUnitStateRetained[] texUnits) {
+ RenderingAttributesRetained ra;
+ TextureAttributesRetained ta;
+ boolean alphaBlend, alphaTest, textureBlend = false;
+
+ alphaBlend =
+ definingTransparency != null &&
+ definingTransparency.transparencyMode !=
+ TransparencyAttributes.NONE &&
+ (VirtualUniverse.mc.isD3D() ||
+ !VirtualUniverse.mc.isD3D() &&
+ definingTransparency.transparencyMode !=
+ TransparencyAttributes.SCREEN_DOOR);
+
+
+ if (texUnits != null) {
+ for (int i = 0;
+ textureBlend == false && i < texUnits.length;
+ i++) {
+ if (texUnits[i] != null &&
+ texUnits[i].texAttrs != null) {
+ textureBlend = textureBlend ||
+ (texUnits[i].texAttrs.textureMode ==
+ TextureAttributes.BLEND);
+ }
+ }
+ }
+
+ alphaTest =
+ renderAttrs != null && renderAttrs.alphaTestFunction != RenderingAttributes.ALWAYS;
+
+ boolean oldUseAlpha = useAlpha;
+ useAlpha = alphaBlend || alphaTest || textureBlend;
+
+ if( !oldUseAlpha && useAlpha) {
+ GeometryArrayRetained geo = null;
+
+ if(vertexArrayRenderAtomList != null)
+ geo = (GeometryArrayRetained)vertexArrayRenderAtomList.geometry();
+
+ if(geo != null) {
+ if (!(geo instanceof IndexedGeometryArrayRetained) ||
+ ((geo.vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0)) {
+ renderBin.addGeometryToLockList(geo);
+ // Add the geometry to the dirty list only if the geometry is by
+ // refernce and there is color and we need to use alpha and its
+ // not multiScreen
+ if ((( geo.vertexFormat & GeometryArray.BY_REFERENCE)!=0) &&
+ (geo.c4fAllocated == 0) &&
+ ((geo.vertexFormat & GeometryArray.COLOR) != 0) &&
+ useAlpha &&
+ !renderBin.multiScreen) {
+ renderBin.addDirtyReferenceGeometry(geo);
+ }
+ }
+ }
+ }
+ }
+
+ final boolean isSwitchOn() {
+ // The switchOn status of the entire RM can be determined
+ // by the switchOn status of any renderAtoms below.
+ // This is possible because renderAtoms generated from a common
+ // switch branch are placed in the same renderMolecule
+ if (primaryRenderAtomList != null) {
+ return primaryRenderAtomList.renderAtom.geometryAtom.
+ source.switchState.lastSwitchOn;
+
+ }
+
+ if (vertexArrayRenderAtomList != null) {
+ return vertexArrayRenderAtomList.renderAtom.geometryAtom.
+ source.switchState.lastSwitchOn;
+
+ }
+
+ if (separateDlistRenderAtomList != null) {
+ return separateDlistRenderAtomList.renderAtom.geometryAtom.
+ source.switchState.lastSwitchOn;
+ }
+ return false;
+ }
+
+ /**
+ * Renders this RenderMolecule
+ */
+ boolean render(Canvas3D cv, int pass, int dirtyBits) {
+
+ boolean isVisible = isSwitchOn();
+
+ if (!isVisible) {
+ return false;
+ }
+
+ isVisible = false;
+
+ // include this LightBin to the to-be-updated list in Canvas
+ cv.setStateToUpdate(Canvas3D.RENDERMOLECULE_BIT, this);
+
+ boolean modeSupportDL = true;
+ isNonUniformScale = !trans[localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD]].isCongruent();
+ // We have to dynamically switch between using displaymode
+ // mode or not instead of decide in canBeInDisplayList(),
+ // since polygonAttribute can be change by editable Appearance
+ // or editable polygonAttribute which mode we can't take
+ // advantage of display list mode in many cases just because
+ // there are three special cases to handle.
+
+ // Another case for punting to vertex array is if pass specifies
+ // something other than -1. That means, we are in the
+ // multi-texturing multi-pass case. Then we'll use vertex array
+ // instead. Or the length of the texCoordSetMap is greater than
+ // the number of texture units supported by the Canvas, then
+ // we'll have to punt to vertex array as well.
+
+ if ((pass != TextureBin.USE_DISPLAYLIST) ||
+ (texCoordSetMapLen > cv.numTexCoordSupported) ||
+ (VirtualUniverse.mc.isD3D() &&
+ (((definingPolygonAttributes != null) &&
+ ((isQuadGeometryArray &&
+ (definingPolygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE))||
+ (isTriGeometryArray &&
+ (definingPolygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT)))) ||
+ cv.texLinearMode))) {
+ modeSupportDL = false;
+ }
+
+ /*
+ System.out.println("texCoord " + texCoordSetMapLen + " " +
+ cv.numTexCoordSupported + " " + modeSupportDL);
+
+ System.out.println("primaryMoleculeType = "+primaryMoleculeType+" primaryRenderAtomList ="+primaryRenderAtomList+" separateDlistRenderAtomList ="+separateDlistRenderAtomList+" vertexArrayRenderAtomList ="+vertexArrayRenderAtomList);
+ */
+ // Send down the model view only once, if its not of type text
+ if ((primaryMoleculeType & (TEXT3D_MOLECULE| ORIENTEDSHAPE3D_MOLECULE)) == 0) {
+ if (primaryRenderAtomList != null) {
+ if ((primaryRenderMethod != VirtualUniverse.mc.getDisplayListRenderMethod()) ||
+ modeSupportDL) {
+ if (primaryMoleculeType != SEPARATE_DLIST_PER_RINFO_MOLECULE) {
+
+ if ((primaryRenderMethod != VirtualUniverse.mc.getDisplayListRenderMethod()) &&
+ (pass == TextureBin.USE_DISPLAYLIST)) {
+ pass = TextureBin.USE_VERTEXARRAY;
+ }
+ if (primaryRenderMethod.render(this, cv, pass, primaryRenderAtomList,dirtyBits))
+ isVisible = true;
+ }
+ else {
+ if (renderBin.dlistRenderMethod.renderSeparateDlistPerRinfo(this, cv, pass,primaryRenderAtomList,dirtyBits))
+ isVisible = true;
+
+ }
+ } else {
+ if (pass == TextureBin.USE_DISPLAYLIST) {
+ pass = TextureBin.USE_VERTEXARRAY;
+ }
+ if(cachedVertexArrayRenderMethod.render(this, cv, pass,
+ primaryRenderAtomList,
+ dirtyBits)) {
+ isVisible = true;
+ }
+ }
+ }
+ }
+ else { // TEXT3D or ORIENTEDSHAPE3D
+ if (primaryRenderAtomList != null) {
+ if (pass == TextureBin.USE_DISPLAYLIST) {
+ pass = TextureBin.USE_VERTEXARRAY;
+ }
+ if(primaryRenderMethod.render(this, cv, pass, primaryRenderAtomList,
+ dirtyBits)) {
+ isVisible = true;
+ }
+ }
+ }
+
+ if (separateDlistRenderAtomList != null) {
+ if (modeSupportDL) {
+ if(renderBin.dlistRenderMethod.
+ renderSeparateDlists(this, cv, pass,
+ separateDlistRenderAtomList,
+ dirtyBits)) {
+ isVisible = true;
+ }
+
+ } else {
+ if (pass == TextureBin.USE_DISPLAYLIST) {
+ pass = TextureBin.USE_VERTEXARRAY;
+ }
+ if(cachedVertexArrayRenderMethod.render(this, cv, pass,
+ separateDlistRenderAtomList,
+ dirtyBits)) {
+ isVisible = true;
+ }
+ }
+
+ }
+
+ // TODO: In the case of independent primitives such as quads,
+ // it would still be better to call multi draw arrays
+ if (vertexArrayRenderAtomList != null) {
+ if (pass == TextureBin.USE_DISPLAYLIST) {
+ pass = TextureBin.USE_VERTEXARRAY;
+ }
+ if(cachedVertexArrayRenderMethod.render(this, cv, pass,
+ vertexArrayRenderAtomList,
+ dirtyBits)) {
+ isVisible = true;
+ }
+ }
+ return isVisible;
+ }
+
+ void updateAttributes(Canvas3D cv, int dirtyBits) {
+
+
+ boolean setTransparency = false;
+
+ // If this is a beginning of a frame OR diff. geometryType
+ // then reload everything for the first rendermolecule
+ // System.out.println("updateAttributes");
+ int bitMask = geometryType | Canvas3D.MATERIAL_DIRTY|
+ Canvas3D.COLORINGATTRS_DIRTY|
+ Canvas3D.TRANSPARENCYATTRS_DIRTY;
+
+ /*
+ System.out.println("RM : updateAttributes " + this +
+ " dirtyBits is " + dirtyBits);
+ */
+
+ // If beginning of a frame then reload all the attributes
+ if ((cv.canvasDirty & bitMask) != 0) {
+ if ((geometryType & SURFACE) != 0) {
+ if (definingPolygonAttributes == null) {
+ cv.resetPolygonAttributes(cv.ctx);
+ } else {
+ definingPolygonAttributes.updateNative(cv.ctx);
+ }
+ cv.polygonAttributes = polygonAttributes;
+ }
+ if ((geometryType & LINE) != 0) {
+ if (definingLineAttributes == null) {
+ cv.resetLineAttributes(cv.ctx);
+ } else {
+ definingLineAttributes.updateNative(cv.ctx);
+ }
+ cv.lineAttributes = lineAttributes;
+ }
+ if ((geometryType & POINT) != 0) {
+ if (definingPointAttributes == null) {
+ cv.resetPointAttributes(cv.ctx);
+ } else {
+ definingPointAttributes.updateNative(cv.ctx);
+ }
+ cv.pointAttributes = pointAttributes;
+ }
+
+ if (definingTransparency == null) {
+ cv.resetTransparency(cv.ctx, geometryType,
+ polygonMode, lineAA, pointAA);
+ } else {
+ definingTransparency.updateNative(cv.ctx,
+ alpha, geometryType,
+ polygonMode, lineAA,
+ pointAA);
+ }
+ cv.transparency = transparency;
+
+ if (definingMaterial == null) {
+ cv.updateMaterial(cv.ctx, red, green, blue, alpha);
+ } else {
+ definingMaterial.updateNative(cv.ctx,
+ red, green, blue, alpha,
+ enableLighting);
+ }
+ cv.material = material;
+ cv.enableLighting = enableLighting;
+
+ if (definingColoringAttributes == null) {
+ cv.resetColoringAttributes(cv.ctx, red, green, blue,
+ alpha, enableLighting);
+ } else {
+ definingColoringAttributes.updateNative(cv.ctx,
+ dRed,
+ dBlue,
+ dGreen,alpha,
+ enableLighting);
+ }
+ cv.coloringAttributes = coloringAttributes;
+
+ // Use Object instead of AppearanceRetained class for
+ // state caching optimation for memory performance
+ cv.appHandle = appHandle;
+ }
+
+ // assuming neighbor dirty bits ORing is implemented
+ // note that we need to set it to ALL_DIRTY at the
+ // begining of textureBin first and only do the ORing
+ // whenever encounter a non-visible rm
+
+ else if (cv.renderMolecule != this && (dirtyBits != 0)) {
+
+ // no need to download states if appHandle is the same
+ if (cv.appHandle != appHandle) {
+
+ // Check if the attribute bundle in the canvas is the same
+ // as the attribute bundle in this renderMolecule
+
+ if (cv.transparency != transparency &&
+ (dirtyBits & TRANSPARENCY_DIRTY) != 0) {
+ setTransparency = true;
+ if (definingTransparency == null) {
+
+ cv.resetTransparency(cv.ctx, geometryType,
+ polygonMode, lineAA, pointAA);
+ } else {
+ definingTransparency.updateNative(cv.ctx, alpha,
+ geometryType, polygonMode,
+ lineAA, pointAA);
+ }
+ cv.transparency = transparency;
+ }
+
+ if (setTransparency || ((cv.enableLighting != enableLighting) ||
+ (cv.material != material) &&
+ (dirtyBits & MATERIAL_DIRTY) != 0)){
+ if (definingMaterial == null) {
+ cv.updateMaterial(cv.ctx, red, green, blue, alpha);
+ } else {
+ definingMaterial.updateNative(cv.ctx, red, green,
+ blue, alpha,
+ enableLighting);
+ }
+ cv.material = material;
+ cv.enableLighting = enableLighting;
+ }
+
+ if (((geometryType & SURFACE) != 0) &&
+ cv.polygonAttributes != polygonAttributes &&
+ (dirtyBits & POLYGONATTRS_DIRTY) != 0) {
+
+ if (definingPolygonAttributes == null) {
+ cv.resetPolygonAttributes(cv.ctx);
+ } else {
+ definingPolygonAttributes.updateNative(cv.ctx);
+ }
+ cv.polygonAttributes = polygonAttributes;
+ }
+
+ if (((geometryType & LINE) != 0) &&
+ cv.lineAttributes != lineAttributes &&
+ (dirtyBits & LINEATTRS_DIRTY) != 0) {
+
+ if (definingLineAttributes == null) {
+ cv.resetLineAttributes(cv.ctx);
+ } else {
+ definingLineAttributes.updateNative(cv.ctx);
+ }
+ cv.lineAttributes = lineAttributes;
+ }
+
+ if (((geometryType & POINT) != 0) &&
+ cv.pointAttributes != pointAttributes &&
+ (dirtyBits & POINTATTRS_DIRTY) != 0) {
+
+ if (definingPointAttributes == null) {
+ cv.resetPointAttributes(cv.ctx);
+ } else {
+ definingPointAttributes.updateNative(cv.ctx);
+ }
+ cv.pointAttributes = pointAttributes;
+ }
+
+ // Use Object instead of AppearanceRetained class for
+ // state caching optimation for memory performance
+ cv.appHandle = appHandle;
+ }
+ // no state caching for color attrs, which can also be
+ // changed by primitive with colors
+ if(setTransparency || ((dirtyBits & COLORINGATTRS_DIRTY) != 0)) {
+
+ if (definingColoringAttributes == null) {
+ cv.resetColoringAttributes(cv.ctx,
+ red, green, blue, alpha,
+ enableLighting);
+ } else {
+ definingColoringAttributes.updateNative(cv.ctx,
+ dRed,
+ dBlue,
+ dGreen,alpha,
+ enableLighting);
+
+ }
+ cv.coloringAttributes = coloringAttributes;
+ }
+
+ }
+
+ if ((primaryMoleculeType & (TEXT3D_MOLECULE| ORIENTEDSHAPE3D_MOLECULE)) == 0) {
+ Transform3D modelMatrix =
+ trans[localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD]];
+
+ if (cv.modelMatrix != modelMatrix) {
+ cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat,
+ modelMatrix);
+ }
+ }
+
+ cv.canvasDirty &= ~bitMask;
+ cv.renderMolecule = this;
+ }
+
+ void transparentSortRender(Canvas3D cv, int pass, TransparentRenderingInfo tinfo) {
+
+ Transform3D modelMatrix =
+ trans[localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD]];
+
+ // include this LightBin to the to-be-updated list in Canvas
+ cv.setStateToUpdate(Canvas3D.RENDERMOLECULE_BIT, this);
+
+
+ boolean modeSupportDL = true;
+
+ // We have to dynamically switch between using displaymode
+ // mode or not instead of decide in canBeInDisplayList(),
+ // since polygonAttribute can be change by editable Appearance
+ // or editable polygonAttribute which mode we can't take
+ // advantage of display list mode in many cases just because
+ // there are three special cases to handle.
+
+ // Another case for punting to vertex array is if pass specifies
+ // something other than -1. That means, we are in the
+ // multi-texturing multi-pass case. Then we'll use vertex array
+ // instead.
+
+ if ((pass != TextureBin.USE_DISPLAYLIST) ||
+ (texCoordSetMapLen > cv.numTexCoordSupported) ||
+ (VirtualUniverse.mc.isD3D() &&
+ (((definingPolygonAttributes != null) &&
+ ((isQuadGeometryArray &&
+ (definingPolygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE)) ||
+ (isTriGeometryArray &&
+ (definingPolygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT))))
+ ||
+ cv.texLinearMode))) {
+ modeSupportDL = false;
+ }
+
+ // System.out.println("r.isOpaque = "+isOpaque+" rinfo = "+tinfo.rInfo+" groupType = "+tinfo.rInfo.groupType);
+ // Only support individual dlist or varray
+ // If this rInfo is a part of a bigger dlist, render as VA
+ // TODO: What to do with Text3D, Raster, CG?
+ if ((tinfo.rInfo.groupType & RenderAtom.SEPARATE_DLIST_PER_RINFO) != 0) {
+ RenderAtomListInfo save= tinfo.rInfo.next;
+ // Render only one geometry
+ tinfo.rInfo.next = null;
+ // System.out.println("cachedVertexArrayRenderMethod = "+cachedVertexArrayRenderMethod);
+ // System.out.println("tinfo.rInfo = "+tinfo.rInfo);
+ if (modeSupportDL) {
+ renderBin.dlistRenderMethod.renderSeparateDlistPerRinfo(this, cv, pass,
+ tinfo.rInfo,
+ ALL_DIRTY_BITS);
+ }
+ else {
+ cachedVertexArrayRenderMethod.render(this, cv, pass, tinfo.rInfo,ALL_DIRTY_BITS);
+ }
+ tinfo.rInfo.next = save;
+ }
+ else if ((tinfo.rInfo.groupType & (RenderAtom.VARRAY| RenderAtom.DLIST)) != 0) {
+ RenderAtomListInfo save= tinfo.rInfo.next;
+ // Render only one geometry
+ tinfo.rInfo.next = null;
+ // System.out.println("cachedVertexArrayRenderMethod = "+cachedVertexArrayRenderMethod);
+ // System.out.println("tinfo.rInfo = "+tinfo.rInfo);
+ cachedVertexArrayRenderMethod.render(this, cv, pass, tinfo.rInfo,
+ ALL_DIRTY_BITS);
+ tinfo.rInfo.next = save;
+ }
+
+ // Only support individual dlist or varray
+ else if ((tinfo.rInfo.groupType & RenderAtom.SEPARATE_DLIST_PER_GEO) != 0) {
+ RenderAtomListInfo save= tinfo.rInfo.next;
+ tinfo.rInfo.next = null;
+ if (modeSupportDL) {
+ renderBin.dlistRenderMethod.renderSeparateDlists(this, cv, pass,
+ tinfo.rInfo,
+ ALL_DIRTY_BITS);
+ }
+ else {
+ cachedVertexArrayRenderMethod.render(this, cv, pass, tinfo.rInfo,
+ ALL_DIRTY_BITS);
+ }
+ tinfo.rInfo.next = save;
+ }
+ else {
+ RenderAtomListInfo save= tinfo.rInfo.next;
+ primaryRenderMethod.render(this, cv, pass, primaryRenderAtomList,
+ ALL_DIRTY_BITS);
+ tinfo.rInfo.next = save;
+ }
+
+ }
+
+
+ /**
+ * This render method is used to render the transparency attributes.
+ * It is used in the multi-texture multi-pass case to reset the
+ * transparency attributes to what it was
+ */
+ void updateTransparencyAttributes(Canvas3D cv) {
+ if (definingTransparency == null) {
+ cv.resetTransparency(cv.ctx, geometryType, polygonMode,
+ lineAA, pointAA);
+ } else {
+ definingTransparency.updateNative(cv.ctx, alpha, geometryType,
+ polygonMode, lineAA, pointAA);
+ }
+ }
+
+ void updateDisplayList(Canvas3D cv) {
+ // This function only gets called when primaryRenderAtomsList are
+ if (primaryRenderAtomList != null) {
+ ((DisplayListRenderMethod)primaryRenderMethod).buildDisplayList(this, cv);
+ }
+ }
+
+ void releaseAllPrimaryDisplayListID() {
+
+ if (primaryRenderAtomList != null) {
+ if (primaryMoleculeType == SEPARATE_DLIST_PER_RINFO_MOLECULE) {
+ RenderAtomListInfo ra = primaryRenderAtomList;
+ int id;
+
+ while (ra != null) {
+ id = ra.renderAtom.dlistIds[ra.index];
+
+ if (id > 0) {
+ VirtualUniverse.mc.freeDisplayListId(new Integer(id));
+ ra.renderAtom.dlistIds[ra.index] = -1;
+ }
+ ra = ra.next;
+ }
+ }
+ else if (primaryMoleculeType == DLIST_MOLECULE) {
+ if (displayListIdObj != null) {
+ VirtualUniverse.mc.freeDisplayListId(displayListIdObj);
+ displayListIdObj = null;
+ displayListId = -1;
+ }
+ }
+ }
+
+ }
+
+ void releaseAllPrimaryDisplayListResources(Canvas3D cv) {
+ if (primaryRenderAtomList != null) {
+ if (primaryMoleculeType == SEPARATE_DLIST_PER_RINFO_MOLECULE) {
+ RenderAtomListInfo ra = primaryRenderAtomList;
+ int id;
+ while (ra != null) {
+ id = ra.renderAtom.dlistIds[ra.index];
+ if (id > 0) {
+ cv.freeDisplayList(cv.ctx, id);
+ }
+ ra = ra.next;
+ }
+ }
+ else if (primaryMoleculeType == DLIST_MOLECULE) {
+ if (displayListId > 0) {
+ cv.freeDisplayList(cv.ctx, displayListId);
+ }
+ }
+ }
+ }
+
+ void updateAllPrimaryDisplayLists(Canvas3D cv) {
+ // This function only gets called when primaryRenderAtomsList are
+ if (primaryRenderAtomList != null) {
+ if (primaryMoleculeType == SEPARATE_DLIST_PER_RINFO_MOLECULE) {
+ RenderAtomListInfo ra = primaryRenderAtomList;
+ while (ra != null) {
+ renderBin.dlistRenderMethod.buildDlistPerRinfo(ra, this, cv);
+ ra = ra.next;
+ }
+ }
+ else if(primaryMoleculeType == DLIST_MOLECULE) {
+ ((DisplayListRenderMethod)primaryRenderMethod).buildDisplayList(this, cv);
+ }
+ }
+ }
+
+ void checkEquivalenceWithBothNeighbors(int dirtyBits) {
+ RenderMolecule leftRm = prev;
+ RenderMolecule rightRm = next;
+ dirtyAttrsAcrossRms = ALL_DIRTY_BITS;
+ boolean reload_color = true;
+
+ if (prev != null) {
+ checkEquivalenceWithLeftNeighbor(prev, dirtyBits);
+ }
+ if (next != null) {
+ next.checkEquivalenceWithLeftNeighbor(this, dirtyBits);
+ }
+ }
+
+ boolean reloadColor(RenderMolecule rm) {
+ if (((rm.vertexFormat & GeometryArray.COLOR) == 0) ||
+ (((rm.vertexFormat & GeometryArray.COLOR) != 0) &&
+ (vertexFormat & GeometryArray.COLOR) != 0)) {
+ return false;
+ }
+ return true;
+ }
+
+ void checkEquivalenceWithLeftNeighbor(RenderMolecule rm, int dirtyBits) {
+ boolean reload_color = reloadColor(rm);
+ // TODO : For now ignore the dirtyBits being sent in
+ dirtyAttrsAcrossRms = ALL_DIRTY_BITS ;
+
+
+
+ // There is some interdepenency between the different components
+ // in the way it is sent down to the native code
+ // Material is affected by transparency and coloring attrs
+ // Transparency is affected by poly/line/pointAA
+ // ColoringAttrs is affected by material and transaparency
+ int materialColoringDirty = (MATERIAL_DIRTY |
+ TRANSPARENCY_DIRTY |
+ COLORINGATTRS_DIRTY);
+
+ int transparencyDirty = (TRANSPARENCY_DIRTY|
+ POLYGONATTRS_DIRTY |
+ LINEATTRS_DIRTY |
+ POINTATTRS_DIRTY);
+
+ if ((dirtyAttrsAcrossRms & POLYGONATTRS_DIRTY) != 0) {
+ if (rm.geometryType == geometryType &&
+ (rm.polygonAttributes == polygonAttributes ||
+ ((rm.definingPolygonAttributes != null) &&
+ (rm.definingPolygonAttributes.equivalent(definingPolygonAttributes)))))
+ dirtyAttrsAcrossRms &= ~POLYGONATTRS_DIRTY;
+
+ }
+
+ if ((dirtyAttrsAcrossRms & POINTATTRS_DIRTY) != 0) {
+ if (rm.geometryType == geometryType &&
+ ((rm.pointAttributes == pointAttributes) ||
+ ((rm.definingPointAttributes != null) &&
+ (rm.definingPointAttributes.equivalent(definingPointAttributes)))))
+ dirtyAttrsAcrossRms &= ~POINTATTRS_DIRTY;
+
+ }
+
+ if ((dirtyAttrsAcrossRms & LINEATTRS_DIRTY) != 0) {
+ if (rm.geometryType == geometryType &&
+ ((rm.lineAttributes == lineAttributes) ||
+ ((rm.definingLineAttributes != null) &&
+ (rm.definingLineAttributes.equivalent(definingLineAttributes)))))
+ dirtyAttrsAcrossRms &= ~LINEATTRS_DIRTY;
+ }
+
+ if ((dirtyAttrsAcrossRms & materialColoringDirty) != 0) {
+ if (materialEquivalent(rm, reload_color)) {
+ dirtyAttrsAcrossRms &= ~MATERIAL_DIRTY;
+ }
+ else {
+ dirtyAttrsAcrossRms |= MATERIAL_DIRTY;
+ }
+ }
+
+
+
+
+ if ((dirtyAttrsAcrossRms & materialColoringDirty) != 0) {
+ if (coloringEquivalent(rm, reload_color)) {
+ dirtyAttrsAcrossRms &= ~COLORINGATTRS_DIRTY;
+ }
+ else {
+ dirtyAttrsAcrossRms |= COLORINGATTRS_DIRTY;
+ }
+ }
+
+ if ((dirtyAttrsAcrossRms & transparencyDirty) != 0) {
+ if (transparencyEquivalent(rm)) {
+ dirtyAttrsAcrossRms &= ~TRANSPARENCY_DIRTY;
+ }
+ else {
+ dirtyAttrsAcrossRms |= TRANSPARENCY_DIRTY;
+ }
+ }
+ }
+ void translate() {
+ // System.out.println("onUpdateList = "+onUpdateList+" renderBin.localeChanged = "+renderBin.localeChanged+" rm = "+this);
+ int i = localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD];
+
+ localeLocalToVworld[i].mat[0] = localToVworld[i].mat[0];
+ localeLocalToVworld[i].mat[1] = localToVworld[i].mat[1];
+ localeLocalToVworld[i].mat[2] = localToVworld[i].mat[2];
+ localeLocalToVworld[i].mat[3] = localToVworld[i].mat[3] + localeTranslation.x ;
+ localeLocalToVworld[i].mat[4] = localToVworld[i].mat[4];
+ localeLocalToVworld[i].mat[5] = localToVworld[i].mat[5];
+ localeLocalToVworld[i].mat[6] = localToVworld[i].mat[6];
+ localeLocalToVworld[i].mat[7] = localToVworld[i].mat[7]+ localeTranslation.y;
+ localeLocalToVworld[i].mat[8] = localToVworld[i].mat[8];
+ localeLocalToVworld[i].mat[9] = localToVworld[i].mat[9];
+ localeLocalToVworld[i].mat[10] = localToVworld[i].mat[10];
+ localeLocalToVworld[i].mat[11] = localToVworld[i].mat[11]+ localeTranslation.z;
+ localeLocalToVworld[i].mat[12] = localToVworld[i].mat[12];
+ localeLocalToVworld[i].mat[13] = localToVworld[i].mat[13];
+ localeLocalToVworld[i].mat[14] = localToVworld[i].mat[14];
+ localeLocalToVworld[i].mat[15] = localToVworld[i].mat[15];
+ // System.out.println("rm = "+this+" localTovworld = "+localeLocalToVworld[i]+" localeTranslation = "+localeTranslation);
+ }
+
+
+ boolean isOpaque() {
+ if (!VirtualUniverse.mc.isD3D()) {
+ // D3D doesn't support line/point antialiasing
+ if ((geometryType & SURFACE) != 0) {
+ if (definingPolygonAttributes != null) {
+ if ((definingPolygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_POINT) &&
+ (definingPointAttributes != null) &&
+ definingPointAttributes.pointAntialiasing) {
+ return false;
+ } else if ((definingPolygonAttributes.polygonMode ==
+ PolygonAttributes.POLYGON_LINE) &&
+ (definingLineAttributes != null) &&
+ definingLineAttributes.lineAntialiasing) {
+ return false;
+ }
+ }
+ } else if ((geometryType & POINT) != 0) {
+ if ((definingPointAttributes != null) &&
+ definingPointAttributes.pointAntialiasing) {
+ return false;
+ }
+ } else if ((geometryType & LINE) != 0) {
+ if ((definingLineAttributes != null) &&
+ definingLineAttributes.lineAntialiasing) {
+ return false;
+ }
+ }
+ return ((definingTransparency == null) ||
+ (definingTransparency.transparencyMode ==
+ TransparencyAttributes.NONE) ||
+ (definingTransparency.transparencyMode ==
+ TransparencyAttributes.SCREEN_DOOR));
+ } else {
+ return ((definingTransparency == null) ||
+ (definingTransparency.transparencyMode ==
+ TransparencyAttributes.NONE));
+ }
+ }
+
+
+ boolean updateNodeComponent() {
+ // System.out.println("soleUser = "+soleUser+" rm = "+this);
+ if ((soleUserCompDirty & MATERIAL_DIRTY) != 0) {
+ // Note: this RM is a soleUser(only then this function is called)
+ // and if definingMaterial == material, then the material is freq
+ // changed and therefore is not cloned, only other time it can be
+ // same is when an equivalent material is added in and this can
+ // never be true when a bin is a soleUser of a appearance
+
+ // Evaluate before replacing the old Value
+ if (soleUser) {
+ boolean cloned = definingMaterial != null && definingMaterial != material;
+ // System.out.println("===>Rm = "+this);
+
+ // System.out.println("===> updating node component, cloned = "+cloned+" material.changedFrequent = "+material.changedFrequent);
+ // System.out.println("===> definingMaterial ="+definingMaterial+" material = "+material);
+
+ material = ((AppearanceRetained)appHandle).material;
+ if (material == null)
+ definingMaterial = null;
+ else {
+ if (material.changedFrequent != 0) {
+ definingMaterial = material;
+ }
+ else {
+ // If the one replaced is a cloned copy, then ..
+ if (cloned) {
+ definingMaterial.set(material);
+ }
+ else {
+ definingMaterial = (MaterialRetained)material.clone();
+ }
+ }
+ }
+ }
+ evalMaterialCachedState();
+ }
+ if ((soleUserCompDirty & LINEATTRS_DIRTY) != 0) {
+ if (soleUser) {
+ // Evaluate before replacing the old Value
+ boolean cloned = definingLineAttributes != null && definingLineAttributes != lineAttributes;
+
+ lineAttributes = ((AppearanceRetained)appHandle).lineAttributes;
+ if (lineAttributes == null) {
+ lineAA = false;
+ definingLineAttributes = null;
+ } else {
+ if (lineAttributes.changedFrequent != 0) {
+ definingLineAttributes = lineAttributes;
+ }
+ else {
+ // If the one replaced is a cloned copy, then ..
+ if (cloned) {
+ definingLineAttributes.set(lineAttributes);
+ }
+ else {
+ definingLineAttributes = (LineAttributesRetained)lineAttributes.clone();
+ }
+ }
+ lineAA = definingLineAttributes.lineAntialiasing;
+ }
+ }
+ else {
+ lineAA = definingLineAttributes.lineAntialiasing;
+ }
+ }
+ if ((soleUserCompDirty & POINTATTRS_DIRTY) != 0) {
+ if (soleUser) {
+ // Evaluate before replacing the old Value
+ boolean cloned = definingPointAttributes != null && definingPointAttributes != pointAttributes;
+
+ pointAttributes = ((AppearanceRetained)appHandle).pointAttributes;
+ if (pointAttributes == null) {
+ pointAA = false;
+ definingPointAttributes = null;
+ } else {
+ if (pointAttributes.changedFrequent != 0) {
+ definingPointAttributes = pointAttributes;
+ }
+ else {
+ // If the one replaced is a cloned copy, then ..
+ if (cloned) {
+ definingPointAttributes.set(pointAttributes);
+ }
+ else {
+ definingPointAttributes = (PointAttributesRetained)pointAttributes.clone();
+ }
+ }
+ pointAA = definingPointAttributes.pointAntialiasing;
+ }
+ }
+ else {
+ pointAA = definingPointAttributes.pointAntialiasing;
+ }
+
+ }
+ if ((soleUserCompDirty & POLYGONATTRS_DIRTY) != 0) {
+ if (soleUser) {
+ // Evaluate before replacing the old Value
+ boolean cloned = definingPolygonAttributes != null && definingPolygonAttributes != polygonAttributes;
+
+
+ polygonAttributes = ((AppearanceRetained)appHandle).polygonAttributes;
+
+ if (polygonAttributes == null) {
+ polygonMode = PolygonAttributes.POLYGON_FILL;
+ definingPolygonAttributes = null;
+ } else {
+ if (polygonAttributes.changedFrequent != 0) {
+ definingPolygonAttributes = polygonAttributes;
+ }
+ else {
+ // If the one replaced is a cloned copy, then ..
+ if (cloned) {
+ definingPolygonAttributes.set(polygonAttributes);
+ }
+ else {
+ definingPolygonAttributes = (PolygonAttributesRetained)polygonAttributes.clone();
+ }
+ }
+
+ polygonMode = definingPolygonAttributes.polygonMode;
+ }
+ }
+ else {
+ polygonMode = definingPolygonAttributes.polygonMode;
+ }
+
+ if (polygonMode == PolygonAttributes.POLYGON_LINE) {
+ geometryType |= LINE;
+ } else if (polygonMode == PolygonAttributes.POLYGON_POINT) {
+ geometryType |= POINT;
+ }
+ }
+
+ if ((soleUserCompDirty & TRANSPARENCY_DIRTY) != 0) {
+ if (soleUser) {
+ // Evaluate before replacing the old Value
+ boolean cloned = definingTransparency != null && definingTransparency != transparency;
+ transparency = ((AppearanceRetained)appHandle).transparencyAttributes;
+
+ if (transparency == null) {
+ alpha = 1.0f ;
+ definingTransparency = null;
+ } else {
+ if (transparency.changedFrequent != 0) {
+ definingTransparency = transparency;
+ }
+ else {
+ // If the one replaced is a cloned copy, then ..
+ if (cloned) {
+ definingTransparency.set(transparency);
+ }
+ else {
+ definingTransparency = (TransparencyAttributesRetained)transparency.clone();
+ }
+ }
+
+ alpha = 1.0f - definingTransparency.transparency;
+ }
+ }
+ else {
+ alpha = 1.0f - definingTransparency.transparency;
+ }
+ }
+
+ if ((soleUserCompDirty & COLORINGATTRS_DIRTY) != 0) {
+ if (soleUser) {
+ // Evaluate before replacing the old Value
+ boolean cloned = definingColoringAttributes != null && definingColoringAttributes != coloringAttributes;
+
+ coloringAttributes = ((AppearanceRetained)appHandle).coloringAttributes;
+ // System.out.println("coloringAttributes and soleUser");
+ // System.out.println("coloringAttributes ="+coloringAttributes);
+ if (coloringAttributes == null) {
+ definingColoringAttributes = null;
+ red = 1.0f;
+ green = 1.0f;
+ blue = 1.0f;
+ } else {
+ // System.out.println("coloringAttributes.changedFrequent = "+coloringAttributes.changedFrequent );
+ if (coloringAttributes.changedFrequent != 0) {
+ definingColoringAttributes = coloringAttributes;
+ }
+ else {
+ // If the one replaced is a cloned copy, then ..
+ if (cloned) {
+ definingColoringAttributes.set(coloringAttributes);
+ }
+ else {
+ definingColoringAttributes = (ColoringAttributesRetained)coloringAttributes.clone();
+ }
+ }
+ red = definingColoringAttributes.color.x;
+ green = definingColoringAttributes.color.y;
+ blue = definingColoringAttributes.color.z;
+ }
+ }
+ else {
+ red = definingColoringAttributes.color.x;
+ green = definingColoringAttributes.color.y;
+ blue = definingColoringAttributes.color.z;
+ }
+ }
+ // System.out.println("rm = "+this+"red = "+red+" green = "+green+" blue = "+blue);
+ boolean newVal = isOpaque() || inOrderedGroup;
+ return (isOpaqueOrInOG != newVal);
+
+ }
+
+ void evalMaterialCachedState() {
+ if (definingMaterial == null) {
+ enableLighting = false;;
+ definingMaterial = null;
+ dRed = 1.0f;
+ dGreen = 1.0f;
+ dBlue = 1.0f;
+ }
+ else {
+ if ((geometryType & RASTER) != 0) {
+ enableLighting = false;
+ dRed = 1.0f;
+ dGreen = 1.0f;
+ dBlue = 1.0f;
+ } else {
+ if (normalPresent)
+ enableLighting = definingMaterial.lightingEnable;
+ else
+ enableLighting = false;
+ dRed = definingMaterial.diffuseColor.x;
+ dGreen = definingMaterial.diffuseColor.y;
+ dBlue = definingMaterial.diffuseColor.z;
+ }
+ }
+ }
+
+
+ void markBitsAsDirty(int leftBits, int rightBits) {
+ if (prev != null) {
+ checkEquivalenceWithLeftNeighbor(prev, leftBits);
+ prev.soleUserCompDirty &= ~ALL_DIRTY_BITS;
+ }
+ else if (prevMap != null) {
+ checkEquivalenceWithLeftNeighbor(prevMap, leftBits);
+ prevMap.soleUserCompDirty &= ~ALL_DIRTY_BITS;
+ }
+ if (next != null) {
+ if ((next.soleUserCompDirty & ALL_DIRTY_BITS) == 0) {
+ next.checkEquivalenceWithLeftNeighbor(this, rightBits);
+ } else {
+ next.soleUserCompDirty = rightBits;
+ }
+ }
+ else if (nextMap != null) {
+ if ((nextMap.soleUserCompDirty & ALL_DIRTY_BITS) == 0) {
+ nextMap.checkEquivalenceWithLeftNeighbor(this, rightBits);
+ } else {
+ nextMap.soleUserCompDirty = rightBits;
+ }
+ }
+
+ }
+
+ void handleMaterialEquivalence() {
+ // Check if it has equivalent material to any of the "non-dirty"
+ // renderMolecules before this one
+ RenderMolecule curPrevRm = null;
+ RenderMolecule curNextRm = null;
+ boolean found = false;
+ int leftBits = ALL_DIRTY_BITS;
+ int rightBits = ALL_DIRTY_BITS;
+ if (prev != null) {
+ curPrevRm = prev.prev;
+ if (materialEquivalent(prev, reloadColor(prev))) {
+ found = true;
+ leftBits = (((soleUserCompDirty | prev.soleUserCompDirty) &ALL_DIRTY_BITS) & ~MATERIAL_DIRTY);
+ rightBits = (soleUserCompDirty & ALL_DIRTY_BITS);
+ markBitsAsDirty(leftBits, rightBits);
+ }
+ }
+ else if (!found && next != null) {
+ curNextRm = next.next;
+
+ if (materialEquivalent(next, reloadColor(next))) {
+ found = true;
+ int bits = 0;
+ if (prev != null)
+ bits = prev.soleUserCompDirty;
+ else if (prevMap != null)
+ bits = prevMap.soleUserCompDirty;
+
+ leftBits = ((soleUserCompDirty |bits) &ALL_DIRTY_BITS);
+ rightBits = ((soleUserCompDirty & ALL_DIRTY_BITS) & ~MATERIAL_DIRTY);
+ markBitsAsDirty(leftBits, rightBits);
+
+ }
+ }
+ // try place it next to a equivalent material on the left
+ while (!found && curPrevRm != null) {
+ if (materialEquivalent(curPrevRm, reloadColor(curPrevRm))) {
+ found = true;
+ // Remove the renderMolecule from it place
+ prev.next = next;
+ prev.nextMap = nextMap;
+ if (next != null) {
+ next.prev = prev;
+ if ((next.soleUserCompDirty & ALL_DIRTY_BITS) == 0) {
+ next.checkEquivalenceWithLeftNeighbor(prev, ALL_DIRTY_BITS);
+ }
+ else {
+ next.soleUserCompDirty = ALL_DIRTY_BITS;
+ }
+ }
+ else if (nextMap != null) {
+ nextMap.prevMap = prev;
+ if ((nextMap.soleUserCompDirty & ALL_DIRTY_BITS) == 0) {
+ nextMap.checkEquivalenceWithLeftNeighbor(prev,ALL_DIRTY_BITS);
+ }
+ else {
+ nextMap.soleUserCompDirty |= ALL_DIRTY_BITS;
+ }
+ }
+
+ // Insert it after the equivalent RM
+ next = curPrevRm.next;
+ nextMap = curPrevRm.nextMap;
+ curPrevRm.nextMap = null;
+ if (next != null) {
+ next.prev = this;
+ }
+ else if (nextMap != null) {
+ nextMap.prevMap = this;
+ }
+ prev = curPrevRm;
+ curPrevRm.next = this;
+ leftBits = (ALL_DIRTY_BITS & ~MATERIAL_DIRTY);
+ markBitsAsDirty(leftBits, ALL_DIRTY_BITS);
+ }
+ curPrevRm = curPrevRm.prev;
+ }
+
+ // Check if it has equivalent material to any of the renderMolecules after
+ // this one
+ while (!found && curNextRm != null) {
+ if (materialEquivalent(curNextRm, reloadColor(curNextRm))) {
+ found = true;
+ // switch the pointers
+ next.prev = prev;
+ next.prevMap = prevMap;
+ if (prev != null) {
+ prev.next = next;
+ if ((next.soleUserCompDirty & ALL_DIRTY_BITS) == 0) {
+ next.checkEquivalenceWithLeftNeighbor(prev, ALL_DIRTY_BITS);
+ }
+ else {
+ next.soleUserCompDirty = ALL_DIRTY_BITS;
+ }
+ }
+ else if (prevMap != null) {
+ prevMap.nextMap = next;
+ if ((next.soleUserCompDirty & ALL_DIRTY_BITS) == 0) {
+ next.checkEquivalenceWithLeftNeighbor(prevMap, ALL_DIRTY_BITS);
+ }
+ else {
+ next.soleUserCompDirty = ALL_DIRTY_BITS;
+ }
+ }
+
+ // Insert it before the equivalent RM
+ prev = curNextRm.prev;
+ prevMap = curNextRm.prevMap;
+ curNextRm.prevMap = null;
+ if (curNextRm.prev != null) {
+ curNextRm.prev.next = this;
+ }
+ else if (prevMap != null) {
+ prevMap.nextMap = this;
+ }
+ next = curNextRm;
+ curNextRm.prev = this;
+ rightBits = (ALL_DIRTY_BITS & ~MATERIAL_DIRTY);
+ markBitsAsDirty(ALL_DIRTY_BITS, rightBits);
+ }
+ curNextRm = curNextRm.next;
+ }
+ // If there are no equivalent ones, evaluate the dirty bits in the current place
+ if (!found) {
+ if (prev != null) {
+ leftBits = ((soleUserCompDirty|prev.soleUserCompDirty) & ALL_DIRTY_BITS);
+ }
+ else if (prevMap != null) {
+ leftBits = ((soleUserCompDirty|prevMap.soleUserCompDirty) & ALL_DIRTY_BITS);
+ }
+ if (next != null) {
+ rightBits = ((soleUserCompDirty|next.soleUserCompDirty) & ALL_DIRTY_BITS);
+ }
+ else if (nextMap != null) {
+ rightBits = ((soleUserCompDirty|nextMap.soleUserCompDirty) & ALL_DIRTY_BITS);
+ }
+ markBitsAsDirty(leftBits, rightBits);
+ }
+
+ }
+
+ void reEvaluateEquivalence () {
+ // If Material changed, reInsert next to a equivalent material under
+ // the same transform group
+ // to prevent unnecessary material download
+ // This RM may have been evaluated due to an other RM is the same list
+ // If not, ...
+ if ((soleUserCompDirty & ALL_DIRTY_BITS) != 0) {
+ if ((soleUserCompDirty & MATERIAL_DIRTY) != 0) {
+ handleMaterialEquivalence();
+ }
+ else {
+ int dirtyBits = (soleUserCompDirty & ALL_DIRTY_BITS);
+ if (prev != null) {
+ checkEquivalenceWithLeftNeighbor(prev, ((dirtyBits|prev.soleUserCompDirty) & ALL_DIRTY_BITS));
+ prev.soleUserCompDirty = 0;
+ } else if (prevMap != null) {
+ checkEquivalenceWithLeftNeighbor(prevMap, ((dirtyBits|prevMap.soleUserCompDirty) & ALL_DIRTY_BITS));
+ prevMap.soleUserCompDirty = 0;
+ }
+ if (next != null) {
+ next.checkEquivalenceWithLeftNeighbor(this,((next.soleUserCompDirty|soleUserCompDirty) & ALL_DIRTY_BITS));
+ } else if (nextMap != null) {
+ nextMap.checkEquivalenceWithLeftNeighbor(this,((nextMap.soleUserCompDirty | soleUserCompDirty) & ALL_DIRTY_BITS));
+ }
+ }
+ }
+ soleUserCompDirty &= ~ALL_DIRTY_BITS;
+ }
+
+
+ boolean materialEquivalent(RenderMolecule rm, boolean reloadColor) {
+ if (!reloadColor) {
+ if (((this.material == rm.material) ||
+ ((rm.definingMaterial != null) &&
+ (rm.definingMaterial.equivalent(definingMaterial)))) &&
+ rm.alpha == alpha &&
+ enableLighting == rm.enableLighting &&
+ (enableLighting ||
+ (!enableLighting &&
+ rm.red ==red &&
+ rm.green == green &&
+ rm.blue == blue))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean coloringEquivalent(RenderMolecule rm, boolean reload_color) {
+ if (!reload_color) {
+ if (((rm.coloringAttributes == coloringAttributes) ||
+ ((rm.definingColoringAttributes != null) &&
+ (rm.definingColoringAttributes.equivalent(definingColoringAttributes)))) &&
+ (!enableLighting || (enableLighting && (dRed == rm.dRed && dBlue == rm.dBlue && dGreen == rm.dGreen)))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean transparencyEquivalent(RenderMolecule rm) {
+ if (((rm.transparency == transparency) ||
+ ((rm.definingTransparency != null) &&
+ (rm.definingTransparency.equivalent(definingTransparency))) &&
+ (rm.definingTransparency.transparencyMode < TransparencyAttributes.SCREEN_DOOR &&
+ blendOn() == rm.blendOn()))) {
+ return true;
+ }
+ return false;
+ }
+
+ boolean blendOn() {
+ if (lineAA && ((((geometryType & LINE) != 0) ||
+ polygonMode == PolygonAttributes.POLYGON_LINE))) {
+ return true;
+ }
+ if (pointAA && ((((geometryType & POINT) != 0) ||
+ polygonMode == PolygonAttributes.POLYGON_POINT))) {
+ return true;
+ }
+ return false;
+ }
+
+ VirtualUniverse getVirtualUniverse() {
+ return null;
+ }
+
+
+ void handleLocaleChange() {
+ if (locale == renderBin.locale) {
+ if (localToVworld != localeLocalToVworld) {
+ if (localeTranslation != null) {
+ // return to the freelist;
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ localeLocalToVworld[0]);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ localeLocalToVworld[1]);
+ }
+ localeLocalToVworld = localToVworld;
+ localeTranslation = null;
+ }
+ }
+ else {
+ // Using the localToVworl then, go back to making a new copy
+ if (localeTranslation == null) {
+ localeLocalToVworld = new Transform3D[2];
+ /*
+ localeLocalToVworld[0] = VirtualUniverse.mc.getTransform3D(null);
+ localeLocalToVworld[1] = VirtualUniverse.mc.getTransform3D(null);
+ */
+
+ localeLocalToVworld[0] = new Transform3D();
+ localeLocalToVworld[1] = new Transform3D();
+
+ localeTranslation = new Vector3d();
+ locale.hiRes.difference(renderBin.locale.hiRes, localeTranslation);
+ translate();
+ int i = localToVworldIndex[NodeRetained.CURRENT_LOCAL_TO_VWORLD];
+
+ localeLocalToVworld[i].mat[0] = localToVworld[i].mat[0];
+ localeLocalToVworld[i].mat[1] = localToVworld[i].mat[1];
+ localeLocalToVworld[i].mat[2] = localToVworld[i].mat[2];
+ localeLocalToVworld[i].mat[3] = localToVworld[i].mat[3] + localeTranslation.x ;
+ localeLocalToVworld[i].mat[4] = localToVworld[i].mat[4];
+ localeLocalToVworld[i].mat[5] = localToVworld[i].mat[5];
+ localeLocalToVworld[i].mat[6] = localToVworld[i].mat[6];
+ localeLocalToVworld[i].mat[7] = localToVworld[i].mat[7]+ localeTranslation.y;
+ localeLocalToVworld[i].mat[8] = localToVworld[i].mat[8];
+ localeLocalToVworld[i].mat[9] = localToVworld[i].mat[9];
+ localeLocalToVworld[i].mat[10] = localToVworld[i].mat[10];
+ localeLocalToVworld[i].mat[11] = localToVworld[i].mat[11]+ localeTranslation.z;
+ localeLocalToVworld[i].mat[12] = localToVworld[i].mat[12];
+ localeLocalToVworld[i].mat[13] = localToVworld[i].mat[13];
+ localeLocalToVworld[i].mat[14] = localToVworld[i].mat[14];
+ localeLocalToVworld[i].mat[15] = localToVworld[i].mat[15];
+ }
+ }
+
+ trans = localeLocalToVworld;
+ }
+
+
+ /**
+ * updateNodeComponentCheck is called for each soleUser RenderMolecule
+ * into which new renderAtom has been added. This method is called before
+ * updateNodeComponent() to allow RenderMolecule to catch any node
+ * component changes that have been missed because the changes
+ * come when there is no active renderAtom associated with the
+ * TextureBin. See bug# 4503926 for details.
+ */
+ public void updateNodeComponentCheck() {
+
+ // If the renderMolecule has been removed, do nothing ..
+ if ((onUpdateList &ON_UPDATE_CHECK_LIST ) == 0)
+ return;
+
+ onUpdateList &= ~ON_UPDATE_CHECK_LIST;
+ NodeComponentRetained nc = (NodeComponentRetained)appHandle;
+ if ((nc.compChanged & RM_COMPONENTS) != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= (nc.compChanged & RM_COMPONENTS);
+ }
+ if (definingPolygonAttributes != null &&
+ definingPolygonAttributes == polygonAttributes) {
+ if (definingPolygonAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= POLYGONATTRS_DIRTY;
+ }
+ }
+ if (definingLineAttributes != null &&
+ definingLineAttributes == lineAttributes) {
+ if (definingLineAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= LINEATTRS_DIRTY;
+ }
+ }
+ if (definingPointAttributes != null &&
+ definingPointAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= POINTATTRS_DIRTY;
+ }
+
+ if (definingMaterial != null &&
+ definingMaterial == material) {
+ if (definingMaterial.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= MATERIAL_DIRTY;
+ }
+ }
+
+ if (definingColoringAttributes != null &&
+ definingColoringAttributes == coloringAttributes) {
+ if (definingColoringAttributes.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= COLORINGATTRS_DIRTY;
+ }
+ }
+
+ if (definingTransparency != null &&
+ definingTransparency == transparency) {
+ if (definingTransparency.compChanged != 0) {
+ if ((soleUserCompDirty& ALL_DIRTY_BITS) == 0 ) {
+ renderBin.rmUpdateList.add(this);
+ }
+ soleUserCompDirty |= TRANSPARENCY_DIRTY;
+ }
+ }
+ }
+}
+
+
+
+
diff --git a/src/classes/share/javax/media/j3d/Renderer.java b/src/classes/share/javax/media/j3d/Renderer.java
new file mode 100644
index 0000000..eabfa6e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Renderer.java
@@ -0,0 +1,1688 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * Portions of this code were derived from work done by the Blackdown
+ * group (www.blackdown.org), who did the initial Linux implementation
+ * of the Java 3D API.
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.util.*;
+
+
+class Renderer extends J3dThread {
+ int objectId = -1;
+ // This action causes this thread to wait
+ static final int WAIT = 0;
+
+ // This action causes this thread to notify the view, and then wait.
+ static final int NOTIFY_AND_WAIT = 1;
+
+ // This action causes this thread to be notified
+ static final int NOTIFY = 2;
+
+ // The following are DecalGroup rendering states
+ static final int DECAL_NONE = 0;
+ static final int DECAL_1ST_CHILD = 1;
+ static final int DECAL_NTH_CHILD = 2;
+
+ // stuff for scene antialiasing
+ static final int NUM_ACCUMULATION_SAMPLES = 8;
+
+ static final float ACCUM_SAMPLES_X[] =
+ { -0.54818f, 0.56438f, 0.39462f, -0.54498f,
+ -0.83790f, -0.39263f, 0.32254f, 0.84216f};
+
+ static final float ACCUM_SAMPLES_Y[] =
+ { 0.55331f, -0.53495f, 0.41540f, -0.52829f,
+ 0.82102f, -0.27383f, 0.09133f, -0.84399f};
+
+ static final float accumValue = 1.0f / NUM_ACCUMULATION_SAMPLES;
+
+ // The following are Render arguments
+ static final int RENDER = 0;
+ static final int SWAP = 1;
+ static final int REQUESTRENDER = 2;
+ static final int REQUESTCLEANUP = 3;
+
+ // Renderer Structure used for the messaging to the renderer
+ RendererStructure rendererStructure = new RendererStructure();
+
+
+ // vworldtoVpc matrix for background geometry
+ Transform3D bgVworldToVpc = new Transform3D();
+
+ long lasttime;
+ long currtime;
+ float numframes = 0.0f;
+ static final boolean doTiming = false;
+
+ private static int numInstances = 0;
+ private int instanceNum = -1;
+
+ // Local copy of sharedStereZBuffer flag
+ boolean sharedStereoZBuffer;
+
+ // This is the id for the underlying sharable graphics context
+ long sharedCtx = 0;
+
+ // since the sharedCtx id can be the same as the previous one,
+ // we need to keep a time stamp to differentiate the contexts with the
+ // same id
+ long sharedCtxTimeStamp = 0;
+
+ // display id - to free shared context
+ long display;
+ int window;
+
+ /**
+ * This is the id of the current rendering context
+ */
+ long currentCtx = -1;
+
+ // an unique bit to identify this renderer
+ int rendererBit = 0;
+
+ // List of renderMolecules that are dirty due to additions
+ // or removal of renderAtoms from their display list set
+ // of renderAtoms
+ ArrayList dirtyRenderMoleculeList = new ArrayList();
+
+ // List of individual dlists that need to be rebuilt
+ ArrayList dirtyRenderAtomList = new ArrayList();
+
+ // List of (Rm, rInfo) pair of individual dlists that need to be rebuilt
+ ArrayList dirtyDlistPerRinfoList = new ArrayList();
+
+
+ // Texture and display list that should be freed
+ ArrayList textureIdResourceFreeList = new ArrayList();
+ ArrayList displayListResourceFreeList = new ArrayList();
+
+ // Texture that should be reload
+ ArrayList textureReloadList = new ArrayList();
+
+
+ // This is a local copy of canvas view cache. It is used as a data storage for the
+ // renderer. Note: This isn't the "real" canvasViewCache references by the Canvas.
+ CanvasViewCache copyOfCvCache = new CanvasViewCache(null, null, null);
+
+ J3dMessage[] renderMessage;
+
+ // The screen for this Renderer. Note that this renderer may share
+ // by both on screen and off screen. When view unregister, we need
+ // to set both reference to null.
+ Screen3D onScreen;
+ Screen3D offScreen;
+
+ // full screen anti-aliasing projection matrices
+ double accumLeftProjMat[] = new double[16];
+ double accumRightProjMat[] = new double[16];
+ double accumInfLeftProjMat[] = new double[16];
+ double accumInfRightProjMat[] = new double[16];
+
+ // rendering messages
+ J3dMessage m[];
+ int nmesg = 0;
+
+ // List of contexts created
+ ArrayList listOfCtxs = new ArrayList();
+
+ // Parallel list of canvases
+ ArrayList listOfCanvases = new ArrayList();
+
+
+ boolean needToRebuildDisplayList = false;
+ boolean needToResendTextureDown = false;
+
+ // True when either one of dirtyRenderMoleculeList,
+ // dirtyDlistPerRinfoList, dirtyRenderAtomList size > 0
+ boolean dirtyDisplayList = false;
+
+ // Remember OGL context resources to free
+ // before context is destroy.
+ // It is used when sharedCtx = true;
+ ArrayList textureIDResourceTable = new ArrayList(5);
+
+ native void D3DCleanUp();
+
+ private synchronized int newInstanceNum() {
+ return (++numInstances);
+ }
+
+ int getInstanceNum() {
+ if (instanceNum == -1)
+ instanceNum = newInstanceNum();
+ return instanceNum;
+ }
+
+ /**
+ * Constructs a new Renderer
+ */
+ Renderer(ThreadGroup t) {
+ super(t);
+ setName("J3D-Renderer-" + getInstanceNum());
+
+ type = J3dThread.RENDER_THREAD;
+ rendererBit = VirtualUniverse.mc.getRendererBit();
+ renderMessage = new J3dMessage[1];
+ }
+
+
+ /**
+ * The main loop for the renderer.
+ */
+ void doWork(long referenceTime) {
+ RenderAtom ra;
+ RenderBin renderBin = null;
+ Canvas3D cv, canvas=null;
+ Object firstArg;
+ View view = null;
+ Color3f col;
+ int stereo_mode;
+ int num_stereo_passes, num_render_passes, num_accum_passes = 1;
+ int pass, apass, i, j, k;
+ boolean doAccum = false;
+ double accumDx = 0.0f, accumDy = 0.0f;
+ double accumDxFactor = 1.0f, accumDyFactor = 1.0f;
+
+ double accumLeftX = 0.0, accumLeftY = 0.0,
+ accumRightX = 0.0, accumRightY = 0.0,
+ accumInfLeftX = 0.0, accumInfLeftY = 0.0,
+ accumInfRightX = 0.0, accumInfRightY = 0.0;
+ int opArg, status;
+ boolean done = false;
+ Transform3D t3d = null;
+
+ opArg = ((Integer)args[0]).intValue();
+
+ try {
+ if (opArg == SWAP) {
+
+ Object [] swapArray = (Object[])args[2];
+
+ view = (View)args[3];
+
+ for (i=0; i<swapArray.length; i++) {
+ cv = (Canvas3D) swapArray[i];
+ if (!cv.isRunning) {
+ continue;
+ }
+
+ doneSwap: try {
+
+ if (!cv.validCanvas) {
+ continue;
+ }
+
+ if (cv.active && (cv.ctx != 0) &&
+ (cv.view != null) && (cv.imageReady)) {
+ if (cv.useDoubleBuffer) {
+ synchronized (cv.drawingSurfaceObject) {
+ if (cv.validCtx) {
+ if (VirtualUniverse.mc.doDsiRenderLock) {
+ // Set doDsiLock flag for rendering based on system
+ // property, If we force DSI lock for swap
+ // buffer, we lose most of the parallelism that having
+ // multiple renderers gives us.
+
+ if (!cv.drawingSurfaceObject.renderLock()) {
+ break doneSwap;
+ }
+ cv.makeCtxCurrent();
+ cv.syncRender(cv.ctx, true);
+ status = cv.swapBuffers(cv.ctx,
+ cv.screen.display,
+ cv.window);
+ if (status != Canvas3D.NOCHANGE) {
+ cv.resetRendering(status);
+ }
+ cv.drawingSurfaceObject.unLock();
+ } else {
+ cv.makeCtxCurrent();
+
+ cv.syncRender(cv.ctx, true);
+ status = cv.swapBuffers(cv.ctx,
+ cv.screen.display,
+ cv.window);
+ if (status != Canvas3D.NOCHANGE) {
+ cv.resetRendering(status);
+ }
+
+ }
+ }
+ }
+ }
+ cv.view.inCanvasCallback = true;
+ try {
+ cv.postSwap();
+ } catch (RuntimeException e) {
+ System.err.println("Exception occurred during Canvas3D callback:");
+ e.printStackTrace();
+ }
+ // reset flag
+ cv.imageReady = false;
+ cv.view.inCanvasCallback = false;
+ // Clear canvasDirty bit ONLY when postSwap() success
+
+ // Set all dirty bits except environment set and lightbin
+ // they are only set dirty if the last used light bin or
+ // environment set values for this canvas change between
+ // one frame and other
+
+ if (!cv.ctxChanged) {
+ cv.canvasDirty = (0xffff & ~(Canvas3D.LIGHTBIN_DIRTY |
+ Canvas3D.LIGHTENABLES_DIRTY |
+ Canvas3D.AMBIENTLIGHT_DIRTY |
+ Canvas3D.MODELCLIP_DIRTY |
+ Canvas3D.VWORLD_SCALE_DIRTY |
+ Canvas3D.FOG_DIRTY));
+ // Force reload of transform next frame
+ cv.modelMatrix = null;
+
+ // Force the cached renderAtom to null
+ cv.ra = null;
+ } else {
+ cv.ctxChanged = false;
+ }
+ }
+ } catch (NullPointerException ne) {
+ //ne.printStackTrace();
+ if (VirtualUniverse.mc.doDsiRenderLock) {
+ cv.drawingSurfaceObject.unLock();
+ }
+ }
+ }
+
+ if (view != null) { // STOP_TIMER
+ // incElapsedFrames() is delay until MC:updateMirroObject
+ if (view.viewCache.getDoHeadTracking()) {
+ VirtualUniverse.mc.sendRunMessage(view,
+ J3dThread.RENDER_THREAD);
+ }
+ }
+
+ } else if (opArg == REQUESTCLEANUP) {
+ Integer mtype = (Integer) args[2];
+
+ if (mtype == MasterControl.REMOVEALLCTXS_CLEANUP) {
+ // from MasterControl when View is last views
+ removeAllCtxs();
+ } else if (mtype == MasterControl.FREECONTEXT_CLEANUP) {
+ // from MasterControl freeContext(View v)
+ cv = (Canvas3D) args[1];
+ removeCtx(cv, cv.screen.display, cv.window, cv.ctx,
+ true, true);
+ } else if (mtype == MasterControl.RESETCANVAS_CLEANUP) {
+ // from MasterControl RESET_CANVAS postRequest
+ cv = (Canvas3D) args[1];
+ if (cv.ctx != 0) {
+ cv.makeCtxCurrent();
+ }
+ cv.freeContextResources(cv.screen.renderer, true, cv.ctx);
+ } else if (mtype == MasterControl.REMOVECTX_CLEANUP) {
+ // from Canvas3D removeCtx() postRequest
+ Object[] obj = (Object []) args[1];
+ Canvas3D c = (Canvas3D) obj[0];
+ removeCtx(c,
+ ((Long) obj[1]).longValue(),
+ ((Integer) obj[2]).intValue(),
+ ((Long) obj[3]).longValue(),
+ false, !c.offScreen);
+ }
+ return;
+ } else { // RENDER || REQUESTRENDER
+ int renderType;
+ nmesg = 0;
+ int totalMessages = 0;
+ if (opArg == RENDER) {
+ m = renderMessage;
+ m[0] = VirtualUniverse.mc.getMessage();
+ m[0].type = J3dMessage.RENDER_RETAINED;
+ m[0].incRefcount();
+ m[0].args[0] = args[1];
+ totalMessages = 1;
+ } else { // REQUESTRENDER
+ m = rendererStructure.getMessages();
+ totalMessages = rendererStructure.getNumMessage();
+ if (totalMessages <= 0) {
+ return;
+ }
+ }
+
+
+ doneRender: while (nmesg < totalMessages) {
+
+ firstArg = m[nmesg].args[0];
+
+ if (firstArg == null) {
+ Object secondArg = m[nmesg].args[1];
+ if (secondArg instanceof Canvas3D) {
+ // message from Canvas3Ds to destroy Context
+ Integer reqType = (Integer) m[nmesg].args[2];
+ Canvas3D c = (Canvas3D) secondArg;
+ if (reqType == MasterControl.SET_GRAPHICSCONFIG_FEATURES) {
+ GraphicsConfiguration gc = c.graphicsConfiguration;
+ NativeConfigTemplate3D nct =
+ GraphicsConfigTemplate3D.nativeTemplate;
+ if (c.offScreen) {
+ // offScreen canvas neither supports
+ // double buffering nor stereo
+ c.doubleBufferAvailable = false;
+ c.stereoAvailable = false;
+ } else {
+ c.doubleBufferAvailable = nct.hasDoubleBuffer(gc);
+ c.stereoAvailable = nct.hasStereo(gc);
+ }
+ c.sceneAntialiasingMultiSamplesAvailable =
+ nct.hasSceneAntialiasingMultiSamples(gc);
+ if (c.sceneAntialiasingMultiSamplesAvailable) {
+ c.sceneAntialiasingAvailable = true;
+ } else {
+ c.sceneAntialiasingAvailable =
+ nct.hasSceneAntialiasingAccum(gc);
+ }
+ GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY);
+ } else if (reqType == MasterControl.SET_QUERYPROPERTIES){
+ c.createQueryContext();
+ // currentCtx change after we create a new context
+ GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY);
+ currentCtx = -1;
+ }
+ } else if (secondArg instanceof Integer) {
+ // message from TextureRetained finalize() method
+ // to free texture id
+ freeTextureID(((Integer) secondArg).intValue(), (String)m[nmesg].args[2]);
+ } else if (secondArg instanceof GeometryArrayRetained) {
+ // message from GeometryArrayRetained
+ // clearLive() to free D3D array
+ ((GeometryArrayRetained) secondArg).freeD3DArray(false);
+ } else if (secondArg instanceof GraphicsConfigTemplate3D) {
+ GraphicsConfigTemplate3D gct =
+ (GraphicsConfigTemplate3D) secondArg;
+ Integer reqType = (Integer) m[nmesg].args[2];
+ if (reqType == MasterControl.GETBESTCONFIG) {
+ gct.testCfg =
+ gct.nativeTemplate.getBestConfiguration(gct,
+ (GraphicsConfiguration []) gct.testCfg);
+ } else if (reqType == MasterControl.ISCONFIGSUPPORT) {
+ if (gct.nativeTemplate.isGraphicsConfigSupported(gct,
+ (GraphicsConfiguration) gct.testCfg)) {
+ gct.testCfg = Boolean.TRUE;
+ } else {
+ gct.testCfg = Boolean.FALSE;
+ }
+ }
+ gct.runMonitor(J3dThread.NOTIFY);
+ }
+
+ m[nmesg++].decRefcount();
+ continue;
+ }
+
+ canvas = (Canvas3D) firstArg;
+
+ renderType = m[nmesg].type;
+
+ if ((canvas.view == null) || !canvas.firstPaintCalled) {
+ // This happen when the canvas just remove from the View
+ if (renderType == J3dMessage.RENDER_OFFSCREEN) {
+ canvas.offScreenRendering = false;
+ }
+ m[nmesg++].decRefcount();
+ continue;
+ }
+
+ if (!canvas.validCanvas &&
+ (renderType != J3dMessage.RENDER_OFFSCREEN)) {
+ m[nmesg++].decRefcount();
+ continue;
+ }
+
+ if (renderType == J3dMessage.RESIZE_CANVAS) {
+ canvas.d3dResize();
+ // render the image again after resize
+ VirtualUniverse.mc.sendRunMessage(canvas.view, J3dThread.RENDER_THREAD);
+ m[nmesg++].decRefcount();
+ } else if (renderType == J3dMessage.TOGGLE_CANVAS) {
+ canvas.d3dToggle();
+ VirtualUniverse.mc.sendRunMessage(canvas.view, J3dThread.RENDER_THREAD);
+ m[nmesg++].decRefcount();
+ } else if (renderType == J3dMessage.RENDER_IMMEDIATE) {
+ int command = ((Integer)m[nmesg].args[1]).intValue();
+ //System.out.println("command= " + command);
+ if (needToResendTextureDown) {
+ VirtualUniverse.mc.resendTexTimestamp++;
+ needToResendTextureDown = false;
+ }
+
+ if (canvas.ctx != 0) {
+ // ctx may not construct until doClear();
+ canvas.beginScene();
+ }
+
+ switch (command) {
+ case GraphicsContext3D.CLEAR:
+ canvas.graphicsContext3D.doClear();
+ break;
+ case GraphicsContext3D.DRAW:
+ canvas.graphicsContext3D.doDraw(
+ (Geometry)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SWAP:
+ canvas.doSwap();
+ break;
+ case GraphicsContext3D.READ_RASTER:
+ canvas.graphicsContext3D.doReadRaster(
+ (Raster)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_APPEARANCE:
+ canvas.graphicsContext3D.doSetAppearance(
+ (Appearance)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_BACKGROUND:
+ canvas.graphicsContext3D.doSetBackground(
+ (Background)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_FOG:
+ canvas.graphicsContext3D.doSetFog(
+ (Fog)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_LIGHT:
+ canvas.graphicsContext3D.doSetLight(
+ (Light)m[nmesg].args[2],
+ ((Integer)m[nmesg].args[3]).intValue());
+ break;
+ case GraphicsContext3D.INSERT_LIGHT:
+ canvas.graphicsContext3D.doInsertLight(
+ (Light)m[nmesg].args[2],
+ ((Integer)m[nmesg].args[3]).intValue());
+ break;
+ case GraphicsContext3D.REMOVE_LIGHT:
+ canvas.graphicsContext3D.doRemoveLight(
+ ((Integer)m[nmesg].args[2]).intValue());
+ break;
+ case GraphicsContext3D.ADD_LIGHT:
+ canvas.graphicsContext3D.doAddLight(
+ (Light)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_HI_RES:
+ canvas.graphicsContext3D.doSetHiRes(
+ (HiResCoord)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_MODEL_TRANSFORM:
+ t3d = (Transform3D)m[nmesg].args[2];
+ canvas.graphicsContext3D.doSetModelTransform(t3d);
+ // return t3d to freelist. t3d was gotten from GraphicsContext3D
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ t3d);
+ break;
+ case GraphicsContext3D.MULTIPLY_MODEL_TRANSFORM:
+ t3d = (Transform3D)m[nmesg].args[2];
+ canvas.graphicsContext3D.doMultiplyModelTransform(t3d);
+ // return t3d to freelist. t3d was gotten from GraphicsContext3D
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ t3d);
+ break;
+ case GraphicsContext3D.SET_SOUND:
+ canvas.graphicsContext3D.doSetSound(
+ (Sound)m[nmesg].args[2],
+ ((Integer)m[nmesg].args[3]).intValue());
+ break;
+ case GraphicsContext3D.INSERT_SOUND:
+ canvas.graphicsContext3D.doInsertSound(
+ (Sound)m[nmesg].args[2],
+ ((Integer)m[nmesg].args[3]).intValue());
+ break;
+ case GraphicsContext3D.REMOVE_SOUND:
+ canvas.graphicsContext3D.doRemoveSound(
+ ((Integer)m[nmesg].args[2]).intValue());
+ break;
+ case GraphicsContext3D.ADD_SOUND:
+ canvas.graphicsContext3D.doAddSound(
+ (Sound)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_AURAL_ATTRIBUTES:
+ canvas.graphicsContext3D.doSetAuralAttributes(
+ (AuralAttributes)m[nmesg].args[2]);
+ break;
+ case GraphicsContext3D.SET_BUFFER_OVERRIDE:
+ canvas.graphicsContext3D.doSetBufferOverride(
+ ((Boolean)m[nmesg].args[2]).booleanValue());
+ break;
+ case GraphicsContext3D.SET_FRONT_BUFFER_RENDERING:
+ canvas.graphicsContext3D.doSetFrontBufferRendering(
+ ((Boolean)m[nmesg].args[2]).booleanValue());
+ break;
+ case GraphicsContext3D.SET_STEREO_MODE:
+ canvas.graphicsContext3D.doSetStereoMode(
+ ((Integer)m[nmesg].args[2]).intValue());
+ break;
+ case GraphicsContext3D.FLUSH:
+ canvas.graphicsContext3D.doFlush(
+ ((Boolean)m[nmesg].args[2]).booleanValue());
+ break;
+ case GraphicsContext3D.FLUSH2D:
+ canvas.graphics2D.doFlush();
+ break;
+ case GraphicsContext3D.DRAWANDFLUSH2D:
+ Object ar[] = m[nmesg].args;
+ canvas.graphics2D.doDrawAndFlushImage(
+ (BufferedImage) ar[2],
+ ((Point) ar[3]).x,
+ ((Point) ar[3]).y,
+ (ImageObserver) ar[4]);
+ break;
+ case GraphicsContext3D.SET_MODELCLIP:
+ canvas.graphicsContext3D.doSetModelClip(
+ (ModelClip)m[nmesg].args[2]);
+ break;
+ default:
+ break;
+ }
+
+ if (canvas.ctx != 0) {
+ canvas.endScene();
+ }
+ m[nmesg++].decRefcount();
+ } else { // retained mode rendering
+ m[nmesg++].decRefcount();
+
+ ImageComponent2DRetained offBufRetained = null;
+
+ if (renderType == J3dMessage.RENDER_OFFSCREEN) {
+ if (canvas.window == 0 || !canvas.active) {
+ canvas.offScreenRendering = false;
+ continue;
+ } else {
+ offBufRetained = (ImageComponent2DRetained)
+ canvas.offScreenBuffer.retained;
+
+ if (offBufRetained.isByReference()) {
+ offBufRetained.geomLock.getLock();
+ offBufRetained.evaluateExtensions(
+ canvas.extensionsSupported);
+ }
+ }
+ } else if (!canvas.active) {
+ continue;
+ }
+
+ boolean background_image_update = false;
+
+ renderBin = canvas.view.renderBin;
+
+ // setup rendering context
+
+ // We need to catch NullPointerException when the dsi
+ // gets yanked from us during a remove.
+
+ if (canvas.useSharedCtx) {
+ if (sharedCtx == 0) {
+ display = canvas.screen.display;
+
+ // Always lock for context create
+ if (!canvas.drawingSurfaceObject.renderLock()) {
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ sharedCtx =
+ canvas.createContext(canvas.screen.display,
+ canvas.window,
+ canvas.vid,
+ canvas.visInfo,
+ 0, true,
+ canvas.offScreen);
+ if (sharedCtx == 0) {
+ canvas.drawingSurfaceObject.unLock();
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+ sharedCtxTimeStamp =
+ VirtualUniverse.mc.getContextTimeStamp();
+
+ needToRebuildDisplayList = true;
+ }
+
+ canvas.drawingSurfaceObject.unLock();
+ }
+ }
+ if (canvas.ctx == 0) {
+
+ display = canvas.screen.display;
+
+ // Always lock for context create
+ if (!canvas.drawingSurfaceObject.renderLock()) {
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ canvas.ctx =
+ canvas.createContext(canvas.screen.display,
+ canvas.window, canvas.vid,
+ canvas.visInfo, sharedCtx,
+ false, canvas.offScreen);
+
+
+
+ if (canvas.ctx == 0) {
+ canvas.drawingSurfaceObject.unLock();
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+
+ if (canvas.graphics2D != null) {
+ canvas.graphics2D.init();
+ }
+
+ canvas.ctxTimeStamp =
+ VirtualUniverse.mc.getContextTimeStamp();
+ listOfCtxs.add(new Long(canvas.ctx));
+ listOfCanvases.add(canvas);
+
+ if (renderBin.nodeComponentList.size() > 0) {
+ for (i = 0; i < renderBin.nodeComponentList.size(); i++) {
+ NodeComponentRetained nc = (NodeComponentRetained)renderBin.nodeComponentList.get(i);
+ nc.evaluateExtensions(canvas.extensionsSupported);
+ }
+ }
+
+
+ // query for the number of texture units supported
+ if (canvas.multiTexAccelerated) {
+ canvas.numTexUnitSupported =
+ canvas.getTextureUnitCount(canvas.ctx);
+ if (VirtualUniverse.mc.textureUnitMax < canvas.numTexUnitSupported) {
+ canvas.numTexUnitSupported = VirtualUniverse.mc.textureUnitMax;
+ }
+ }
+
+ // enable separate specular color
+ canvas.enableSeparateSpecularColor();
+
+ }
+
+
+ // create the cache texture state in canvas
+ // for state download checking purpose
+
+ if (canvas.texUnitState == null) {
+ canvas.texUnitState =
+ new TextureUnitStateRetained[
+ canvas.numTexCoordSupported];
+ for (int t = 0; t < canvas.numTexCoordSupported;
+ t++) {
+ canvas.texUnitState[t] =
+ new TextureUnitStateRetained();
+ canvas.texUnitState[t].texture = null;
+ canvas.texUnitState[t].mirror = null;
+ }
+ }
+
+
+ // also create the texture unit state map
+ // which is a mapping from texture unit state to
+ // the actual underlying texture unit
+
+ if (canvas.texUnitStateMap == null) {
+ canvas.texUnitStateMap =
+ new int[canvas.numTexCoordSupported];
+ }
+
+ canvas.resetImmediateRendering(Canvas3D.NOCHANGE);
+ canvas.drawingSurfaceObject.contextValidated();
+
+ if (!canvas.useSharedCtx) {
+ canvas.needToRebuildDisplayList = true;
+ }
+ canvas.drawingSurfaceObject.unLock();
+ } else {
+ if (canvas.isRunning) {
+ canvas.makeCtxCurrent();
+ }
+ }
+
+
+ if (renderBin != null) {
+ if ((VirtualUniverse.mc.doDsiRenderLock) &&
+ (!canvas.drawingSurfaceObject.renderLock())) {
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+
+ if (needToResendTextureDown) {
+ VirtualUniverse.mc.resendTexTimestamp++;
+ needToResendTextureDown = false;
+ }
+ // handle free resource
+ if (canvas.useSharedCtx) {
+ freeResourcesInFreeList(canvas);
+ } else {
+ canvas.freeResourcesInFreeList(canvas.ctx);
+ }
+
+ // save the BACKGROUND_IMAGE_DIRTY before canvas.updateViewCache
+ // clean it
+ background_image_update =
+ ((canvas.cvDirtyMask & Canvas3D.BACKGROUND_IMAGE_DIRTY) != 0);
+
+ // copyOfcvCache is a copy of canvas view
+ // cache. It is used as a data storage for the
+ // renderer. Note: This isn't the "real"
+ // canvasViewCache references by the Canvas.
+ //
+ // Note : For performance reason, copyOfcvCache
+ // doesn't contain are valid canvasViewCache info.,
+ // only data needed by the renderer are stored.
+ //
+ // The valid data are : useStereo, canvasWidth,
+ // canvasHeight, leftProjection, rightProjection,
+ // leftVpcToEc, rightVpcToEc, leftFrustumPlanes,
+ // rightFrustumPlanes, vpcToVworld and vworldToVpc.
+
+ if (VirtualUniverse.mc.doDsiRenderLock) {
+ canvas.drawingSurfaceObject.unLock();
+ }
+
+ // Deadlock if we include updateViewCache in
+ // drawingSurfaceObject sync.
+ canvas.updateViewCache(false, copyOfCvCache, null,
+ renderBin.geometryBackground != null);
+
+ if ((VirtualUniverse.mc.doDsiRenderLock) &&
+ (!canvas.drawingSurfaceObject.renderLock())) {
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+
+ // setup viewport
+ canvas.setViewport(canvas.ctx, 0, 0,
+ copyOfCvCache.getCanvasWidth(),
+ copyOfCvCache.getCanvasHeight());
+
+
+
+ // rebuild the display list of all dirty renderMolecules.
+ if (canvas.useSharedCtx) {
+ if (needToRebuildDisplayList) {
+ renderBin.updateAllRenderMolecule(
+ this, canvas);
+ needToRebuildDisplayList = false;
+ }
+
+ if (dirtyDisplayList) {
+ renderBin.updateDirtyDisplayLists(canvas,
+ dirtyRenderMoleculeList,
+ dirtyDlistPerRinfoList,
+ dirtyRenderAtomList,true);
+ dirtyDisplayList = false;
+ }
+
+ // for shared context, download textures upfront
+ // to minimize the context switching overhead
+ int sz = textureReloadList.size();
+
+ if (sz > 0) {
+ for (j = sz-1; j>=0; j--) {
+ ((TextureRetained)textureReloadList.get(j)).
+ reloadTextureSharedContext(canvas);
+ }
+ textureReloadList.clear();
+ }
+
+ } else {
+ // update each canvas
+ if (canvas.needToRebuildDisplayList) {
+ renderBin.updateAllRenderMolecule(canvas);
+ canvas.needToRebuildDisplayList = false;
+ }
+ if (canvas.dirtyDisplayList) {
+ renderBin.updateDirtyDisplayLists(canvas,
+ canvas.dirtyRenderMoleculeList,
+ canvas.dirtyDlistPerRinfoList,
+ canvas.dirtyRenderAtomList, false);
+ canvas.dirtyDisplayList = false;
+ }
+ }
+
+ // lighting setup
+ if (canvas.view.localEyeLightingEnable !=
+ canvas.ctxEyeLightingEnable) {
+ canvas.ctxUpdateEyeLightingEnable(canvas.ctx,
+ canvas.view.localEyeLightingEnable);
+ canvas.ctxEyeLightingEnable =
+ canvas.view.localEyeLightingEnable;
+ }
+
+
+ // stereo setup
+ boolean useStereo = copyOfCvCache.getUseStereo();
+ if (useStereo) {
+ num_stereo_passes = 2;
+ stereo_mode = Canvas3D.FIELD_LEFT;
+
+ sharedStereoZBuffer =
+ VirtualUniverse.mc.sharedStereoZBuffer;
+ } else {
+ num_stereo_passes = 1;
+ stereo_mode = Canvas3D.FIELD_ALL;
+
+ // just in case user set flag -
+ // disable since we are not in stereo
+ sharedStereoZBuffer = false;
+ }
+
+ // full screen anti-aliasing setup
+ if (canvas.view.getSceneAntialiasingEnable() &&
+ canvas.sceneAntialiasingAvailable) {
+ if (!VirtualUniverse.mc.isD3D() &&
+ ((canvas.extensionsSupported & Canvas3D.ARB_MULTISAMPLE) == 0) ||
+ !canvas.sceneAntialiasingMultiSamplesAvailable) {
+ doAccum = true;
+ num_accum_passes = NUM_ACCUMULATION_SAMPLES;
+
+ System.arraycopy(
+ copyOfCvCache.getLeftProjection().mat,
+ 0, accumLeftProjMat, 0, 16);
+
+
+ accumDxFactor = (
+ canvas.canvasViewCache.getPhysicalWindowWidth() /
+ canvas.canvasViewCache.getCanvasWidth())*canvas.view.fieldOfView;
+
+ accumDyFactor = (
+ canvas.canvasViewCache.getPhysicalWindowHeight() /
+ canvas.canvasViewCache.getCanvasHeight())*canvas.view.fieldOfView;
+
+
+ accumLeftX = accumLeftProjMat[3];
+ accumLeftY = accumLeftProjMat[7];
+
+ if (useStereo) {
+ System.arraycopy(
+ copyOfCvCache.getRightProjection().mat,
+ 0, accumRightProjMat, 0, 16);
+ accumRightX = accumRightProjMat[3];
+ accumRightY = accumRightProjMat[7];
+ }
+
+ if (renderBin.geometryBackground != null) {
+ System.arraycopy(
+ copyOfCvCache.getInfLeftProjection().mat,
+ 0, accumInfLeftProjMat, 0, 16);
+ accumInfLeftX = accumInfLeftProjMat[3];
+ accumInfLeftY = accumInfLeftProjMat[7];
+ if (useStereo) {
+ System.arraycopy(
+ copyOfCvCache.getInfRightProjection().mat,
+ 0, accumInfRightProjMat, 0, 16);
+ accumInfRightX = accumInfRightProjMat[3];
+ accumInfRightY = accumInfRightProjMat[7];
+ }
+ }
+ } else {
+ if (!canvas.antialiasingSet) {
+ canvas.setFullSceneAntialiasing(canvas.ctx, true);
+ canvas.antialiasingSet = true;
+ }
+ }
+ } else {
+ if (canvas.antialiasingSet) {
+ canvas.setFullSceneAntialiasing(canvas.ctx, false);
+ canvas.antialiasingSet = false;
+ }
+ }
+
+ // background geometry setup
+ if (renderBin.geometryBackground != null) {
+ renderBin.updateInfVworldToVpc();
+ }
+
+ // setup default render mode - render to both eyes
+ canvas.setRenderMode(canvas.ctx,
+ Canvas3D.FIELD_ALL,
+ canvas.useDoubleBuffer);
+
+ // Support DVR
+ /*
+ System.out.println("canvas.supportVideoResize() is " +
+ canvas.supportVideoResize());
+ */
+ if(canvas.supportVideoResize()) {
+ if(canvas.view.dvrResizeCompensation !=
+ canvas.cachedDvrResizeCompensation) {
+ /*
+ System.out.println("Renderer : dvrResizeComp " +
+ canvas.view.dvrResizeCompensation);
+ */
+ canvas.videoResizeCompensation(canvas.ctx,
+ canvas.view.dvrResizeCompensation);
+ canvas.cachedDvrResizeCompensation =
+ canvas.view.dvrResizeCompensation;
+
+ }
+ if(canvas.view.dvrFactor != canvas.cachedDvrFactor) {
+ /*
+ System.out.println("Renderer : dvrFactor is " +
+ canvas.view.dvrFactor);
+ */
+ canvas.videoResize(canvas.ctx,
+ canvas.screen.display,
+ canvas.window,
+ canvas.view.dvrFactor);
+ canvas.cachedDvrFactor = canvas.view.dvrFactor;
+
+ }
+
+ }
+
+ canvas.beginScene();
+
+ // this is if the background image resizes with the canvas
+ int winWidth = copyOfCvCache.getCanvasWidth();
+ int winHeight = copyOfCvCache.getCanvasHeight();
+
+
+ // clear background if not full screen antialiasing
+ // and not in stereo mode
+ if (!doAccum && !sharedStereoZBuffer) {
+ BackgroundRetained bg = renderBin.background;
+ if (!VirtualUniverse.mc.isBackgroundTexture) {
+ canvas.clear(canvas.ctx,
+ bg.color.x,
+ bg.color.y,
+ bg.color.z,
+ winWidth,
+ winHeight,
+ bg.image,
+ bg.imageScaleMode,
+ (bg.image != null?
+ bg.image.imageYdown[0]:null));
+ } else {
+ if ((bg.texImage != null) &&
+ (objectId == -1)) {
+ objectId = VirtualUniverse.mc.
+ getTexture2DId();
+ }
+ canvas.textureclear(canvas.ctx,
+ bg.xmax,
+ bg.ymax,
+ bg.color.x,
+ bg.color.y,
+ bg.color.z,
+ winWidth,
+ winHeight,
+ objectId,
+ bg.imageScaleMode,
+ bg.texImage,
+ background_image_update);
+ }
+// canvas.clear(canvas.ctx,
+// bg.color.x,
+// bg.color.y,
+// bg.color.z,
+// bg.image);
+ }
+
+ // handle preRender callback
+ if (VirtualUniverse.mc.doDsiRenderLock) {
+ canvas.drawingSurfaceObject.unLock();
+ }
+ canvas.view.inCanvasCallback = true;
+
+ try {
+ canvas.preRender();
+ } catch (RuntimeException e) {
+ System.err.println("Exception occurred " +
+ "during Canvas3D callback:");
+ e.printStackTrace();
+ }
+ canvas.view.inCanvasCallback = false;
+
+ if ((VirtualUniverse.mc.doDsiRenderLock) &&
+ (!canvas.drawingSurfaceObject.renderLock())) {
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+
+ // render loop
+ for (pass = 0; pass < num_stereo_passes; pass++) {
+ if (doAccum) {
+ canvas.clearAccum(canvas.ctx);
+ }
+ canvas.setRenderMode(canvas.ctx, stereo_mode,
+ canvas.useDoubleBuffer);
+
+
+
+ for (apass = 0; apass < num_accum_passes; apass++) {
+
+ // jitter projection matrix and clear background
+ // for full screen anti-aliasing rendering
+ if (doAccum) {
+ accumDx = ACCUM_SAMPLES_X[apass] *
+ accumDxFactor;
+ accumDy = ACCUM_SAMPLES_Y[apass] *
+ accumDyFactor;
+
+ accumLeftProjMat[3] = accumLeftX +
+ accumLeftProjMat[0] * accumDx +
+ accumLeftProjMat[1] * accumDy;
+
+ accumLeftProjMat[7] = accumLeftY +
+ accumLeftProjMat[4] * accumDx +
+ accumLeftProjMat[5] * accumDy;
+
+ if (useStereo) {
+ accumRightProjMat[3] = accumRightX +
+ accumRightProjMat[0] * accumDx +
+ accumRightProjMat[1] * accumDy;
+
+ accumRightProjMat[7] = accumRightY +
+ accumRightProjMat[4] * accumDx +
+ accumRightProjMat[5] * accumDy;
+ }
+
+ if (renderBin.geometryBackground != null) {
+ accumInfLeftProjMat[3] = accumInfLeftX +
+ accumInfLeftProjMat[0] * accumDx +
+ accumInfLeftProjMat[1] * accumDy;
+
+ accumInfLeftProjMat[7] = accumInfLeftY +
+ accumInfLeftProjMat[4] * accumDx +
+ accumInfLeftProjMat[5] * accumDy;
+
+ if (useStereo) {
+ accumInfRightProjMat[3] =
+ accumInfRightX +
+ accumInfRightProjMat[0] * accumDx +
+ accumInfRightProjMat[1] * accumDy;
+
+ accumInfRightProjMat[7] =
+ accumInfRightY +
+ accumInfRightProjMat[4] * accumDx +
+ accumInfRightProjMat[5] * accumDy;
+ }
+ }
+ }
+
+ // clear background for stereo and
+ // accumulation buffer cases
+ if (doAccum || sharedStereoZBuffer) {
+ BackgroundRetained bg = renderBin.background;
+ if (!VirtualUniverse.mc.isBackgroundTexture) {
+ canvas.clear(canvas.ctx,
+ bg.color.x,
+ bg.color.y,
+ bg.color.z,
+ winWidth,
+ winHeight,
+ bg.image,
+ bg.imageScaleMode,
+ (bg.image != null?bg.image.imageYdown[0]:null));
+ }
+ else {
+ if ((bg.texImage != null) &&
+ (objectId == -1)) {
+ objectId = VirtualUniverse.mc.
+ getTexture2DId();
+ }
+
+ canvas.textureclear(canvas.ctx,
+ bg.xmax,
+ bg.ymax,
+ bg.color.x,
+ bg.color.y,
+ bg.color.z,
+ winWidth,
+ winHeight,
+ objectId,
+ bg.imageScaleMode,
+ bg.texImage,
+ background_image_update);
+ }
+ }
+
+ // render background geometry
+ if (renderBin.geometryBackground != null) {
+
+ // setup rendering matrices
+ if (pass == 0) {
+ canvas.vpcToEc =
+ copyOfCvCache.getInfLeftVpcToEc();
+ if (doAccum) {
+ canvas.setProjectionMatrix(
+ canvas.ctx,
+ accumInfLeftProjMat);
+ } else {
+ canvas.setProjectionMatrix(
+ canvas.ctx,
+ copyOfCvCache.getInfLeftProjection().mat);
+ }
+ } else {
+ canvas.vpcToEc =
+ copyOfCvCache.getInfRightVpcToEc();
+ if (doAccum) {
+ canvas.setProjectionMatrix(
+ canvas.ctx,
+ accumInfRightProjMat);
+ } else {
+ canvas.setProjectionMatrix(
+ canvas.ctx,
+ copyOfCvCache.getInfRightProjection().mat);
+ }
+ }
+ canvas.vworldToEc.mul(canvas.vpcToEc,
+ copyOfCvCache.getInfVworldToVpc());
+
+ // render background geometry
+ renderBin.renderBackground(canvas);
+ }
+
+ // setup rendering matrices
+ if (pass == 0) {
+ canvas.vpcToEc = copyOfCvCache.getLeftVpcToEc();
+ if (doAccum) {
+ canvas.setProjectionMatrix(
+ canvas.ctx, accumLeftProjMat);
+ } else {
+ canvas.setProjectionMatrix(canvas.ctx,
+ copyOfCvCache.getLeftProjection().mat);
+ }
+ } else {
+ canvas.vpcToEc = copyOfCvCache.getRightVpcToEc();
+ if (doAccum) {
+ canvas.setProjectionMatrix(
+ canvas.ctx, accumRightProjMat);
+ } else {
+ canvas.setProjectionMatrix(canvas.ctx,
+ copyOfCvCache.getRightProjection().mat);
+ }
+ }
+ canvas.vworldToEc.mul(canvas.vpcToEc,
+ copyOfCvCache.getVworldToVpc());
+
+
+ synchronized (copyOfCvCache) {
+ if (pass == 0) {
+ canvas.setFrustumPlanes(copyOfCvCache.getLeftFrustumPlanesInVworld());
+ } else {
+ canvas.setFrustumPlanes(copyOfCvCache.getRightFrustumPlanesInVworld());
+ }
+ }
+
+ // render opaque geometry
+ renderBin.renderOpaque(canvas);
+
+ // render ordered geometry
+ renderBin.renderOrdered(canvas);
+
+ // handle renderField callback
+ if (VirtualUniverse.mc.doDsiRenderLock) {
+ canvas.drawingSurfaceObject.unLock();
+ }
+ canvas.view.inCanvasCallback = true;
+ try {
+ canvas.renderField(stereo_mode);
+ } catch (RuntimeException e) {
+ System.err.println("Exception occurred during " +
+ "Canvas3D callback:");
+ e.printStackTrace();
+ }
+ canvas.view.inCanvasCallback = false;
+ if ((VirtualUniverse.mc.doDsiRenderLock) &&
+ (!canvas.drawingSurfaceObject.renderLock())) {
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ break doneRender;
+ }
+
+ // render transparent geometry
+ renderBin.renderTransparent(canvas);
+
+ if (doAccum)
+ canvas.accum(canvas.ctx, accumValue);
+ }
+
+ if (doAccum)
+ canvas.accumReturn(canvas.ctx);
+ if (useStereo) {
+ stereo_mode = Canvas3D.FIELD_RIGHT;
+ canvas.rightStereoPass = true;
+ }
+ }
+ canvas.imageReady = true;
+ canvas.rightStereoPass = false;
+
+ // reset renderMode
+ canvas.setRenderMode(canvas.ctx,
+ Canvas3D.FIELD_ALL,
+ canvas.useDoubleBuffer);
+
+ // handle postRender callback
+ if (VirtualUniverse.mc.doDsiRenderLock) {
+ canvas.drawingSurfaceObject.unLock();
+ }
+ canvas.view.inCanvasCallback = true;
+
+ try {
+ canvas.postRender();
+ } catch (RuntimeException e) {
+ System.err.println("Exception occurred during " +
+ "Canvas3D callback:");
+ e.printStackTrace();
+ }
+ canvas.view.inCanvasCallback = false;
+
+ // end offscreen rendering
+ if (canvas.offScreenRendering) {
+
+ canvas.syncRender(canvas.ctx, true);
+ canvas.endOffScreenRendering();
+
+ // do the postSwap for offscreen here
+ canvas.view.inCanvasCallback = true;
+ try {
+ canvas.postSwap();
+ } catch (RuntimeException e) {
+ System.err.println("Exception occurred during Canvas 3D callback:");
+ e.printStackTrace();
+ }
+
+ if (offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+
+ canvas.offScreenRendering = false;
+ canvas.view.inCanvasCallback = false;
+ }
+
+
+ canvas.endScene();
+
+ if (doTiming) {
+ numframes += 1.0f;
+ if (numframes >= 20.0f) {
+ currtime = System.currentTimeMillis();
+ System.err.println(
+ numframes/((currtime-lasttime)/1000.0f) +
+ " frames per second");
+ numframes = 0.0f;
+ lasttime = currtime;
+
+ // For taking memory footprint of the entire scene.
+ /*
+ long totalMem, freeMem, usedMem;
+ for(int ii=0; ii<5;ii++) {
+ totalMem = Runtime.getRuntime().totalMemory();
+ freeMem = Runtime.getRuntime().freeMemory();
+ usedMem = totalMem - freeMem;
+ System.out.print("mem used - before: " + usedMem + "bytes ");
+ //System.out.print("mem used - before: " + usedMem + " ");
+ System.runFinalization();
+ System.gc();
+ System.runFinalization();
+ totalMem = Runtime.getRuntime().totalMemory();
+ freeMem = Runtime.getRuntime().freeMemory();
+ usedMem = totalMem - freeMem;
+ System.out.println("after: " + usedMem + "bytes ");
+ //System.out.println("after: " + usedMem + " ");
+ try {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e) { }
+
+ }
+ */
+
+ }
+ }
+ } else { // if (renderBin != null)
+ if ((offBufRetained != null) &&
+ offBufRetained.isByReference()) {
+ offBufRetained.geomLock.unLock();
+ }
+ }
+ }
+ }
+
+ // clear array to prevent memory leaks
+ if (opArg == RENDER) {
+ m[0] = null;
+ } else {
+ Arrays.fill(m, 0, totalMessages, null);
+ }
+ }
+ } catch (NullPointerException ne) {
+ ne.printStackTrace();
+ if (canvas != null) {
+ if (canvas.ctx != 0) {
+ canvas.endScene();
+ }
+ // drawingSurfaceObject will safely ignore
+ // this request if this is not lock before
+ canvas.drawingSurfaceObject.unLock();
+
+ }
+ }
+ }
+
+ // resource clean up
+ void shutdown() {
+ removeAllCtxs();
+
+ if (VirtualUniverse.mc.isD3D()) {
+ D3DCleanUp();
+ }
+ }
+
+ void cleanup() {
+ super.cleanup();
+ renderMessage = new J3dMessage[1];
+ rendererStructure = new RendererStructure();
+ bgVworldToVpc = new Transform3D();
+ numframes = 0.0f;
+ sharedCtx = 0;
+ sharedCtxTimeStamp = 0;
+ dirtyRenderMoleculeList.clear();
+ dirtyRenderAtomList.clear();
+ dirtyDlistPerRinfoList.clear();
+ textureIdResourceFreeList.clear();
+ displayListResourceFreeList.clear();
+ copyOfCvCache = new CanvasViewCache(null, null, null);
+ onScreen = null;
+ offScreen = null;
+ m = null;
+ nmesg = 0;
+ lasttime = 0;
+ currtime = 0;
+ display = 0;
+ }
+
+
+
+ // This is only invoked from removeCtx()/removeAllCtxs()
+ // with drawingSurface already lock
+ final void makeCtxCurrent(long sharedCtx, long display, int window) {
+ if (sharedCtx != currentCtx) {
+ Canvas3D.useCtx(sharedCtx, display, window);
+ currentCtx = sharedCtx;
+ }
+ }
+
+ // No need to free graphics2d and background if it is from
+ // Canvas3D postRequest() offScreen rendering since the
+ // user thread will not wait for it. Also we can just
+ // reuse it as Canvas3D did not destroy.
+ void removeCtx(Canvas3D cv, long display, int window, long ctx,
+ boolean resetCtx, boolean freeBackground) {
+
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ if (ctx != 0) {
+ int idx = listOfCtxs.indexOf(new Long(ctx));
+ if (idx >= 0) {
+ listOfCtxs.remove(idx);
+ listOfCanvases.remove(idx);
+ // display is always 0 under windows
+ if ((MasterControl.isWin32 || (display != 0)) &&
+ (window != 0) && cv.added) {
+ // cv.ctx may reset to -1 here so we
+ // always use the ctx pass in.
+ if (cv.drawingSurfaceObject.renderLock()) {
+ // if it is the last one, free shared resources
+ if (sharedCtx != 0) {
+ if (listOfCtxs.isEmpty()) {
+ makeCtxCurrent(sharedCtx, display, window);
+ freeResourcesInFreeList(null);
+ freeContextResources();
+ Canvas3D.destroyContext(display, window, sharedCtx);
+ currentCtx = -1;
+ } else {
+ freeResourcesInFreeList(cv);
+ }
+ cv.makeCtxCurrent(ctx, display, window);
+ } else {
+ cv.makeCtxCurrent(ctx, display, window);
+ cv.freeResourcesInFreeList(ctx);
+ }
+ cv.freeContextResources(this, freeBackground, ctx);
+ Canvas3D.destroyContext(display, window, ctx);
+ currentCtx = -1;
+ cv.drawingSurfaceObject.unLock();
+ }
+ }
+ }
+
+ if (resetCtx) {
+ cv.ctx = 0;
+ }
+
+ if ((sharedCtx != 0) && listOfCtxs.isEmpty()) {
+ sharedCtx = 0;
+ sharedCtxTimeStamp = 0;
+ }
+ cv.ctxTimeStamp = 0;
+ }
+ }
+ }
+
+ void removeAllCtxs() {
+ Canvas3D cv;
+
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+
+ for (int i=listOfCanvases.size()-1; i >=0; i--) {
+ cv = (Canvas3D) listOfCanvases.get(i);
+
+ if ((cv.screen != null) && (cv.ctx != 0)) {
+ if ((MasterControl.isWin32 || (display != 0)) &&
+ (cv.window != 0) && cv.added) {
+ if (cv.drawingSurfaceObject.renderLock()) {
+ // We need to free sharedCtx resource
+ // first before last non-sharedCtx to
+ // workaround Nvidia driver bug under Linux
+ // that crash on freeTexture ID:4685156
+ if ((i == 0) && (sharedCtx != 0)) {
+ makeCtxCurrent(sharedCtx, display, window);
+ freeResourcesInFreeList(null);
+ freeContextResources();
+ Canvas3D.destroyContext(display, window, sharedCtx);
+ currentCtx = -1;
+ }
+ cv.makeCtxCurrent();
+ cv.freeResourcesInFreeList(cv.ctx);
+ cv.freeContextResources(this, true, cv.ctx);
+ Canvas3D.destroyContext(cv.screen.display,
+ cv.window,
+ cv.ctx);
+ currentCtx = -1;
+ cv.drawingSurfaceObject.unLock();
+ }
+ }
+ }
+
+ cv.ctx = 0;
+ cv.ctxTimeStamp = 0;
+ }
+
+ if (sharedCtx != 0) {
+ sharedCtx = 0;
+ sharedCtxTimeStamp = 0;
+ }
+ listOfCanvases.clear();
+ listOfCtxs.clear();
+ }
+ }
+
+ void freeTextureID(int texId, String texture) {
+ Canvas3D currentCanvas = null;
+
+ // get the current canvas
+ for (int i=listOfCtxs.size()-1; i >= 0; i--) {
+ Canvas3D c = (Canvas3D) listOfCanvases.get(i);
+ if (c.ctx == currentCtx) {
+ currentCanvas = c;
+ break;
+ }
+ }
+
+ if (currentCanvas == null) {
+ return;
+ }
+
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ if (sharedCtx != 0) {
+ currentCanvas.makeCtxCurrent(sharedCtx);
+ // OGL share context is used
+ Canvas3D.freeTexture(sharedCtx, texId);
+ } else {
+ for (int i=listOfCtxs.size()-1; i >= 0; i--) {
+ Canvas3D c = (Canvas3D) listOfCanvases.get(i);
+ c.makeCtxCurrent();
+ Canvas3D.freeTexture(c.ctx, texId);
+ }
+ }
+ // restore current context
+ currentCanvas.makeCtxCurrent();
+ }
+ if (texture.equals("2D")){
+ VirtualUniverse.mc.freeTexture2DId(texId);
+ }
+ else if(texture.equals("3D")){
+ VirtualUniverse.mc.freeTexture3DId(texId);
+ }
+ }
+
+
+ // handle free resource in the FreeList
+ void freeResourcesInFreeList(Canvas3D cv) {
+ Iterator it;
+ boolean isFreeTex = (textureIdResourceFreeList.size() > 0);
+ boolean isFreeDL = (displayListResourceFreeList.size() > 0);
+ ArrayList list;
+ int i, val;
+ GeometryArrayRetained geo;
+
+ if (isFreeTex || isFreeDL) {
+ if (cv != null) {
+ cv.makeCtxCurrent(sharedCtx);
+ }
+
+ if (isFreeDL) {
+ for (it = displayListResourceFreeList.iterator(); it.hasNext();) {
+ val = ((Integer) it.next()).intValue();
+ if (val <= 0) {
+ continue;
+ }
+ Canvas3D.freeDisplayList(sharedCtx, val);
+ }
+ displayListResourceFreeList.clear();
+ }
+ if (isFreeTex) {
+ for (it = textureIdResourceFreeList.iterator(); it.hasNext();) {
+ val = ((Integer) it.next()).intValue();
+ if (val <= 0) {
+ continue;
+ }
+ if (val >= textureIDResourceTable.size()) {
+ System.out.println("Error in freeResourcesInFreeList : ResourceIDTableSize = " +
+ textureIDResourceTable.size() +
+ " val = " + val);
+ } else {
+ textureIDResourceTable.set(val, null);
+ }
+ Canvas3D.freeTexture(sharedCtx, val);
+ }
+ textureIdResourceFreeList.clear();
+ }
+ if (cv != null) {
+ cv.makeCtxCurrent(cv.ctx);
+ }
+ }
+ }
+
+ final void addTextureResource(int id, Object obj) {
+ if (textureIDResourceTable.size() <= id) {
+ for (int i=textureIDResourceTable.size();
+ i < id; i++) {
+ textureIDResourceTable.add(null);
+ }
+ textureIDResourceTable.add(obj);
+ } else {
+ textureIDResourceTable.set(id, obj);
+ }
+ }
+
+ void freeContextResources() {
+ Object obj;
+ TextureRetained tex;
+ DetailTextureImage detailTex;
+
+ for (int id = textureIDResourceTable.size()-1; id > 0; id--) {
+ obj = textureIDResourceTable.get(id);
+ if (obj == null) {
+ continue;
+ }
+ Canvas3D.freeTexture(sharedCtx, id);
+ if (obj instanceof TextureRetained) {
+ tex = (TextureRetained) obj;
+ synchronized (tex.resourceLock) {
+ tex.resourceCreationMask &= ~rendererBit;
+ if (tex.resourceCreationMask == 0) {
+ tex.freeTextureId(id);
+ }
+ }
+ } else if (obj instanceof DetailTextureImage) {
+ detailTex = (DetailTextureImage) obj;
+ detailTex.freeDetailTextureId(id, rendererBit);
+ }
+
+ }
+ textureIDResourceTable.clear();
+
+ // displayList is free in Canvas.freeContextResources()
+ }
+
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/RendererStructure.java b/src/classes/share/javax/media/j3d/RendererStructure.java
new file mode 100644
index 0000000..34122a4
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RendererStructure.java
@@ -0,0 +1,56 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A renderer structure is an object that organizes messages
+ * to the renderer thread.
+ */
+class RendererStructure extends J3dStructure{
+
+ /**
+ * This constructor does nothing
+ */
+ RendererStructure() {
+ super(null, J3dThread.RENDER_THREAD);
+ }
+
+ /**
+ * Returns all messages in the queue.
+ */
+ J3dMessage[] getMessages() {
+ int sz;
+
+ synchronized (messageList) {
+ if ((sz = messageList.size()) > 0) {
+ if (msgList.length < sz) {
+ msgList = new J3dMessage[sz];
+ }
+ messageList.toArrayAndClear(msgList);
+ }
+ }
+
+ nMessage = sz;
+ return msgList;
+ }
+
+
+ void processMessages(long referenceTime) {}
+
+ void removeNodes(J3dMessage m) {}
+
+ void cleanup() {}
+}
diff --git a/src/classes/share/javax/media/j3d/RenderingAttributes.java b/src/classes/share/javax/media/j3d/RenderingAttributes.java
new file mode 100644
index 0000000..488c48d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderingAttributes.java
@@ -0,0 +1,735 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The RenderingAttributes object defines common rendering attributes
+ * for all primitive types. The rendering attributes are:<p>
+ * <ul>
+ * <li>Alpha test function - used to compare the alpha test value with
+ * each per-pixel alpha value. If the test passes, the pixel is
+ * written, otherwise the pixel is not written. The alpha test
+ * function is set with the <code>setAlphaTestFunction</code>
+ * method. The alpha test
+ * function is one of the following:</li><p>
+ * <ul>
+ * <li>ALWAYS - pixels are always drawn, irrespective of the alpha
+ * value. This effectively disables alpha testing. This is
+ * the default setting.</li><p>
+ *
+ * <li>NEVER - pixels are never drawn, irrespective of the alpha
+ * value.</li><p>
+ *
+ * <li>EQUAL - pixels are drawn if the pixel alpha value is equal
+ * to the alpha test value.</li><p>
+ *
+ * <li>NOT_EQUAL - pixels are drawn if the pixel alpha value is
+ * not equal to the alpha test value.</li><p>
+ *
+ * <li>LESS - pixels are drawn if the pixel alpha value is less
+ * than the alpha test value.</li><p>
+ *
+ * <li>LESS_OR_EQUAL - pixels are drawn if the pixel alpha value
+ * is less than or equal to the alpha test value.</li><p>
+ *
+ * <li>GREATER - pixels are drawn if the pixel alpha value is greater
+ * than the alpha test value.</li><p>
+ *
+ * <li>GREATER_OR_EQUAL - pixels are drawn if the pixel alpha
+ * value is greater than or equal to the alpha test value.</li><p>
+ * </ul>
+ * <li>Alpha test value - the test value used by the alpha test function.
+ * This value is compared to the alpha value of each rendered pixel.
+ * The alpha test value is set with the <code>setAlphaTestValue</code>
+ * method. The default alpha test value is 0.0.</li><p>
+ *
+ * <li>Raster operation - the raster operation function for this
+ * RenderingAttributes component object. The raster operation is
+ * set with the <code>setRasterOp</code> method. The raster operation
+ * is enabled or disabled with the <code>setRasterOpEnable</code>
+ * method. The raster operation is one of the following:</li><p>
+ * <ul>
+ * <li>ROP_COPY - DST = SRC. This is the default operation.</li>
+ * <li>ROP_XOR - DST = SRC ^ DST.</li><p>
+ * </ul>
+ * <li>Vertex colors - vertex colors can be ignored for this
+ * RenderingAttributes object. This capability is set with the
+ * <code>setIgnoreVertexColors</code> method. If
+ * ignoreVertexColors is false, per-vertex colors are used, when
+ * present in the associated geometry objects, taking
+ * precedence over the ColoringAttributes color and the
+ * specified Material color(s). If ignoreVertexColors is true, per-vertex
+ * colors are ignored. In this case, if lighting is enabled, the
+ * Material diffuse color will be used as the object color.
+ * if lighting is disabled, the ColoringAttributes color is
+ * used. The default value is false.</li><p>
+ *
+ * <li>Visibility flag - when set, invisible objects are
+ * not rendered (subject to the visibility policy for
+ * the current view), but they can be picked or collided with.
+ * This flag is set with the <code>setVisible</code>
+ * method. By default, the visibility flag is true.</li><p>
+ *
+ * <li>Depth buffer - can be enabled or disabled for this
+ * RenderingAttributes component object. The
+ * <code>setDepthBufferEnable</code> method enables
+ * or disabled the depth buffer. The
+ * <code>setDepthBufferWriteEnable</code> method enables or disables
+ * writing the depth buffer for this object. During the transparent
+ * rendering pass, this attribute can be overridden by the
+ * depthBufferFreezeTransparent attribute in the View
+ * object. Transparent objects include BLENDED transparent and
+ * antialiased lines and points. Transparent objects do not
+ * include opaque objects or primitives rendered with
+ * SCREEN_DOOR transparency. By default, the depth buffer
+ * is enabled and the depth buffer write is enabled.</li><p>
+ * </ul>
+ *
+ * @see Appearance
+ *
+ */
+public class RenderingAttributes extends NodeComponent {
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows reading its alpha test value component information.
+ */
+ public static final int
+ ALLOW_ALPHA_TEST_VALUE_READ = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_VALUE_READ;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows writing its alpha test value component information.
+ */
+ public static final int
+ ALLOW_ALPHA_TEST_VALUE_WRITE = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_VALUE_WRITE;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows reading its alpha test function component information.
+ */
+ public static final int
+ ALLOW_ALPHA_TEST_FUNCTION_READ = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_FUNCTION_READ;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows writing its alpha test function component information.
+ */
+ public static final int
+ ALLOW_ALPHA_TEST_FUNCTION_WRITE = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_FUNCTION_WRITE;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows reading its depth buffer enable and depth buffer write enable
+ * component information.
+ */
+ public static final int
+ ALLOW_DEPTH_ENABLE_READ = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_DEPTH_ENABLE_READ;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows writing its depth buffer enable and depth buffer write enable
+ * component information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_DEPTH_ENABLE_WRITE =
+ CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_DEPTH_ENABLE_WRITE;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows reading its visibility information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_VISIBLE_READ =
+ CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_VISIBLE_READ;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows writing its visibility information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_VISIBLE_WRITE =
+ CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_VISIBLE_WRITE;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows reading its ignore vertex colors information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_IGNORE_VERTEX_COLORS_READ =
+ CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_IGNORE_VERTEX_COLORS_READ;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows writing its ignore vertex colors information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_IGNORE_VERTEX_COLORS_WRITE =
+ CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_IGNORE_VERTEX_COLORS_WRITE;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows reading its raster operation information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_RASTER_OP_READ =
+ CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_RASTER_OP_READ;
+
+ /**
+ * Specifies that this RenderingAttributes object
+ * allows writing its raster operation information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_RASTER_OP_WRITE =
+ CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_RASTER_OP_WRITE;
+
+ /**
+ * Indicates pixels are always drawn irrespective of alpha value.
+ * This effectively disables alpha testing.
+ */
+ public static final int ALWAYS = 0;
+
+ /**
+ * Indicates pixels are never drawn irrespective of alpha value.
+ */
+ public static final int NEVER = 1;
+
+ /**
+ * Indicates pixels are drawn if pixel alpha value is equal
+ * to alpha test value.
+ */
+ public static final int EQUAL = 2;
+
+ /**
+ * Indicates pixels are drawn if pixel alpha value is not equal
+ * to alpha test value.
+ */
+ public static final int NOT_EQUAL = 3;
+
+ /**
+ * Indicates pixels are drawn if pixel alpha value is less
+ * than alpha test value.
+ */
+ public static final int LESS = 4;
+
+ /**
+ * Indicates pixels are drawn if pixel alpha value is less
+ * than or equal to alpha test value.
+ */
+ public static final int LESS_OR_EQUAL = 5;
+
+ /**
+ * Indicates pixels are drawn if pixel alpha value is greater
+ * than alpha test value.
+ */
+ public static final int GREATER = 6;
+
+ /**
+ * Indicates pixels are drawn if pixel alpha value is greater
+ * than or equal to alpha test value.
+ */
+ public static final int GREATER_OR_EQUAL = 7;
+
+
+// public static final int ROP_CLEAR = 0x0;
+// public static final int ROP_AND = 0x1;
+// public static final int ROP_AND_REVERSE = 0x2;
+
+ /**
+ * Raster operation: <code>DST = SRC</code>.
+ * @see #setRasterOp
+ * @since Java 3D 1.2
+ */
+ public static final int ROP_COPY = 0x3;
+
+// public static final int ROP_AND_INVERTED = 0x4;
+// public static final int ROP_NOOP = 0x5;
+
+ /**
+ * Raster operation: <code>DST = SRC ^ DST</code>.
+ * @see #setRasterOp
+ * @since Java 3D 1.2
+ */
+ public static final int ROP_XOR = 0x6;
+
+// public static final int ROP_OR = 0x7;
+// public static final int ROP_NOR = 0x8;
+// public static final int ROP_EQUIV = 0x9;
+// public static final int ROP_INVERT = 0xA;
+// public static final int ROP_OR_REVERSE = 0xB;
+// public static final int ROP_COPY_INVERTED = 0xC;
+// public static final int ROP_OR_INVERTED = 0xD;
+// public static final int ROP_NAND = 0xE;
+// public static final int ROP_SET = 0xF;
+
+
+ /**
+ * Constructs a RenderingAttributes object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * depth buffer enable : true<br>
+ * depth buffer write enable : true<br>
+ * alpha test function : ALWAYS<br>
+ * alpha test value : 0.0<br>
+ * visible : true<br>
+ * ignore vertex colors : false<br>
+ * raster operation enable : false<br>
+ * raster operation : ROP_COPY<br>
+ * </ul>
+ */
+ public RenderingAttributes() {
+ // Just use default attributes
+ }
+
+ /**
+ * Constructs a RenderingAttributes object with specified values.
+ * @param depthBufferEnable a flag to turn depth buffer on/off
+ * @param depthBufferWriteEnable a flag to to make depth buffer
+ * read/write or read only
+ * @param alphaTestValue the alpha test reference value
+ * @param alphaTestFunction the function for comparing alpha values
+ */
+ public RenderingAttributes(boolean depthBufferEnable,
+ boolean depthBufferWriteEnable,
+ float alphaTestValue,
+ int alphaTestFunction){
+
+ this(depthBufferEnable, depthBufferWriteEnable, alphaTestValue,
+ alphaTestFunction, true, false, false, ROP_COPY);
+ }
+
+ /**
+ * Constructs a RenderingAttributes object with specified values
+ * @param depthBufferEnable a flag to turn depth buffer on/off
+ * @param depthBufferWriteEnable a flag to make depth buffer
+ * read/write or read only
+ * @param alphaTestValue the alpha test reference value
+ * @param alphaTestFunction the function for comparing alpha values
+ * @param visible a flag that specifies whether the object is visible
+ * @param ignoreVertexColors a flag to enable or disable
+ * the ignoring of per-vertex colors
+ * @param rasterOpEnable a flag that specifies whether logical
+ * raster operations are enabled for this RenderingAttributes object.
+ * This disables all alpha blending operations.
+ * @param rasterOp the logical raster operation, one of ROP_COPY or
+ * ROP_XOR.
+ *
+ * @since Java 3D 1.2
+ */
+ public RenderingAttributes(boolean depthBufferEnable,
+ boolean depthBufferWriteEnable,
+ float alphaTestValue,
+ int alphaTestFunction,
+ boolean visible,
+ boolean ignoreVertexColors,
+ boolean rasterOpEnable,
+ int rasterOp) {
+
+ ((RenderingAttributesRetained)this.retained).initDepthBufferEnable(depthBufferEnable);
+ ((RenderingAttributesRetained)this.retained).initDepthBufferWriteEnable(depthBufferWriteEnable);
+ ((RenderingAttributesRetained)this.retained).initAlphaTestValue(alphaTestValue);
+ ((RenderingAttributesRetained)this.retained).initAlphaTestFunction(alphaTestFunction);
+ ((RenderingAttributesRetained)this.retained).initVisible(visible);
+
+
+ ((RenderingAttributesRetained)this.retained).initIgnoreVertexColors(ignoreVertexColors);
+ ((RenderingAttributesRetained)this.retained).initRasterOpEnable(rasterOpEnable);
+ ((RenderingAttributesRetained)this.retained).initRasterOp(rasterOp);
+ }
+
+ /**
+ * Enables or disables depth buffer mode for this RenderingAttributes
+ * component object.
+ * @param state true or false to enable or disable depth buffer mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDepthBufferEnable(boolean state){
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DEPTH_ENABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes0"));
+
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setDepthBufferEnable(state);
+ else
+ ((RenderingAttributesRetained)this.retained).initDepthBufferEnable(state);
+
+ }
+
+ /**
+ * Retrieves the state of zBuffer Enable flag
+ * @return true if depth buffer mode is enabled, false
+ * if depth buffer mode is disabled
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getDepthBufferEnable(){
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DEPTH_ENABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes1"));
+
+ return ((RenderingAttributesRetained)this.retained).getDepthBufferEnable();
+ }
+
+ /**
+ * Enables or disables writing the depth buffer for this object.
+ * During the transparent rendering pass,
+ * this attribute can be overridden by
+ * the depthBufferFreezeTransparent attribute in the View object.
+ * @param state true or false to enable or disable depth buffer Write mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see View#setDepthBufferFreezeTransparent
+ */
+ public void setDepthBufferWriteEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DEPTH_ENABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes2"));
+
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setDepthBufferWriteEnable(state);
+ else
+ ((RenderingAttributesRetained)this.retained).initDepthBufferWriteEnable(state);
+ }
+
+ /**
+ * Retrieves the state of Depth Buffer Write Enable flag.
+ * @return true if depth buffer is writable, false
+ * if depth buffer is read-only
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getDepthBufferWriteEnable(){
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DEPTH_ENABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes3"));
+
+ return ((RenderingAttributesRetained)this.retained).getDepthBufferWriteEnable();
+ }
+
+ /**
+ * Set alpha test value used by alpha test function. This value is
+ * compared to the alpha value of each rendered pixel.
+ * @param value the alpha test value
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAlphaTestValue(float value){
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ALPHA_TEST_VALUE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes4"));
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setAlphaTestValue(value);
+ else
+ ((RenderingAttributesRetained)this.retained).initAlphaTestValue(value);
+
+ }
+
+ /**
+ * Retrieves the alpha test value.
+ * @return the alpha test value.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getAlphaTestValue(){
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ALPHA_TEST_VALUE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes5"));
+
+ return ((RenderingAttributesRetained)this.retained).getAlphaTestValue();
+ }
+
+ /**
+ * Set alpha test function. This function is used to compare the
+ * alpha test value with each per-pixel alpha value. If the test
+ * passes, the pixel is written otherwise the pixel is not
+ * written.
+ * @param function the new alpha test function. One of
+ * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
+ * GREATER_OR_EQUAL
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAlphaTestFunction(int function){
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ALPHA_TEST_FUNCTION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes6"));
+
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setAlphaTestFunction(function);
+ else
+ ((RenderingAttributesRetained)this.retained).initAlphaTestFunction(function);
+
+ }
+
+ /**
+ * Retrieves current alpha test function.
+ * @return the current alpha test function
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getAlphaTestFunction(){
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_ALPHA_TEST_FUNCTION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes7"));
+
+ return ((RenderingAttributesRetained)this.retained).getAlphaTestFunction();
+ }
+
+ /**
+ * Sets the visibility flag for this RenderingAttributes
+ * component object. Invisible objects are not rendered (subject to
+ * the visibility policy for the current view), but they can be picked
+ * or collided with. The default value is true.
+ * @param visible true or false to enable or disable visibility
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see View#setVisibilityPolicy
+ *
+ * @since Java 3D 1.2
+ */
+ public void setVisible(boolean visible) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_VISIBLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes8"));
+
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setVisible(visible);
+ else
+ ((RenderingAttributesRetained)this.retained).initVisible(visible);
+ }
+
+ /**
+ * Retrieves the visibility flag for this RenderingAttributes object.
+ * @return true if the object is visible; false
+ * if the object is invisible.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getVisible() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_VISIBLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes9"));
+
+ return ((RenderingAttributesRetained)this.retained).getVisible();
+ }
+
+ /**
+ * Sets a flag that indicates whether vertex colors are ignored
+ * for this RenderingAttributes object. If
+ * <code>ignoreVertexColors</code> is false, per-vertex
+ * colors are used, when present in the associated Geometry
+ * objects, taking precedence over the ColoringAttributes color
+ * and the specified Material color(s). If <code>ignoreVertexColors</code>
+ * is true, per-vertex colors are ignored. In this case, if
+ * lighting is enabled, the Material diffuse color will be
+ * used as the object color. If lighting is disabled, the
+ * ColoringAttributes color will be used. The default value is false.
+ *
+ * @param ignoreVertexColors true or false to enable or disable
+ * the ignoring of per-vertex colors
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see ColoringAttributes
+ * @see Material
+ *
+ * @since Java 3D 1.2
+ */
+ public void setIgnoreVertexColors(boolean ignoreVertexColors) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_IGNORE_VERTEX_COLORS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes12"));
+
+
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setIgnoreVertexColors(ignoreVertexColors);
+ else
+ ((RenderingAttributesRetained)this.retained).initIgnoreVertexColors(ignoreVertexColors);
+ }
+
+ /**
+ * Retrieves the ignoreVertexColors flag for this
+ * RenderingAttributes object.
+ * @return true if per-vertex colors are ignored; false
+ * if per-vertex colors are used.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getIgnoreVertexColors() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_IGNORE_VERTEX_COLORS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes13"));
+
+
+ return ((RenderingAttributesRetained)this.retained).getIgnoreVertexColors();
+ }
+
+ /**
+ * Sets the rasterOp enable flag for this RenderingAttributes
+ * component object. When set to true, this enables logical
+ * raster operations as specified by the setRasterOp method.
+ * Enabling raster operations effectively disables alpha blending,
+ * which is used for transparency and antialiasing. Raster
+ * operations, especially XOR mode, are primarily useful when
+ * rendering to the front buffer in immediate mode. Most
+ * applications will not wish to enable this mode.
+ *
+ * @param rasterOpEnable true or false to enable or disable
+ * raster operations
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #setRasterOp
+ *
+ * @since Java 3D 1.2
+ */
+ public void setRasterOpEnable(boolean rasterOpEnable) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_RASTER_OP_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes10"));
+
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setRasterOpEnable(rasterOpEnable);
+ else
+ ((RenderingAttributesRetained)this.retained).initRasterOpEnable(rasterOpEnable);
+ }
+
+ /**
+ * Retrieves the rasterOp enable flag for this RenderingAttributes
+ * object.
+ * @return true if raster operations are enabled; false
+ * if raster operations are disabled.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getRasterOpEnable() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_RASTER_OP_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes11"));
+
+
+ return ((RenderingAttributesRetained)this.retained).getRasterOpEnable();
+ }
+
+ /**
+ * Sets the raster operation function for this RenderingAttributes
+ * component object.
+ *
+ * @param rasterOp the logical raster operation, one of ROP_COPY or
+ * ROP_XOR
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void setRasterOp(int rasterOp) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_RASTER_OP_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes10"));
+
+ if (isLive())
+ ((RenderingAttributesRetained)this.retained).setRasterOp(rasterOp);
+ else
+ ((RenderingAttributesRetained)this.retained).initRasterOp(rasterOp);
+ }
+
+ /**
+ * Retrieves the current raster operation for this RenderingAttributes
+ * object.
+ * @return one of ROP_COPY or ROP_XOR.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getRasterOp() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_RASTER_OP_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes11"));
+
+ return ((RenderingAttributesRetained)this.retained).getRasterOp();
+ }
+
+ /**
+ * Creates a retained mode RenderingAttributesRetained object that this
+ * RenderingAttributes component object will point to.
+ */
+ void createRetained() {
+ this.retained = new RenderingAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ RenderingAttributes ra = new RenderingAttributes();
+ ra.duplicateNodeComponent(this);
+ return ra;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ RenderingAttributesRetained attr =
+ (RenderingAttributesRetained) originalNodeComponent.retained;
+ RenderingAttributesRetained rt =
+ (RenderingAttributesRetained) retained;
+
+ rt.initDepthBufferEnable(attr.getDepthBufferEnable());
+ rt.initDepthBufferWriteEnable(attr.getDepthBufferWriteEnable());
+ rt.initAlphaTestValue(attr.getAlphaTestValue());
+ rt.initAlphaTestFunction(attr.getAlphaTestFunction());
+ rt.initVisible(attr.getVisible());
+ rt.initIgnoreVertexColors(attr.getIgnoreVertexColors());
+ rt.initRasterOpEnable(attr.getRasterOpEnable());
+ rt.initRasterOp(attr.getRasterOp());
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/RenderingAttributesRetained.java b/src/classes/share/javax/media/j3d/RenderingAttributesRetained.java
new file mode 100644
index 0000000..56ea708
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderingAttributesRetained.java
@@ -0,0 +1,486 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * The RenderingAttributes object defines all rendering state that can be set
+ * as a component object of a Shape3D node.
+ */
+class RenderingAttributesRetained extends NodeComponentRetained {
+ // A list of pre-defined bits to indicate which component
+ // in this RenderingAttributes object changed.
+ static final int DEPTH_ENABLE = 0x01;
+
+ static final int DEPTH_WRITE_ENABLE = 0x02;
+
+ static final int ALPHA_TEST_VALUE = 0x04;
+
+ static final int ALPHA_TEST_FUNC = 0x08;
+
+ static final int VISIBLE = 0x10;
+
+ static final int IGNORE_VCOLOR = 0x20;
+
+ static final int RASTER_OP_ENABLE = 0x40;
+
+ static final int RASTER_OP_VALUE = 0x80;
+
+
+ // depth buffer Enable for hidden surface removal
+ boolean depthBufferEnable = true;
+
+ boolean depthBufferWriteEnable = true;
+
+ float alphaTestValue = 0.0f;
+
+ int alphaTestFunction = RenderingAttributes.ALWAYS;
+
+ boolean visible = true;
+
+ boolean ignoreVertexColors = false;
+
+ // raster operation
+ boolean rasterOpEnable = false;
+ int rasterOp = RenderingAttributes.ROP_COPY;
+
+ // depth buffer comparison function. Used by multi-texturing only
+ static final int LESS = 0;
+ static final int LEQUAL = 1;
+
+ /**
+ * Sets the visibility flag for this RenderingAttributes component object.
+ * @param visible true or false to enable or disable visibility
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see View#setVisibilityPolicy
+ *
+ * @since Java 3D 1.2
+ */
+ final void initVisible(boolean state){
+ visible = state;
+ }
+
+ /**
+ * Sets the visibility flag for this RenderingAttributes
+ * component object. Invisible objects are not rendered (subject to
+ * the visibility policy for the current view), but they can be picked
+ * or collided with.
+ * @param visible true or false to enable or disable visibility
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see View#setVisibilityPolicy
+ *
+ * @since Java 3D 1.2
+ */
+ final void setVisible(boolean state){
+ // Optimize : If new state equal to current state, should I simply return ?
+ // Is it safe ?
+ initVisible(state);
+
+ // Need to call sendMessage twice. Not an efficient approach, but
+ // it simplified code logic and speed up the common case; where
+ // perUniv is false.
+
+
+ sendMessage(VISIBLE, (state ? Boolean.TRUE: Boolean.FALSE));
+
+ }
+
+ /**
+ * Retrieves the visibility flag for this RenderingAttributes object.
+ * @return true if the object is visible; false
+ * if the object is invisible.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ final boolean getVisible() {
+ return visible;
+ }
+
+ final void initIgnoreVertexColors(boolean state) {
+ ignoreVertexColors = state;
+ }
+
+ final void setIgnoreVertexColors(boolean state) {
+ initIgnoreVertexColors(state);
+ sendMessage(IGNORE_VCOLOR,
+ (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ final boolean getIgnoreVertexColors() {
+ return ignoreVertexColors;
+ }
+
+ /**
+ * Enables or disables depth buffer mode for this RenderAttributes
+ * component object.
+ * @param state true or false to enable or disable depth buffer mode
+ */
+ final void initDepthBufferEnable(boolean state){
+ depthBufferEnable = state;
+ }
+
+ /**
+ * Enables or disables depth buffer mode for this RenderAttributes
+ * component object and sends a
+ * message notifying the interested structures of the change.
+ * @param state true or false to enable or disable depth buffer mode
+ */
+ final void setDepthBufferEnable(boolean state){
+ initDepthBufferEnable(state);
+ sendMessage(DEPTH_ENABLE, (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Retrieves the state of zBuffer Enable flag
+ * @return true if depth buffer mode is enabled, false
+ * if depth buffer mode is disabled
+ */
+ final boolean getDepthBufferEnable(){
+ return depthBufferEnable;
+ }
+
+ /**
+ * Enables or disables writing the depth buffer for this object.
+ * During the transparent rendering pass,
+ * this attribute can be overridden by
+ * the depthBufferFreezeTransparent attribute in the View object.
+ * @param state true or false to enable or disable depth buffer Write mode
+ * @see View#setDepthBufferFreezeTransparent
+ */
+ final void initDepthBufferWriteEnable(boolean state){
+ depthBufferWriteEnable = state;
+ }
+
+ /**
+ * Enables or disables writing the depth buffer for this object and sends
+ * a message notifying the interested structures of the change.
+ * During the transparent rendering pass,
+ * this attribute can be overridden by
+ * the depthBufferFreezeTransparent attribute in the View object.
+ * @param state true or false to enable or disable depth buffer Write mode
+ * @see View#setDepthBufferFreezeTransparent
+ */
+ final void setDepthBufferWriteEnable(boolean state){
+
+ initDepthBufferWriteEnable(state);
+ sendMessage(DEPTH_WRITE_ENABLE, (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Retrieves the state of Depth Buffer Write Enable flag
+ * @return true if depth buffer is writable, false
+ * if depth buffer is read-only
+ */
+ final boolean getDepthBufferWriteEnable(){
+ return depthBufferWriteEnable;
+ }
+
+ /**
+ * Set alpha test value used by alpha test function. This value is
+ * compared to the alpha value of each rendered pixel.
+ * @param value the alpha value
+ */
+ final void initAlphaTestValue(float value){
+ alphaTestValue = value;
+ }
+ /**
+ * Set alpha test value used by alpha test function and sends a
+ * message notifying the interested structures of the change.
+ * This value is compared to the alpha value of each rendered pixel.
+ * @param value the alpha value
+ */
+ final void setAlphaTestValue(float value){
+
+ initAlphaTestValue(value);
+ sendMessage(ALPHA_TEST_VALUE, new Float(value));
+ }
+
+ /**
+ * Retrieves the alpha test value.
+ * @return the alpha test value.
+ */
+ final float getAlphaTestValue(){
+ return alphaTestValue;
+ }
+
+
+ /**
+ * Set alpha test function. This function is used to compare the
+ * alpha test value with each per-pixel alpha value. If the test
+ * passes, then the pixel is written otherwise the pixel is not
+ * written.
+ * @param function the new alpha test function. One of:
+ * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
+ * GREATER_OR_EQUAL.
+ */
+ final void initAlphaTestFunction(int function){
+ alphaTestFunction = function;
+ }
+
+
+ /**
+ * Set alpha test function and sends a
+ * message notifying the interested structures of the change.
+ * This function is used to compare the
+ * alpha test value with each per-pixel alpha value. If the test
+ * passes, then the pixel is written otherwise the pixel is not
+ * written.
+ * @param function the new alpha test function. One of:
+ * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
+ * GREATER_OR_EQUAL.
+ */
+ final void setAlphaTestFunction(int function){
+
+ initAlphaTestFunction(function);
+ sendMessage(ALPHA_TEST_FUNC, new Integer(function));
+ }
+
+ /**
+ * Retrieves current alpha test function.
+ * @return the current alpha test function
+ */
+ final int getAlphaTestFunction(){
+ return alphaTestFunction;
+ }
+
+
+ /**
+ * Initialize the raster op enable flag
+ */
+ final void initRasterOpEnable(boolean flag) {
+ rasterOpEnable = flag;
+ }
+
+ /**
+ * Set the raster op enable flag
+ */
+ final void setRasterOpEnable(boolean flag) {
+ initRasterOpEnable(flag);
+ sendMessage(RASTER_OP_ENABLE, new Boolean(flag));
+ }
+
+ /**
+ * Retrieves the current raster op enable flag.
+ */
+ final boolean getRasterOpEnable() {
+ return rasterOpEnable;
+ }
+
+ /**
+ * Initialize the raster op value
+ */
+ final void initRasterOp(int op) {
+ rasterOp = op;
+ }
+
+ /**
+ * Set the raster op value
+ */
+ final void setRasterOp(int op) {
+ initRasterOp(op);
+ sendMessage(RASTER_OP_VALUE, new Integer(op));
+ }
+
+ /**
+ * Retrieves the current raster op value.
+ */
+ final int getRasterOp() {
+ return rasterOp;
+ }
+
+
+
+ /**
+ * Updates the native context.
+ */
+ native void updateNative(long ctx,
+ boolean depthBufferWriteEnableOverride,
+ boolean depthBufferEnableOverride,
+ boolean depthBufferEnable,
+ boolean depthBufferWriteEnable,
+ float alphaTestValue, int alphaTestFunction,
+ boolean ignoreVertexColors,
+ boolean rasterOpEnable, int rasterOp);
+
+ /**
+ * Updates the native context.
+ */
+ void updateNative(long ctx,
+ boolean depthBufferWriteEnableOverride,
+ boolean depthBufferEnableOverride) {
+ updateNative(ctx,
+ depthBufferWriteEnableOverride, depthBufferEnableOverride,
+ depthBufferEnable, depthBufferWriteEnable, alphaTestValue,
+ alphaTestFunction, ignoreVertexColors,
+ rasterOpEnable, rasterOp);
+ }
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ }
+ else {
+ RenderingAttributesRetained mirrorRa
+ = new RenderingAttributesRetained();
+ mirrorRa.set(this);
+ mirrorRa.source = source;
+ mirror = mirrorRa;
+ }
+ } else {
+ ((RenderingAttributesRetained) mirror).set(this);
+ }
+ }
+
+ /**
+ * Initializes a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ ((RenderingAttributesRetained)mirror).set(this);
+ }
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+ RenderingAttributesRetained mirrorRa = (RenderingAttributesRetained)mirror;
+
+ if ((component & DEPTH_ENABLE) != 0) {
+ mirrorRa.depthBufferEnable = ((Boolean)value).booleanValue();
+ }
+ else if ((component & DEPTH_WRITE_ENABLE) != 0) {
+ mirrorRa.depthBufferWriteEnable = ((Boolean)value).booleanValue();
+ }
+ else if ((component & ALPHA_TEST_VALUE) != 0) {
+ mirrorRa.alphaTestValue = ((Float)value).floatValue();
+ }
+ else if ((component & ALPHA_TEST_FUNC) != 0) {
+ mirrorRa.alphaTestFunction = ((Integer)value).intValue();
+ }
+ else if ((component & VISIBLE) != 0) {
+ mirrorRa.visible = (((Boolean)value).booleanValue());
+ }
+ else if ((component & IGNORE_VCOLOR) != 0) {
+ mirrorRa.ignoreVertexColors = (((Boolean)value).booleanValue());
+ }
+ else if ((component & RASTER_OP_ENABLE) != 0) {
+ mirrorRa.rasterOpEnable = (((Boolean)value).booleanValue());
+ }
+ else if ((component & RASTER_OP_VALUE) != 0) {
+ mirrorRa.rasterOp = (((Integer)value).intValue());
+ }
+ }
+
+ boolean equivalent(RenderingAttributesRetained rr) {
+ return (this == rr) ||
+ ((rr != null) &&
+ (rr.depthBufferEnable == depthBufferEnable) &&
+ (rr.depthBufferWriteEnable == depthBufferWriteEnable) &&
+ (rr.alphaTestValue == alphaTestValue) &&
+ (rr.alphaTestFunction == alphaTestFunction) &&
+ (rr.visible == visible) &&
+ (rr.ignoreVertexColors == ignoreVertexColors) &&
+ (rr.rasterOpEnable == rasterOpEnable) &&
+ (rr.rasterOp == rasterOp));
+ }
+
+ protected void set(RenderingAttributesRetained ra) {
+ super.set(ra);
+ depthBufferEnable = ra.depthBufferEnable;
+ depthBufferWriteEnable = ra.depthBufferWriteEnable;
+ alphaTestValue = ra.alphaTestValue;
+ alphaTestFunction = ra.alphaTestFunction;
+ visible = ra.visible;
+ ignoreVertexColors = ra.ignoreVertexColors;
+ rasterOpEnable = ra.rasterOpEnable;
+ rasterOp = ra.rasterOp;
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.RENDERINGATTRIBUTES_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ // System.out.println("changedFreqent1 = "+changedFrequent);
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ if (attrMask == VISIBLE)
+ createMessage.threads |= J3dThread.UPDATE_GEOMETRY;
+ createMessage.type = J3dMessage.RENDERINGATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList)gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ }
+
+
+ void handleFrequencyChange(int bit) {
+ int mask = 0;
+
+ if (bit == RenderingAttributes.ALLOW_ALPHA_TEST_VALUE_WRITE)
+ mask = ALPHA_TEST_VALUE;
+ if( bit == RenderingAttributes.ALLOW_ALPHA_TEST_FUNCTION_WRITE)
+ mask = ALPHA_TEST_FUNC;
+ if(bit == RenderingAttributes.ALLOW_VISIBLE_WRITE)
+ mask = VISIBLE;
+ if (bit == RenderingAttributes.ALLOW_IGNORE_VERTEX_COLORS_WRITE)
+ mask = IGNORE_VCOLOR;
+ if(bit == RenderingAttributes.ALLOW_RASTER_OP_WRITE)
+ mask = RASTER_OP_ENABLE;
+ if(bit == RenderingAttributes.ALLOW_DEPTH_ENABLE_WRITE)
+ mask = DEPTH_WRITE_ENABLE;
+ if (mask != 0)
+ setFrequencyChangeMask(bit, mask);
+ // System.out.println("changedFreqent2 = "+changedFrequent);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/RenderingAttributesStructure.java b/src/classes/share/javax/media/j3d/RenderingAttributesStructure.java
new file mode 100644
index 0000000..cd959b6
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderingAttributesStructure.java
@@ -0,0 +1,226 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A rendering attributes structure is an object that handles
+ * NodeComponent object updates.
+ */
+
+class RenderingAttributesStructure extends J3dStructure implements ObjectUpdate {
+ // List of textures whose resourceCreation mask should be updated
+ ArrayList objList = new ArrayList();
+
+ RenderingAttributesStructure() {
+ super(null, J3dThread.UPDATE_RENDERING_ATTRIBUTES);
+ }
+
+ void processMessages(long referenceTime) {
+ J3dMessage m;
+ boolean addMirrorObj = false;
+
+ J3dMessage messages[] = getMessages(referenceTime);
+ int nMsg = getNumMessage();
+
+ if (nMsg <= 0) {
+ return;
+ }
+
+ for (int i=0; i < nMsg; i++) {
+ m = messages[i];
+ switch (m.type) {
+ // Apperance is always updated immediately, since rBin needs
+ // the most up-to-date values for restructuring
+ case J3dMessage.APPEARANCE_CHANGED:
+ case J3dMessage.TEXTURE_UNIT_STATE_CHANGED: // TODO: Is this correct?
+ {
+ int component = ((Integer)m.args[1]).intValue();
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ nc.mirror.changedFrequent = ((Integer)m.args[3]).intValue();
+ updateNodeComponent((Object[])m.args);
+ if (nc.mirror.changedFrequent != 0) {
+ objList.add(m);
+ addMirrorObj = true;
+ nc.mirror.compChanged |= component;
+ }
+ else {
+ m.decRefcount();
+ }
+ }
+ break;
+ case J3dMessage.COLORINGATTRIBUTES_CHANGED:
+ case J3dMessage.LINEATTRIBUTES_CHANGED:
+ case J3dMessage.POINTATTRIBUTES_CHANGED:
+ case J3dMessage.POLYGONATTRIBUTES_CHANGED:
+ case J3dMessage.RENDERINGATTRIBUTES_CHANGED:
+ case J3dMessage.TRANSPARENCYATTRIBUTES_CHANGED:
+ case J3dMessage.MATERIAL_CHANGED:
+ case J3dMessage.TEXCOORDGENERATION_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ nc.mirror.changedFrequent = ((Integer)m.args[3]).intValue();
+ if (nc.mirror.changedFrequent != 0) {
+ objList.add(m);
+ addMirrorObj = true;
+ nc.mirror.compChanged = 1;
+ }
+ else {
+ updateNodeComponent((Object[])m.args);
+ m.decRefcount();
+ }
+ }
+ break;
+ case J3dMessage.IMAGE_COMPONENT_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ int changes = ((Integer)m.args[3]).intValue();
+ if(nc.mirror != null)
+ nc.mirror.changedFrequent = changes;
+ if (changes != 0) {
+ objList.add(m);
+ addMirrorObj = true;
+ }
+ else {
+ updateNodeComponent((Object[])m.args);
+ m.decRefcount();
+ }
+ }
+ break;
+ case J3dMessage.TEXTUREATTRIBUTES_CHANGED:
+ {
+
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ nc.mirror.changedFrequent = ((Integer)m.args[4]).intValue();
+
+
+
+ if (nc.mirror.changedFrequent != 0) {
+ objList.add(m);
+ addMirrorObj = true;
+ nc.mirror.compChanged = 1;
+ }
+ else {
+ updateTextureAttributes((Object[])m.args);
+ m.decRefcount();
+ }
+ }
+ break;
+
+ case J3dMessage.TEXTURE_CHANGED:
+ {
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ int attrMask = ((Integer)m.args[1]).intValue();
+ nc.mirror.changedFrequent = ((Integer)m.args[3]).intValue();
+
+ objList.add(m);
+ nc.mirror.compChanged = 1;
+ addMirrorObj = true;
+ }
+ break;
+ case J3dMessage.GEOMETRY_CHANGED:
+ GeometryRetained geo = (GeometryRetained) m.args[1];
+ int val;
+ if (geo instanceof IndexedGeometryArrayRetained) {
+ objList.add(m);
+ addMirrorObj = true;
+ if (m.args[2] == null) {
+ val = ((Integer)m.args[3]).intValue();
+ geo.cachedChangedFrequent = val;
+ }
+ }
+ else {
+ val = ((Integer)m.args[3]).intValue();
+ geo.cachedChangedFrequent = val;
+ m.decRefcount();
+ }
+ break;
+ case J3dMessage.MORPH_CHANGED:
+ objList.add(m);
+ addMirrorObj = true;
+ break;
+ default:
+ m.decRefcount();
+ }
+ }
+ if (addMirrorObj) {
+ VirtualUniverse.mc.addMirrorObject(this);
+ }
+
+ // clear array to prevent memory leaks
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ public void updateObject() {
+
+ int size = objList.size();
+ for (int i = 0; i < size; i++) {
+ J3dMessage m = (J3dMessage)objList.get(i);
+ // Message Only sent to RenderingAttributesStructure
+ // when the geometry type is indexed
+ if (m.type == J3dMessage.GEOMETRY_CHANGED) {
+ GeometryArrayRetained geo = (GeometryArrayRetained)m.args[1];
+ if (m.args[2] == null) {
+ geo.updateMirrorGeometry();
+ }
+ else {
+ geo.initMirrorGeometry();
+ }
+ }
+ else if (m.type == J3dMessage.MORPH_CHANGED) {
+ MorphRetained morph = (MorphRetained) m.args[0];
+ GeometryArrayRetained geo = (GeometryArrayRetained)morph.morphedGeometryArray.retained;
+ geo.updateMirrorGeometry();
+ }
+ else if (m.type == J3dMessage.TEXTUREATTRIBUTES_CHANGED) {
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ nc.mirror.compChanged = 0;
+ updateTextureAttributes((Object[])m.args);
+ }
+ else if (m.type == J3dMessage.APPEARANCE_CHANGED ||
+ m.type == J3dMessage.TEXTURE_UNIT_STATE_CHANGED){
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ nc.mirror.compChanged = 0;
+ }
+ else {
+ NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
+ if (nc.mirror != null)
+ nc.mirror.compChanged = 0;
+ updateNodeComponent(m.args);
+ }
+ m.decRefcount();
+ }
+ objList.clear();
+ }
+
+
+ void updateNodeComponent(Object[] args) {
+ NodeComponentRetained n = (NodeComponentRetained)args[0];
+ n.updateMirrorObject(((Integer)args[1]).intValue(), args[2]);
+ }
+
+ void updateTextureAttributes(Object[] args) {
+ TextureAttributesRetained n = (TextureAttributesRetained)args[0];
+ n.updateMirrorObject(((Integer)args[1]).intValue(), args[2], args[3]);
+ }
+
+ void removeNodes(J3dMessage m) {}
+
+ void cleanup() {}
+
+
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/RenderingEnvironmentStructure.java b/src/classes/share/javax/media/j3d/RenderingEnvironmentStructure.java
new file mode 100644
index 0000000..f63e705
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RenderingEnvironmentStructure.java
@@ -0,0 +1,1754 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import javax.vecmath.*;
+
+/**
+ * A rendering environment structure is an object that organizes lights,
+ * fogs, backgrounds, clips, and model clips.
+ */
+
+class RenderingEnvironmentStructure extends J3dStructure implements ObjectUpdate {
+ /**
+ * The list of light nodes
+ */
+ ArrayList nonViewScopedLights = new ArrayList();
+ HashMap viewScopedLights = new HashMap();
+ int numberOfLights = 0;
+
+ /**
+ * The list of fog nodes
+ */
+ ArrayList nonViewScopedFogs = new ArrayList();
+ HashMap viewScopedFogs = new HashMap();
+ int numberOfFogs = 0;
+
+ /**
+ * The list of alternate app nodes
+ */
+ ArrayList nonViewScopedAltAppearances = new ArrayList();
+ HashMap viewScopedAltAppearances = new HashMap();
+ int numberOfAltApps = 0;
+
+ /**
+ * The list of model clip nodes
+ */
+ ArrayList nonViewScopedModelClips = new ArrayList();
+ HashMap viewScopedModelClips = new HashMap();
+ int numberOfModelClips = 0;
+
+ /**
+ * The list of background nodes
+ */
+ ArrayList nonViewScopedBackgrounds = new ArrayList();
+ HashMap viewScopedBackgrounds = new HashMap();
+ int numberOfBgs = 0;
+
+ /**
+ * The list of clip nodes
+ */
+ ArrayList nonViewScopedClips = new ArrayList();
+ HashMap viewScopedClips = new HashMap();
+ int numberOfClips = 0;
+
+ // For closest Background selection
+ BackgroundRetained[] intersectedBacks = new BackgroundRetained[1];
+
+
+ // For closest Clip selection
+ ClipRetained[] intersectedClips = new ClipRetained[1];
+
+ // For closest Background, Clip, Fog selection
+ Bounds[] intersectedBounds = new Bounds[1];
+
+ Transform3D localeXform = new Transform3D();
+ Vector3d localeTranslation = new Vector3d();
+ Bounds localeBounds = null;
+
+ // For closest Fog selection
+ FogRetained[] intersectedFogs = new FogRetained[1];
+
+ // for closest alternate appearance selection
+ AlternateAppearanceRetained[] intersectedAltApps = new AlternateAppearanceRetained[1];
+
+ // For closest ModelClip selection
+ ModelClipRetained[] intersectedModelClips = new ModelClipRetained[1];
+
+
+ // Back clip distance in V world
+ double backClipDistance;
+
+ // ArrayList of leafRetained object whose mirrorObjects
+ // should be updated
+ ArrayList objList = new ArrayList();
+
+ // ArrayList of leafRetained object whose boundingleaf xform
+ // should be updated
+ ArrayList xformChangeList = new ArrayList();
+
+
+ // freelist management of objects
+ ArrayList objFreeList = new ArrayList();
+
+ LightRetained[] retlights = new LightRetained[5];
+
+ // variables used for processing transform messages
+ boolean transformMsg = false;
+ UpdateTargets targets = null;
+ ArrayList blUsers = null;
+
+ Integer ogInsert = new Integer(J3dMessage.ORDERED_GROUP_INSERTED);
+ Integer ogRemove = new Integer(J3dMessage.ORDERED_GROUP_REMOVED);
+
+ // Used to lock the intersectedBounds {used by fog, mclip etc}
+ // Can used intersectedBounds itself, since this may be realloced
+ Object lockObj = new Object();
+
+ /**
+ * Constructs a RenderingEnvironmentStructure object in the specified
+ * virtual universe.
+ */
+ RenderingEnvironmentStructure(VirtualUniverse u) {
+ super(u, J3dThread.UPDATE_RENDERING_ENVIRONMENT);
+ }
+
+
+ /**
+ * Returns a object array of length 5 to save the 5 objects in the message list.
+ */
+ Object[] getObjectArray() {
+ Object[] objs;
+ int size;
+
+ size = objFreeList.size();
+ if (size == 0) {
+ objs = new Object[5];
+ }
+ else {
+ objs = (Object[]) objFreeList.get(size - 1);
+ objFreeList.remove(size -1);
+ }
+ return objs;
+ }
+
+ void addObjArrayToFreeList(Object[] objs) {
+ int i;
+
+ for (i = 0; i < objs.length; i++)
+ objs[i] = null;
+
+ objFreeList.add(objs);
+ }
+
+
+
+ public void updateObject() {
+ int i, j;
+ Object[] args;
+ LeafRetained leaf;
+ Boolean masks;
+ int size;
+
+ size = objList.size();
+ for (i = 0; i < size; i++) {
+ args = (Object[])objList.get(i);
+ leaf = (LeafRetained)args[0];
+ leaf.updateMirrorObject(args);
+ addObjArrayToFreeList(args);
+ }
+ objList.clear();
+
+ size = xformChangeList.size();
+ for (i = 0; i < size; i++) {
+ leaf = (LeafRetained)xformChangeList.get(i);
+ leaf.updateTransformChange();
+ }
+ xformChangeList.clear();
+
+ }
+
+ void processMessages(long referenceTime) {
+ J3dMessage[] messages = getMessages(referenceTime);;
+ J3dMessage m;
+ int nMsg = getNumMessage();
+
+ if (nMsg <= 0) {
+ return;
+ }
+
+ for (int i=0; i < nMsg; i++) {
+ m = messages[i];
+
+ switch (m.type) {
+ case J3dMessage.INSERT_NODES:
+ insertNodes(m);
+ break;
+ case J3dMessage.REMOVE_NODES:
+ removeNodes(m);
+ break;
+ case J3dMessage.LIGHT_CHANGED:
+ updateLight((Object[])m.args);
+ break;
+ case J3dMessage.BOUNDINGLEAF_CHANGED:
+ updateBoundingLeaf((Object[])m.args);
+ break;
+ case J3dMessage.FOG_CHANGED:
+ updateFog((Object[])m.args);
+ break;
+ case J3dMessage.ALTERNATEAPPEARANCE_CHANGED:
+ updateAltApp((Object[])m.args);
+ break;
+ case J3dMessage.SHAPE3D_CHANGED:
+ updateShape3D((Object[])m.args);
+ break;
+ case J3dMessage.ORIENTEDSHAPE3D_CHANGED:
+ updateOrientedShape3D((Object[])m.args);
+ break;
+ case J3dMessage.MORPH_CHANGED:
+ updateMorph((Object[])m.args);
+ break;
+ case J3dMessage.TRANSFORM_CHANGED:
+ transformMsg = true;
+ break;
+ case J3dMessage.SWITCH_CHANGED:
+ processSwitchChanged(m);
+ // may need to process dirty switched-on transform
+ if (universe.transformStructure.getLazyUpdate()) {
+ transformMsg = true;
+ }
+ break;
+ case J3dMessage.MODELCLIP_CHANGED:
+ updateModelClip((Object[])m.args);
+ break;
+ case J3dMessage.BACKGROUND_CHANGED:
+ updateBackground((Object[])m.args);
+ break;
+ case J3dMessage.CLIP_CHANGED:
+ updateClip((Object[])m.args);
+ break;
+ case J3dMessage.ORDERED_GROUP_INSERTED:
+ updateOrderedGroupInserted(m);
+ break;
+ case J3dMessage.ORDERED_GROUP_REMOVED:
+ updateOrderedGroupsRemoved(m);
+ break;
+ case J3dMessage.VIEWSPECIFICGROUP_CHANGED:
+ updateViewSpecificGroupChanged(m);
+ break;
+ case J3dMessage.VIEWSPECIFICGROUP_INIT:
+ initViewSpecificInfo(m);
+ break;
+ case J3dMessage.VIEWSPECIFICGROUP_CLEAR:
+ clearViewSpecificInfo(m);
+ break;
+ }
+ m.decRefcount();
+ }
+
+ if (transformMsg) {
+ updateTransformChange();
+ transformMsg = false;
+ }
+
+ VirtualUniverse.mc.addMirrorObject(this);
+
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ void updateOrderedGroupInserted(J3dMessage m) {
+ Object[] ogList = (Object[])m.args[0];
+ Object[] ogChildIdList = (Object[])m.args[1];
+ Object[] ogOrderedIdList = (Object[])m.args[2];
+ OrderedGroupRetained og;
+ int index;
+ Object[] objs;
+
+ for (int n = 0; n < ogList.length; n++) {
+ og = (OrderedGroupRetained)ogList[n];
+ og.updateChildIdTableInserted(((Integer) ogChildIdList[n]).intValue(),
+ ((Integer) ogOrderedIdList[n]).intValue());
+ og.incrChildCount();
+ }
+ }
+
+ void updateOrderedGroupsRemoved(J3dMessage m) {
+ Object[] ogList = (Object[])m.args[0];
+ Object[] ogChildIdList = (Object[])m.args[1];
+ OrderedGroupRetained og;
+ int index;
+ Object[] objs;
+
+ for (int n = 0; n < ogList.length; n++) {
+ og = (OrderedGroupRetained)ogList[n];
+ og.updateChildIdTableRemoved(((Integer) ogChildIdList[n]).intValue());
+ og.decrChildCount();
+ }
+
+ }
+ /**
+ * This processes a switch change.
+ */
+ void processSwitchChanged(J3dMessage m) {
+ int i;
+ UnorderList arrList;
+ int size;
+ Object[] nodes, nodesArr;
+ LeafRetained leaf;
+
+ UpdateTargets targets = (UpdateTargets)m.args[0];
+
+ arrList = targets.targetList[Targets.BLN_TARGETS];
+ if (arrList != null) {
+ BoundingLeafRetained mbleaf;
+ Object[] objArr = (Object[])m.args[1];
+ Object[] obj;
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (int h=0; h<size; h++) {
+ nodes = (Object[])nodesArr[h];
+ obj = (Object[])objArr[h];
+
+ for (i=0; i<nodes.length; i++) {
+
+ Object[] users = (Object[])obj[i];
+ mbleaf = (BoundingLeafRetained)nodes[i];
+
+ //mbleaf.switchState.updateCurrentSwitchOn();
+ for (int j = 0; j < users.length; j++) {
+ leaf = (LeafRetained)users[j];
+ if (leaf instanceof FogRetained ||
+ leaf instanceof LightRetained ||
+ leaf instanceof ModelClipRetained ||
+ leaf instanceof ClipRetained ||
+ leaf instanceof AlternateAppearanceRetained ||
+ leaf instanceof BackgroundRetained) {
+ leaf.updateBoundingLeaf();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void insertNodes(J3dMessage m) {
+ Object[] nodes = (Object[])m.args[0];
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ Object n;
+ int i;
+ GeometryAtom ga;
+ int num;
+ LightRetained lt;
+ FogRetained fg;
+ ModelClipRetained mc;
+ ArrayList list;
+
+ for (i=0; i<nodes.length; i++) {
+ n = nodes[i];
+ if (n instanceof LightRetained) {
+ lt = (LightRetained)n;
+ numberOfLights++;
+
+ // If this particulat light is not scoped, added it
+ // to all the views
+ if (lt.inBackgroundGroup)
+ lt.geometryBackground.lights.add(lt);
+ else
+ nonViewScopedLights.add(lt);
+
+ } else if (n instanceof FogRetained) {
+ fg = (FogRetained)n;
+ numberOfFogs++;
+ // If the fog is scoped to a view , then ..
+
+ if (fg.inBackgroundGroup) {
+ fg.geometryBackground.fogs.add(fg);
+ } else {
+ nonViewScopedFogs.add(fg);
+ }
+
+
+ } else if (n instanceof AlternateAppearanceRetained) {
+ AlternateAppearanceRetained altApp = (AlternateAppearanceRetained)n;
+
+ numberOfAltApps++;
+
+ nonViewScopedAltAppearances.add(n);
+
+ } else if (n instanceof BackgroundRetained) {
+ BackgroundRetained bg = (BackgroundRetained)n;
+ numberOfBgs++;
+
+ nonViewScopedBackgrounds.add(n);
+
+ } else if (n instanceof ClipRetained) {
+ ClipRetained cl = (ClipRetained)n;
+ numberOfClips++;
+
+ nonViewScopedClips.add(n);
+
+ } else if (n instanceof ModelClipRetained) {
+ mc = (ModelClipRetained)n;
+ numberOfModelClips++;
+
+ nonViewScopedModelClips.add(n);
+
+ }
+
+ }
+
+
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+
+
+ for (i = 0; i < size; i++) {
+ n = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ if (n instanceof LightRetained) {
+ ((LightRetained)n).isViewScoped = true;
+ numberOfLights++;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ if ((list = (ArrayList)viewScopedLights.get(view)) == null) {
+ list = new ArrayList();
+ viewScopedLights.put(view, list);
+ }
+ list.add(n);
+ }
+ } else if (n instanceof FogRetained) {
+ ((FogRetained)n).isViewScoped = true;
+ numberOfFogs++;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ if ((list = (ArrayList)viewScopedFogs.get(view)) == null) {
+ list = new ArrayList();
+ viewScopedFogs.put(view, list);
+ }
+ list.add(n);
+ }
+ } else if (n instanceof AlternateAppearanceRetained) {
+ ((AlternateAppearanceRetained)n).isViewScoped = true;
+ numberOfAltApps++;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ if ((list = (ArrayList)viewScopedAltAppearances.get(view)) == null) {
+ list = new ArrayList();
+ viewScopedAltAppearances.put(view, list);
+ }
+ list.add(n);
+ }
+ } else if (n instanceof BackgroundRetained) {
+ ((BackgroundRetained)n).isViewScoped = true;
+ numberOfBgs++;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ if ((list = (ArrayList)viewScopedBackgrounds.get(view)) == null) {
+ list = new ArrayList();
+ viewScopedBackgrounds.put(view, list);
+ }
+ list.add(n);
+ }
+ } else if (n instanceof ClipRetained) {
+ ((ClipRetained)n).isViewScoped = true;
+ numberOfClips++;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ if ((list = (ArrayList)viewScopedClips.get(view)) == null) {
+ list = new ArrayList();
+ viewScopedClips.put(view, list);
+ }
+ list.add(n);
+ }
+ } else if (n instanceof ModelClipRetained) {
+ ((ModelClipRetained)n).isViewScoped = true;
+ numberOfModelClips++;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ if ((list = (ArrayList)viewScopedModelClips.get(view)) == null) {
+ list = new ArrayList();
+ viewScopedModelClips.put(view, list);
+ }
+ list.add(n);
+ }
+ }
+ }
+
+ }
+ if (numberOfLights > retlights.length)
+ retlights = new LightRetained[numberOfLights];
+ if (intersectedFogs.length < numberOfFogs)
+ intersectedFogs = new FogRetained[numberOfFogs];
+ if (intersectedAltApps.length < numberOfAltApps)
+ intersectedAltApps = new AlternateAppearanceRetained[numberOfAltApps];
+ if (intersectedBacks.length < numberOfBgs)
+ intersectedBacks = new BackgroundRetained[numberOfBgs];
+ if (intersectedClips.length < numberOfClips)
+ intersectedClips = new ClipRetained[numberOfClips];
+ if (intersectedModelClips.length < numberOfModelClips)
+ intersectedModelClips = new ModelClipRetained[numberOfModelClips];
+ }
+
+ void removeNodes(J3dMessage m) {
+ Object[] nodes = (Object[])m.args[0];
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ Object n;
+ int i;
+ GeometryAtom ga;
+ LeafRetained oldsrc = null;
+
+ // System.out.println("RE : removeNodes message " + m);
+ // System.out.println("RE : removeNodes m.args[0] " + m.args[0]);
+
+ for (i=0; i<nodes.length; i++) {
+ n = nodes[i];
+ if (n instanceof LightRetained) {
+ LightRetained lt = (LightRetained)n;
+ if (lt.inBackgroundGroup) {
+ lt.geometryBackground.lights.remove(lt);
+ }
+ else {
+ nonViewScopedLights.remove(nonViewScopedLights.indexOf(n));
+ }
+
+ numberOfLights--;
+ } else if (n instanceof FogRetained) {
+ numberOfFogs--;
+ FogRetained fg = (FogRetained)n;
+ if (fg.inBackgroundGroup) {
+ fg.geometryBackground.fogs.remove(fg);
+ } else {
+ nonViewScopedFogs.remove(nonViewScopedFogs.indexOf(n));
+ }
+ } else if (n instanceof AlternateAppearanceRetained) {
+ numberOfAltApps--;
+ nonViewScopedAltAppearances.remove(nonViewScopedAltAppearances.indexOf(n));
+ }else if (n instanceof BackgroundRetained) {
+ numberOfBgs--;
+ nonViewScopedBackgrounds.remove(nonViewScopedBackgrounds.indexOf(n));
+ } else if (n instanceof ClipRetained) {
+ numberOfClips--;
+ nonViewScopedClips.remove(nonViewScopedClips.indexOf(n));
+ } else if (n instanceof ModelClipRetained) {
+ ModelClipRetained mc = (ModelClipRetained)n;
+ numberOfModelClips--;
+ nonViewScopedModelClips.remove(nonViewScopedModelClips.indexOf(n));
+
+ }
+ else if (n instanceof GeometryAtom) {
+ ga = (GeometryAtom)n;
+ // Check that we have not already cleared the mirrorobject
+ // since mant geometry atoms could be generated for one
+ // mirror shape
+ if (ga.source != oldsrc) {
+ ga.source.clearMirrorShape();
+ oldsrc = ga.source;
+ }
+ }
+ else if (n instanceof OrderedGroupRetained) {
+ // Clear the orderedBins for this orderedGroup
+ ((OrderedGroupRetained)n).clearDerivedDataStructures();
+ }
+ }
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+ ArrayList list;
+ for (i = 0; i < size; i++) {
+ n = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ if (n instanceof LightRetained) {
+ ((LightRetained)n).isViewScoped = false;
+ numberOfLights--;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ list = (ArrayList)viewScopedLights.get(view);
+ list.remove(n);
+ if (list.size() == 0) {
+ viewScopedLights.remove(view);
+ }
+ }
+ } else if (n instanceof FogRetained) {
+ ((FogRetained)n).isViewScoped = false;
+ numberOfFogs--;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ list = (ArrayList)viewScopedFogs.get(view);
+ list.remove(n);
+ if (list.size() == 0) {
+ viewScopedFogs.remove(view);
+ }
+ }
+ } else if (n instanceof AlternateAppearanceRetained) {
+ ((AlternateAppearanceRetained)n).isViewScoped = false;
+ numberOfAltApps--;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ list = (ArrayList)viewScopedAltAppearances.get(view);
+ list.remove(n);
+ if (list.size() == 0) {
+ viewScopedAltAppearances.remove(view);
+ }
+ }
+ } else if (n instanceof BackgroundRetained) {
+ ((BackgroundRetained)n).isViewScoped = false;
+ numberOfBgs--;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ list = (ArrayList)viewScopedBackgrounds.get(view);
+ list.remove(n);
+ if (list.size() == 0) {
+ viewScopedBackgrounds.remove(view);
+ }
+ }
+ } else if (n instanceof ClipRetained) {
+ ((ClipRetained)n).isViewScoped = false;
+ numberOfClips--;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ list = (ArrayList)viewScopedClips.get(view);
+ list.remove(n);
+ if (list.size() == 0) {
+ viewScopedClips.remove(view);
+ }
+ }
+ } else if (n instanceof ModelClipRetained) {
+ ((ModelClipRetained)n).isViewScoped = false;
+ numberOfModelClips--;
+ vlsize = vl.size();
+ for (int k = 0; k < vlsize; k++) {
+ View view = (View)vl.get(k);
+ list = (ArrayList)viewScopedModelClips.get(view);
+ list.remove(n);
+ if (list.size() == 0) {
+ viewScopedModelClips.remove(view);
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ LightRetained[] getInfluencingLights(RenderAtom ra, View view) {
+ LightRetained[] lightAry = null;
+ ArrayList globalLights;
+ int numLights;
+ int i, j, n;
+
+ // Need to lock retlights, since on a multi-processor
+ // system with 2 views on a single universe, there might
+ // be councurrent access
+ synchronized (retlights) {
+ numLights = 0;
+ if (ra.geometryAtom.source.inBackgroundGroup) {
+ globalLights = ra.geometryAtom.source.geometryBackground.lights;
+ numLights = processLights(globalLights, ra, numLights);
+ } else {
+ if ((globalLights = (ArrayList)viewScopedLights.get(view)) != null) {
+ numLights = processLights(globalLights, ra, numLights);
+ }
+ // now process the common lights
+ numLights = processLights(nonViewScopedLights, ra, numLights);
+ }
+
+ boolean newLights = false;
+ if (ra.lights != null && ra.lights.length == numLights) {
+ for (i=0; i<ra.lights.length; i++) {
+ for (j=0; j<numLights; j++) {
+ if (ra.lights[i] == retlights[j]) {
+ break;
+ }
+ }
+ if (j==numLights) {
+ newLights = true;
+ break;
+ }
+ }
+ } else {
+ newLights = true;
+ }
+ if (newLights) {
+ lightAry = new LightRetained[numLights];
+ for (i = 0; i < numLights; i++) {
+ lightAry[i] = (LightRetained)retlights[i];
+ }
+ return (lightAry);
+ } else {
+ return(ra.lights);
+ }
+ }
+ }
+
+ // Called while holding the retlights lock
+ int processLights(ArrayList globalLights, RenderAtom ra, int numLights) {
+ LightRetained[] shapeScopedLt;
+ Bounds bounds;
+ int i, j, n;
+ bounds = ra.localeVwcBounds;
+ int size = globalLights.size();
+
+ if (size > 0) {
+ for (i=0; i<size; i++) {
+ LightRetained light = (LightRetained)globalLights.get(i);
+ // System.out.println("vwcBounds = "+bounds);
+ // System.out.println("light.region = "+light.region);
+ // System.out.println("Intersected = "+bounds.intersect(light.region));
+ // System.out.println("");
+
+ // if ((light.viewList != null && light.viewList.contains(view)) &&
+ // Treat lights in background geo as having infinite bounds
+ if (light.lightOn && light.switchState.currentSwitchOn &&
+ (ra.geometryAtom.source.inBackgroundGroup || bounds.intersect(light.region))){
+ // Get the mirror Shape3D node
+ n = ((Shape3DRetained)ra.geometryAtom.source).numlights;
+ shapeScopedLt = ((Shape3DRetained)ra.geometryAtom.source).lights;
+
+ // System.out.println("numLights per shape= "+n);
+ // scoped Fog/light is kept in the original
+ // shape3D node, what happens if this list changes
+ // while accessing them?. So, Lock.
+ if (light.isScoped) {
+ for (j = 0; j < n; j++) {
+ // then check if the light is scoped to
+ // this group
+ if (light == shapeScopedLt[j]) {
+ retlights[numLights++] = light;
+ break;
+ }
+ }
+ }
+ else {
+ retlights[numLights++] = light;
+ }
+ }
+ }
+ }
+ return numLights;
+
+ }
+ FogRetained getInfluencingFog(RenderAtom ra, View view) {
+ FogRetained fog = null;
+ int i, j, k, n, nfogs;
+ Bounds closestBounds;
+ ArrayList globalFogs;
+ int numFogs;
+
+ // Need to lock lockObj, since on a multi-processor
+ // system with 2 views on a single universe, there might
+ // be councurrent access
+ synchronized(lockObj) {
+ nfogs = 0;
+ Bounds bounds = ra.localeVwcBounds;
+
+ if (intersectedBounds.length < numberOfFogs)
+ intersectedBounds = new Bounds[numberOfFogs];
+
+ if (ra.geometryAtom.source.inBackgroundGroup) {
+ globalFogs = ra.geometryAtom.source.geometryBackground.fogs;
+ nfogs = processFogs(globalFogs, ra, nfogs);
+ // If background, then nfogs > 1, take the first one
+ if (nfogs >= 1)
+ fog = intersectedFogs[0];
+
+ } else {
+ if ((globalFogs = (ArrayList)viewScopedFogs.get(view)) != null) {
+ nfogs = processFogs(globalFogs, ra, nfogs);
+ }
+ // now process the common fogs
+ nfogs = processFogs(nonViewScopedFogs, ra, nfogs);
+
+
+ if (nfogs == 1)
+ fog = intersectedFogs[0];
+
+ else if (nfogs > 1) {
+ closestBounds = bounds.closestIntersection(intersectedBounds);
+ for (j= 0; j < nfogs; j++) {
+ if (intersectedBounds[j] == closestBounds) {
+ fog = intersectedFogs[j];
+ break;
+ }
+ }
+ }
+ }
+ return (fog);
+ }
+ }
+
+ // Called while holding lockObj lock
+ int processFogs(ArrayList globalFogs, RenderAtom ra, int numFogs) {
+ int size = globalFogs.size();
+ FogRetained fog;
+ int i, k, n;
+ Bounds bounds = ra.localeVwcBounds;
+ FogRetained[] shapeScopedFog;
+
+ if (globalFogs.size() > 0) {
+ for (i = 0 ; i < size; i++) {
+ fog = (FogRetained) globalFogs.get(i);
+ // Note : There is no enable check for fog
+ if (fog.region != null && fog.switchState.currentSwitchOn &&
+ (ra.geometryAtom.source.inBackgroundGroup || fog.region.intersect(bounds))) {
+ n = ((Shape3DRetained)ra.geometryAtom.source).numfogs;
+ shapeScopedFog = ((Shape3DRetained)ra.geometryAtom.source).fogs;
+
+ if (fog.isScoped) {
+ for (k = 0; k < n; k++) {
+ // then check if the Fog is scoped to
+ // this group
+ if (fog == shapeScopedFog[k]) {
+ intersectedBounds[numFogs] = fog.region;
+ intersectedFogs[numFogs++] = fog;
+ break;
+ }
+ }
+ }
+ else {
+ intersectedBounds[numFogs] = fog.region;
+ intersectedFogs[numFogs++] = fog;
+ }
+ }
+ }
+ }
+ return numFogs;
+ }
+
+ ModelClipRetained getInfluencingModelClip(RenderAtom ra, View view) {
+ ModelClipRetained modelClip = null;
+ int i, j, k, n, nModelClips;
+ Bounds closestBounds;
+ ArrayList globalModelClips;
+
+
+
+ if (ra.geometryAtom.source.inBackgroundGroup) {
+ return null;
+ }
+ // Need to lock lockObj, since on a multi-processor
+ // system with 2 views on a single universe, there might
+ // be councurrent access
+ synchronized(lockObj) {
+ Bounds bounds = ra.localeVwcBounds;
+ nModelClips = 0;
+ if (intersectedBounds.length < numberOfModelClips)
+ intersectedBounds = new Bounds[numberOfModelClips];
+
+ if ((globalModelClips = (ArrayList)viewScopedModelClips.get(view)) != null) {
+ nModelClips = processModelClips(globalModelClips, ra, nModelClips);
+ }
+
+ // now process the common clips
+ nModelClips = processModelClips(nonViewScopedModelClips, ra, nModelClips);
+
+
+
+
+
+ modelClip = null;
+ if (nModelClips == 1)
+ modelClip = intersectedModelClips[0];
+ else if (nModelClips > 1) {
+ closestBounds = bounds.closestIntersection(intersectedBounds);
+ for (j= 0; j < nModelClips; j++) {
+ if (intersectedBounds[j] == closestBounds) {
+ modelClip = intersectedModelClips[j];
+ break;
+ }
+ }
+ }
+ return (modelClip);
+ }
+
+ }
+
+ int processModelClips(ArrayList globalModelClips, RenderAtom ra, int nModelClips) {
+ int size = globalModelClips.size();
+ int i, k, n;
+ ModelClipRetained modelClip;
+ Bounds bounds = ra.localeVwcBounds;
+ ModelClipRetained[] shapeScopedModelClip;
+
+ if (size > 0) {
+ for (i = 0; i < size; i++) {
+ modelClip = (ModelClipRetained) globalModelClips.get(i);
+ if (modelClip.enableFlag == true &&
+ modelClip.region != null && modelClip.switchState.currentSwitchOn) {
+ if (modelClip.region.intersect(bounds) == true) {
+ n = ((Shape3DRetained)ra.geometryAtom.source).numModelClips;
+ shapeScopedModelClip = ((Shape3DRetained)ra.geometryAtom.source).modelClips;
+
+ if (modelClip.isScoped) {
+ for (k = 0; k < n; k++) {
+ // then check if the modelClip is scoped to
+ // this group
+ if (shapeScopedModelClip[k] == modelClip) {
+
+ intersectedBounds[nModelClips] = modelClip.region;
+ intersectedModelClips[nModelClips++] = modelClip;
+ break;
+ }
+ }
+ }
+ else {
+ intersectedBounds[nModelClips] = modelClip.region;
+ intersectedModelClips[nModelClips++] = modelClip;
+ }
+ }
+ }
+ }
+ }
+ return nModelClips;
+ }
+
+ BackgroundRetained getApplicationBackground(BoundingSphere bounds, Locale viewLocale, View view) {
+ BackgroundRetained bg = null;
+ Bounds closestBounds;
+ BackgroundRetained back;
+ int i = 0, j = 0;
+ int nbacks;
+ ArrayList globalBgs;
+
+
+
+ // Need to lock lockObj, since on a multi-processor
+ // system with 2 views on a single universe, there might
+ // be councurrent access
+ synchronized(lockObj) {
+ nbacks = 0;
+ if (intersectedBounds.length < numberOfBgs)
+ intersectedBounds = new Bounds[numberOfBgs];
+
+
+
+ if ((globalBgs = (ArrayList)viewScopedBackgrounds.get(view)) != null) {
+ nbacks = processBgs(globalBgs, bounds, nbacks, viewLocale);
+ }
+ nbacks = processBgs(nonViewScopedBackgrounds, bounds, nbacks, viewLocale);
+
+ // If there are no intersections, set to black.
+ if (nbacks == 1) {
+ bg = intersectedBacks[0];
+ } else if (nbacks > 1) {
+ closestBounds =
+ bounds.closestIntersection(intersectedBounds);
+ for (j=0; j<nbacks; j++) {
+ if (intersectedBounds[j] == closestBounds) {
+ bg = intersectedBacks[j];
+ //System.out.println("matched " + closestBounds);
+ break;
+ }
+ }
+ }
+ return (bg);
+ }
+ }
+
+
+ // Called while holding lockObj lock
+ int processBgs(ArrayList globalBgs, BoundingSphere bounds, int nbacks, Locale viewLocale) {
+ int size = globalBgs.size();
+ int i;
+ BackgroundRetained back;
+
+ for (i=0; i<size; i++) {
+ back = (BackgroundRetained)globalBgs.get(i);
+ if (back.transformedRegion != null && back.switchState.currentSwitchOn) {
+ if (back.cachedLocale != viewLocale) {
+ localeBounds = (Bounds) back.transformedRegion.clone();
+ // Translate the transformed region
+ back.cachedLocale.hiRes.difference(viewLocale.hiRes, localeTranslation);
+ localeXform.setIdentity();
+ localeXform.setTranslation(localeTranslation);
+ localeBounds.transform(localeXform);
+ if (localeBounds.intersect(bounds) == true) {
+ intersectedBounds[nbacks] = localeBounds;
+ intersectedBacks[nbacks++] = back;
+ }
+ }
+ else {
+ if (back.transformedRegion.intersect(bounds) == true) {
+ intersectedBounds[nbacks] = back.transformedRegion;
+ intersectedBacks[nbacks++] = back;
+ }
+ }
+ }
+ }
+ return nbacks;
+ }
+
+ double[] backClipDistanceInVworld (BoundingSphere bounds, View view) {
+ int i,j;
+ int nclips;
+ Bounds closestBounds;
+ ClipRetained clip;
+ boolean backClipActive;
+ double[] backClipDistance;
+ double distance;
+ ArrayList globalClips;
+
+ // Need to lock intersectedBounds, since on a multi-processor
+ // system with 2 views on a single universe, there might
+ // be councurrent access
+ synchronized(lockObj) {
+ backClipDistance = null;
+ backClipActive = false;
+ nclips = 0;
+ distance = 0.0;
+ if (intersectedBounds.length < numberOfClips)
+ intersectedBounds = new Bounds[numberOfClips];
+
+ if ((globalClips = (ArrayList)viewScopedClips.get(view)) != null) {
+ nclips = processClips(globalClips, bounds, nclips);
+ }
+ nclips = processClips(nonViewScopedClips, bounds, nclips);
+
+
+
+
+ if (nclips == 1) {
+ distance = intersectedClips[0].backDistanceInVworld;
+ backClipActive = true;
+ } else if (nclips > 1) {
+ closestBounds =
+ bounds.closestIntersection(intersectedBounds);
+ for (j=0; j < nclips; j++) {
+ if (intersectedBounds[j] == closestBounds) {
+ distance = intersectedClips[j].backDistanceInVworld;
+ backClipActive = true;
+ break;
+ }
+ }
+ }
+ if (backClipActive) {
+ backClipDistance = new double[1];
+ backClipDistance[0] = distance;
+ }
+ return (backClipDistance);
+ }
+ }
+
+ int processClips(ArrayList globalClips, BoundingSphere bounds, int nclips) {
+ int i;
+ int size = globalClips.size();
+ ClipRetained clip;
+
+ for (i=0 ; i<size; i++) {
+ clip = (ClipRetained)globalClips.get(i);
+ if (clip.transformedRegion.intersect(bounds) == true
+ && clip.switchState.currentSwitchOn) {
+ intersectedBounds[nclips] = clip.transformedRegion;
+ intersectedClips[nclips++] = clip;
+ }
+ }
+ return nclips;
+ }
+
+
+ void updateLight(Object[] args) {
+ Object[] objs;
+ LightRetained light = (LightRetained)args[0];
+ int component = ((Integer)args[1]).intValue();
+ // Store the value to be updated during object update
+ // If its an ambient light, then if color changed, update immediately
+ if ((component & (LightRetained.INIT_MIRROR)) != 0) {
+ light.initMirrorObject(args);
+ }
+
+ if (light instanceof AmbientLightRetained &&
+ ((component & LightRetained.COLOR_CHANGED) != 0)) {
+ light.updateImmediateMirrorObject(args);
+ }
+ else if ((component & (LightRetained.COLOR_CHANGED|
+ LightRetained.INIT_MIRROR |
+ PointLightRetained.POSITION_CHANGED |
+ PointLightRetained.ATTENUATION_CHANGED|
+ DirectionalLightRetained.DIRECTION_CHANGED |
+ SpotLightRetained.DIRECTION_CHANGED |
+ SpotLightRetained.CONCENTRATION_CHANGED |
+ SpotLightRetained.ANGLE_CHANGED)) != 0) {
+ objs = getObjectArray();
+ objs[0] = args[0];
+ objs[1] = args[1];
+ objs[2] = args[2];
+ objs[3] = args[3];
+ objs[4] = args[4];
+
+ objList.add(objs);
+ }
+ else if ((component & LightRetained.CLEAR_MIRROR) != 0) {
+ light.clearMirrorObject(args);
+ }
+ else {
+ light.updateImmediateMirrorObject(args);
+ }
+
+
+
+ }
+
+ void updateBackground(Object[] args) {
+ BackgroundRetained bg = (BackgroundRetained)args[0];
+ bg.updateImmediateMirrorObject(args);
+ }
+
+ void updateFog(Object[] args) {
+ Object[] objs;
+ FogRetained fog = (FogRetained)args[0];
+ int component = ((Integer)args[1]).intValue();
+ if ((component & FogRetained.INIT_MIRROR) != 0) {
+ fog.initMirrorObject(args);
+ // Color, distance et all should be updated when renderer
+ // is not running ..
+ objs = getObjectArray();
+ objs[0] = args[0];
+ objs[1] = args[1];
+ objs[2] = args[2];
+ objs[3] = args[3];
+ objs[4] = args[4];
+ objList.add(objs);
+ }
+ else if ((component & FogRetained.CLEAR_MIRROR) != 0) {
+ fog.clearMirrorObject(args);
+ // Store the value to be updated during object update
+ } else if ((component & (FogRetained.COLOR_CHANGED |
+ LinearFogRetained.FRONT_DISTANCE_CHANGED|
+ LinearFogRetained.BACK_DISTANCE_CHANGED |
+ ExponentialFogRetained.DENSITY_CHANGED)) != 0) {
+ objs = getObjectArray();
+ objs[0] = args[0];
+ objs[1] = args[1];
+ objs[2] = args[2];
+ objs[3] = args[3];
+ objs[4] = args[4];
+ objList.add(objs);
+ }
+ else {
+ fog.updateImmediateMirrorObject(args);
+ }
+
+
+ }
+
+ void updateAltApp(Object[] args) {
+ AlternateAppearanceRetained altApp = (AlternateAppearanceRetained)args[0];
+ int component = ((Integer)args[1]).intValue();
+ if ((component & AlternateAppearanceRetained.INIT_MIRROR) != 0) {
+ AlternateAppearanceRetained altapp = (AlternateAppearanceRetained)args[0];
+ altapp.initMirrorObject(args);
+ }
+ else if ((component & AlternateAppearanceRetained.CLEAR_MIRROR) != 0) {
+ AlternateAppearanceRetained altapp = (AlternateAppearanceRetained)args[0];
+ altapp.clearMirrorObject(args);
+ }
+ else {
+ altApp.updateImmediateMirrorObject(args);
+ }
+
+
+ }
+
+ void updateClip(Object[] args) {
+ ClipRetained clip = (ClipRetained)args[0];
+ clip.updateImmediateMirrorObject(args);
+ }
+
+ void updateModelClip(Object[] args) {
+ ModelClipRetained modelClip = (ModelClipRetained)args[0];
+ Object[] objs;
+ int component = ((Integer)args[1]).intValue();
+
+ if ((component & ModelClipRetained.INIT_MIRROR) != 0) {
+ modelClip.initMirrorObject(args);
+ }
+ if ((component & ModelClipRetained.CLEAR_MIRROR) != 0) {
+ modelClip.clearMirrorObject(args);
+ }
+ else if ((component & (ModelClipRetained.PLANES_CHANGED |
+ ModelClipRetained.INIT_MIRROR |
+ ModelClipRetained.PLANE_CHANGED)) != 0) {
+ objs = getObjectArray();
+ objs[0] = args[0];
+ objs[1] = args[1];
+ objs[2] = args[2];
+ objs[3] = args[3];
+ objs[4] = args[4];
+ objList.add(objs);
+ }
+ else {
+ modelClip.updateImmediateMirrorObject(args);
+ }
+
+ }
+
+ void updateBoundingLeaf(Object[] args) {
+ BoundingLeafRetained bl = (BoundingLeafRetained)args[0];
+ Object[] users = (Object[])(args[3]);
+ bl.updateImmediateMirrorObject(args);
+ // Now update all users of this bounding leaf object
+ for (int i = 0; i < users.length; i++) {
+ LeafRetained mLeaf = (LeafRetained)users[i];
+ mLeaf.updateBoundingLeaf();
+ }
+ }
+
+ void updateShape3D(Object[] args) {
+ Shape3DRetained shape = (Shape3DRetained)args[0];
+ shape.updateImmediateMirrorObject(args);
+ }
+
+ void updateOrientedShape3D(Object[] args) {
+ OrientedShape3DRetained shape = (OrientedShape3DRetained)args[4];
+ shape.updateImmediateMirrorObject(args);
+ }
+
+ void updateMorph(Object[] args) {
+ MorphRetained morph = (MorphRetained)args[0];
+ morph.updateImmediateMirrorObject(args);
+ }
+
+
+ void updateTransformChange() {
+ int i,j;
+ Object[] nodes, nodesArr;
+ BoundingLeafRetained bl;
+ LightRetained ml;
+ UnorderList arrList;
+ int size;
+
+ targets = universe.transformStructure.getTargetList();
+ blUsers = universe.transformStructure.getBlUsers();
+
+ // process misc environment nodes
+ arrList = targets.targetList[Targets.ENV_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (j = 0; j < size; j++) {
+ nodes = (Object[])nodesArr[j];
+
+ for (i = 0; i < nodes.length; i++) {
+ if (nodes[i] instanceof LightRetained) {
+ ml = (LightRetained)nodes[i];
+ ml.updateImmediateTransformChange();
+ xformChangeList.add(nodes[i]);
+
+ } else if (nodes[i] instanceof FogRetained) {
+ FogRetained mfog = (FogRetained) nodes[i];
+ mfog.updateImmediateTransformChange();
+ xformChangeList.add(nodes[i]);
+
+ } else if (nodes[i] instanceof AlternateAppearanceRetained){
+ AlternateAppearanceRetained mAltApp =
+ (AlternateAppearanceRetained) nodes[i];
+ mAltApp.updateImmediateTransformChange();
+ xformChangeList.add(nodes[i]);
+
+ } else if (nodes[i] instanceof BackgroundRetained) {
+ BackgroundRetained bg = (BackgroundRetained) nodes[i];
+ bg.updateImmediateTransformChange();
+
+ } else if (nodes[i] instanceof ModelClipRetained) {
+ ModelClipRetained mc = (ModelClipRetained) nodes[i];
+ mc.updateImmediateTransformChange();
+ }
+ }
+ }
+ }
+
+ // process BoundingLeaf nodes
+ arrList = targets.targetList[Targets.BLN_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+ for (j = 0; j < size; j++) {
+ nodes = (Object[])nodesArr[j];
+ for (i = 0; i < nodes.length; i++) {
+ bl = (BoundingLeafRetained)nodes[i];
+ bl.updateImmediateTransformChange();
+ }
+ }
+ }
+
+ // Now notify the list of all users of bounding leaves
+ // to update its boundingleaf transformed region
+ if (blUsers != null) {
+ for (i = 0; i < blUsers.size(); i++) {
+ LeafRetained leaf = (LeafRetained) blUsers.get(i);
+ leaf.updateBoundingLeaf();
+ }
+ }
+ targets = null;
+ blUsers = null;
+ }
+
+
+ // The first element is TRUE, if alternate app is in effect
+ // The second element return the appearance that should be used
+ // Note , I can't just return null for app, then I don't know
+ // if the appearance is null or if the alternate app in not
+ // in effect
+ Object[] getInfluencingAppearance(RenderAtom ra, View view) {
+ AlternateAppearanceRetained altApp = null;
+ int i, j, k, n, nAltApp;;
+ Bounds closestBounds;
+ Bounds bounds;
+ Object[] retVal;
+ ArrayList globalAltApps;
+ retVal = new Object[2];
+
+ if (ra.geometryAtom.source.inBackgroundGroup) {
+ retVal[0] = Boolean.FALSE;
+ return retVal;
+ }
+
+ // Need to lock lockObj, since on a multi-processor
+ // system with 2 views on a single universe, there might
+ // be councurrent access
+ synchronized(lockObj) {
+ nAltApp = 0;
+ bounds = ra.localeVwcBounds;
+
+ if (intersectedBounds.length < numberOfAltApps)
+ intersectedBounds = new Bounds[numberOfAltApps];
+
+ if ((globalAltApps =(ArrayList)viewScopedAltAppearances.get(view)) != null) {
+ nAltApp = processAltApps(globalAltApps, ra, nAltApp);
+ }
+ nAltApp = processAltApps(nonViewScopedAltAppearances, ra, nAltApp);
+
+
+ altApp = null;
+ if (nAltApp == 1)
+ altApp = intersectedAltApps[0];
+ else if (nAltApp > 1) {
+ closestBounds = bounds.closestIntersection(intersectedBounds);
+ for (j= 0; j < nAltApp; j++) {
+ if (intersectedBounds[j] == closestBounds) {
+ altApp = intersectedAltApps[j];
+ break;
+ }
+ }
+ }
+ if (altApp == null) {
+ retVal[0] = Boolean.FALSE;
+ return retVal;
+ } else {
+ retVal[0] = Boolean.TRUE;
+ retVal[1] = altApp.appearance;
+ return retVal;
+ }
+ }
+ }
+
+ // Called while holding lockObj lock
+ int processAltApps(ArrayList globalAltApps, RenderAtom ra, int nAltApp) {
+ int size = globalAltApps.size();
+ AlternateAppearanceRetained altApp;
+ int i, k, n;
+ Bounds bounds = ra.localeVwcBounds;
+ AlternateAppearanceRetained[] shapeScopedAltApp;
+
+
+ if (size > 0) {
+ for (i = 0; i < size; i++) {
+ altApp = (AlternateAppearanceRetained) globalAltApps.get(i);
+ // System.out.println("altApp.region = "+altApp.region+" altApp.switchState.currentSwitchOn = "+altApp.switchState.currentSwitchOn+" intersect = "+altApp.region.intersect(ra.geometryAtom.vwcBounds));
+ // System.out.println("altApp.isScoped = "+altApp.isScoped);
+ // Note : There is no enable check for fog
+ if (altApp.region != null && altApp.switchState.currentSwitchOn) {
+ if (altApp.region.intersect(bounds) == true) {
+ n = ((Shape3DRetained)ra.geometryAtom.source).numAltApps;
+ shapeScopedAltApp = ((Shape3DRetained)ra.geometryAtom.source).altApps;
+ if (altApp.isScoped) {
+ for (k = 0; k < n; k++) {
+ // then check if the light is scoped to
+ // this group
+ if (altApp == shapeScopedAltApp[k]) {
+
+ intersectedBounds[nAltApp] = altApp.region;
+ intersectedAltApps[nAltApp++] = altApp;
+ break;
+ }
+ }
+ }
+ else {
+ intersectedBounds[nAltApp] = altApp.region;
+ intersectedAltApps[nAltApp++] = altApp;
+ }
+ }
+ }
+ }
+ }
+ return nAltApp;
+ }
+
+ void initViewSpecificInfo(J3dMessage m) {
+ int[] keys = (int[])m.args[2];
+ ArrayList vlists = (ArrayList)m.args[1];
+ ArrayList vsgs = (ArrayList)m.args[0];
+ if (vsgs != null) {
+ // System.out.println("===> non null Vsg");
+ int size = vsgs.size();
+ for (int i = 0; i < size; i++) {
+ ViewSpecificGroupRetained v = (ViewSpecificGroupRetained)vsgs.get(i);
+ ArrayList l = (ArrayList)vlists.get(i);
+ int index = keys[i];
+ // System.out.println("v = "+v+" index = "+index+" l = "+l);
+ v.cachedViewList.add(index, l);
+ /*
+ for (int k = 0; k < v.cachedViewList.size(); k++) {
+ System.out.println("v = "+v+" k = "+k+" v.cachedViewList.get(k) = "+v.cachedViewList.get(k));
+ }
+ */
+ }
+ }
+ }
+
+ void clearViewSpecificInfo(J3dMessage m) {
+ int[] keys = (int[])m.args[1];
+ ArrayList vsgs = (ArrayList)m.args[0];
+ if (vsgs != null) {
+ int size = vsgs.size();
+ for (int i = 0; i < size; i++) {
+ ViewSpecificGroupRetained v = (ViewSpecificGroupRetained)vsgs.get(i);
+ int index = keys[i];
+ if (index == -1) {
+ int csize = v.cachedViewList.size();
+ for (int j = 0; j< csize; j++) {
+ ArrayList l = (ArrayList)v.cachedViewList.get(j);
+ l.clear();
+ }
+ v.cachedViewList.clear();
+ }
+ else {
+ ArrayList l = (ArrayList) v.cachedViewList.remove(index);
+ l.clear();
+ }
+ }
+ }
+ }
+
+ void updateViewSpecificGroupChanged(J3dMessage m) {
+ int component = ((Integer)m.args[0]).intValue();
+ Object[] objAry = (Object[])m.args[1];
+
+ ArrayList ltList = null;
+ ArrayList fogList = null;
+ ArrayList mclipList = null;
+ ArrayList altAppList = null;
+ ArrayList bgList = null;
+ ArrayList clipList = null;
+ int idx;
+
+ if (((component & ViewSpecificGroupRetained.ADD_VIEW) != 0) ||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ View view = (View)objAry[0];
+ ArrayList vsgList = (ArrayList)objAry[1];
+ ArrayList leafList = (ArrayList)objAry[2];
+ int[] keyList = (int[])objAry[3];
+ int size = vsgList.size();
+ // Don't add null views
+
+ if (view != null) {
+ for (i = 0; i < size; i++) {
+ ViewSpecificGroupRetained vsg = (ViewSpecificGroupRetained)vsgList.get(i);
+ int index = keyList[i];
+ vsg.updateCachedInformation(ViewSpecificGroupRetained.ADD_VIEW, view, index);
+
+ }
+ size = leafList.size();
+ // Leaves is non-null only for the top VSG
+
+ if (size > 0) {
+ // Now process the list of affected leaved
+ for( i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof LightRetained) {
+ ((LightRetained)obj).isViewScoped = true;
+ numberOfLights++;
+ if (ltList == null) {
+ if ((ltList = (ArrayList)viewScopedLights.get(view)) == null) {
+ ltList = new ArrayList();
+ viewScopedLights.put(view, ltList);
+ }
+ }
+ ltList.add(obj);
+ }
+ if (obj instanceof FogRetained) {
+ ((FogRetained)obj).isViewScoped = true;
+ numberOfFogs++;
+ if (fogList == null) {
+ if ((fogList= (ArrayList)viewScopedFogs.get(view)) == null) {
+ fogList = new ArrayList();
+ viewScopedFogs.put(view, fogList);
+ }
+ }
+ fogList.add(obj);
+ }
+ if (obj instanceof ModelClipRetained) {
+ ((ModelClipRetained)obj).isViewScoped = true;
+ numberOfModelClips++;
+ if (mclipList == null) {
+ if ((mclipList= (ArrayList)viewScopedModelClips.get(view)) == null) {
+ mclipList = new ArrayList();
+ viewScopedModelClips.put(view, mclipList);
+ }
+ }
+ mclipList.add(obj);
+ }
+ if (obj instanceof AlternateAppearanceRetained) {
+ ((AlternateAppearanceRetained)obj).isViewScoped = true;
+ numberOfAltApps++;
+ if (altAppList == null) {
+ if ((altAppList= (ArrayList)viewScopedAltAppearances.get(view)) == null) {
+ altAppList = new ArrayList();
+ viewScopedAltAppearances.put(view, altAppList);
+ }
+ }
+ altAppList.add(obj);
+ }
+ if (obj instanceof ClipRetained) {
+ ((ClipRetained)obj).isViewScoped = true;
+ numberOfClips++;
+ if (clipList == null) {
+ if ((clipList= (ArrayList)viewScopedClips.get(view)) == null) {
+ clipList = new ArrayList();
+ viewScopedClips.put(view, clipList);
+ }
+ }
+ clipList.add(obj);
+ }
+ if (obj instanceof BackgroundRetained) {
+ ((BackgroundRetained)obj).isViewScoped = true;
+ numberOfBgs++;
+ if (bgList == null) {
+ if ((bgList= (ArrayList)viewScopedBackgrounds.get(view)) == null) {
+ bgList = new ArrayList();
+ viewScopedBackgrounds.put(view, bgList);
+ }
+ }
+ bgList.add(obj);
+ }
+ }
+ if (numberOfLights > retlights.length)
+ retlights = new LightRetained[numberOfLights];
+ if (intersectedFogs.length < numberOfFogs)
+ intersectedFogs = new FogRetained[numberOfFogs];
+ if (intersectedAltApps.length < numberOfAltApps)
+ intersectedAltApps = new AlternateAppearanceRetained[numberOfAltApps];
+ if (intersectedBacks.length < numberOfBgs)
+ intersectedBacks = new BackgroundRetained[numberOfBgs];
+ if (intersectedClips.length < numberOfClips)
+ intersectedClips = new ClipRetained[numberOfClips];
+ if (intersectedModelClips.length < numberOfModelClips)
+ intersectedModelClips = new ModelClipRetained[numberOfModelClips];
+ }
+ }
+ }
+ if (((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0)||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ ArrayList vsgList;
+ ArrayList leafList;
+ int[] keyList;
+ View view;
+
+ if ((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0) {
+ view = (View)objAry[0];
+ vsgList = (ArrayList)objAry[1];
+ leafList = (ArrayList)objAry[2];
+ keyList = (int[])objAry[3];
+ }
+ else {
+ view = (View)objAry[4];
+ vsgList = (ArrayList)objAry[5];
+ leafList = (ArrayList)objAry[6];
+ keyList = (int[])objAry[7];
+ }
+ // Don't add null views
+ if (view != null) {
+ int size = vsgList.size();
+ for (i = 0; i < size; i++) {
+ ViewSpecificGroupRetained vsg = (ViewSpecificGroupRetained)vsgList.get(i);
+ int index = keyList[i];
+ vsg.updateCachedInformation(ViewSpecificGroupRetained.REMOVE_VIEW, view, index);
+
+ }
+ size = leafList.size();
+ // Leaves is non-null only for the top VSG
+ if (size > 0) {
+ // Now process the list of affected leaved
+ for( i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof LightRetained) {
+ ((LightRetained)obj).isViewScoped = false;
+ numberOfLights--;
+ if (ltList == null) {
+ ltList = (ArrayList)viewScopedLights.get(view);
+ }
+ ltList.remove(obj);
+ }
+ if (obj instanceof FogRetained) {
+ ((FogRetained)obj).isViewScoped = false;
+ numberOfFogs--;
+ if (fogList == null) {
+ fogList = (ArrayList)viewScopedFogs.get(view);
+ }
+ fogList.remove(obj);
+ }
+ if (obj instanceof ModelClipRetained) {
+ ((ModelClipRetained)obj).isViewScoped = false;
+ numberOfModelClips--;
+ if (mclipList == null) {
+ mclipList = (ArrayList)viewScopedModelClips.get(view);
+ }
+ mclipList.remove(obj);
+ }
+ if (obj instanceof AlternateAppearanceRetained) {
+ ((AlternateAppearanceRetained)obj).isViewScoped = false;
+ numberOfAltApps--;
+ if (altAppList == null) {
+ altAppList = (ArrayList)viewScopedAltAppearances.get(view);
+ }
+ altAppList.remove(obj);
+ }
+ if (obj instanceof ClipRetained) {
+ ((ClipRetained)obj).isViewScoped = false;
+ numberOfClips--;
+ if (clipList == null) {
+ clipList = (ArrayList)viewScopedClips.get(view);
+ }
+ clipList.remove(obj);
+ }
+ if (obj instanceof BackgroundRetained) {
+ ((BackgroundRetained)obj).isViewScoped = false;
+ numberOfBgs++;
+ if (bgList == null) {
+ bgList = (ArrayList)viewScopedBackgrounds.get(view);
+ }
+ bgList.remove(obj);
+ }
+ }
+
+ // If there are no more lights scoped to the view,
+ // remove the mapping
+ if (ltList != null && ltList.size() == 0)
+ viewScopedLights.remove(view);
+ if (fogList != null && fogList.size() == 0)
+ viewScopedFogs.remove(view);
+ if (mclipList != null && mclipList.size() == 0)
+ viewScopedModelClips.remove(view);
+ if (altAppList != null && altAppList.size() == 0)
+ viewScopedAltAppearances.remove(view);
+ if (clipList != null && clipList.size() == 0)
+ viewScopedClips.remove(view);
+ if (bgList != null && bgList.size() == 0)
+ viewScopedBackgrounds.remove(view);
+ }
+ }
+ }
+
+ }
+
+ boolean isLightScopedToThisView(Object obj, View view) {
+ LightRetained light = (LightRetained)obj;
+ if (light.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedLights.get(view);
+ // If this is a scoped lights, but has no views then ..
+ if (l == null || !l.contains(light))
+ return false;
+ }
+ return true;
+ }
+
+ boolean isFogScopedToThisView(Object obj, View view) {
+ FogRetained fog = (FogRetained)obj;
+ if (fog.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedFogs.get(view);
+ // If this is a scoped fog, but has no views then ..
+ if (l ==null || !l.contains(fog))
+ return false;
+ }
+ return true;
+ }
+
+ boolean isAltAppScopedToThisView(Object obj, View view) {
+ AlternateAppearanceRetained altApp = (AlternateAppearanceRetained)obj;
+ if (altApp.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedAltAppearances.get(view);
+ // If this is a scoped altapp, but has no views then ..
+ if (l == null || !l.contains(altApp))
+ return false;
+ }
+ return true;
+ }
+
+ boolean isBgScopedToThisView(Object obj, View view) {
+ BackgroundRetained bg = (BackgroundRetained)obj;
+ if (bg.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedBackgrounds.get(view);
+ // If this is a scoped bg, but has no views then ..
+ if (l == null || !l.contains(bg))
+ return false;
+ }
+ return true;
+ }
+
+ boolean isClipScopedToThisView(Object obj, View view) {
+ ClipRetained clip = (ClipRetained)obj;
+ if (clip.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedClips.get(view);
+ // If this is a scoped clip, but has no views then ..
+ if (l == null || !l.contains(clip))
+ return false;
+ }
+ return true;
+ }
+
+
+ boolean isMclipScopedToThisView(Object obj, View view) {
+ ModelClipRetained mclip = (ModelClipRetained)obj;
+ if (mclip.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedModelClips.get(view);
+ // If this is a scoped mclip, but has no views then ..
+ if (l == null || !l.contains(mclip))
+ return false;
+ }
+ return true;
+ }
+
+ void cleanup() {}
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/RestrictedAccessException.java b/src/classes/share/javax/media/j3d/RestrictedAccessException.java
new file mode 100644
index 0000000..e52bfec
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RestrictedAccessException.java
@@ -0,0 +1,37 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates an attempt to access or modify a state variable
+ * without permission to do so. For example, invoking a set
+ * method for a state variable that is currently read-only.
+ */
+public class RestrictedAccessException extends RuntimeException {
+
+/**
+ * Create the exception object with default values.
+ */
+ public RestrictedAccessException(){
+ }
+
+/**
+ * Create the exception object that outputs a message.
+ * @param str the message string to be output.
+ */
+ public RestrictedAccessException(String str) {
+
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/RotPosPathInterpolator.java b/src/classes/share/javax/media/j3d/RotPosPathInterpolator.java
new file mode 100644
index 0000000..ebdf064
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RotPosPathInterpolator.java
@@ -0,0 +1,378 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Vector3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Matrix4d;
+import javax.vecmath.Point3f;
+
+
+/**
+ * RotPosPathInterpolator behavior. This class defines a behavior that
+ * modifies the rotational and translational components of its target
+ * TransformGroup by linearly interpolating among a series of predefined
+ * knot/positon and knot/orientation pairs (using the value generated
+ * by the specified Alpha object). The interpolated position and
+ * orientation are used to generate a transform in the local coordinate
+ * system of this interpolator.
+ */
+
+public class RotPosPathInterpolator extends PathInterpolator {
+ private Transform3D rotation = new Transform3D();
+
+ private Vector3f pos = new Vector3f();
+ private Quat4f tQuat = new Quat4f();
+ private Matrix4d tMat = new Matrix4d();
+
+ // Arrays of quaternions and positions at each knot
+ private Quat4f quats[];
+ private Point3f positions[];
+ private float prevInterpolationValue = Float.NaN;
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ RotPosPathInterpolator() {
+ }
+
+
+ /**
+ * Constructs a new interpolator that varies the rotation and translation
+ * of the target TransformGroup's transform.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node affected by this translator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * system in which this interpolator operates
+ * @param knots an array of knot values that specify interpolation points.
+ * @param quats an array of quaternion values at the knots.
+ * @param positions an array of position values at the knots.
+ * @exception IllegalArgumentException if the lengths of the
+ * knots, quats, and positions arrays are not all the same.
+ */
+ public RotPosPathInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ float[] knots,
+ Quat4f[] quats,
+ Point3f[] positions) {
+ super(alpha, target, axisOfTransform, knots);
+
+ if (knots.length != positions.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosPathInterpolator0"));
+
+ if (knots.length != quats.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosPathInterpolator0"));
+
+ setPathArrays(quats, positions);
+ }
+
+
+ /**
+ * Sets the quat at the specified index for this interpolator.
+ * @param index the index to be changed
+ * @param quat the new quat value
+ */
+ public void setQuat(int index, Quat4f quat) {
+ this.quats[index].set(quat);
+ }
+
+
+ /**
+ * Retrieves the quat value at the specified index.
+ * @param index the index of the value requested
+ * @param quat the quat to receive the quat value at the index
+ */
+ public void getQuat(int index, Quat4f quat) {
+ quat.set(this.quats[index]);
+ }
+
+
+ /**
+ * Sets the position at the specified index for this
+ * interpolator.
+ * @param index the index to be changed
+ * @param position the new position value
+ */
+ public void setPosition(int index, Point3f position) {
+ this.positions[index].set(position);
+ }
+
+
+ /**
+ * Retrieves the position value at the specified index.
+ * @param index the index of the value requested
+ * @param position the position to receive the position value at the index
+ */
+ public void getPosition(int index, Point3f position) {
+ position.set(this.positions[index]);
+ }
+
+
+ /**
+ * Replaces the existing arrays of knot values, quaternion
+ * values, and position values with the specified arrays.
+ * The arrays of knots, quats, and positions are copied
+ * into this interpolator object.
+ * @param knots a new array of knot values that specify
+ * interpolation points.
+ * @param quats a new array of quaternion values at the knots.
+ * @param positions a new array of position values at the knots.
+ * @exception IllegalArgumentException if the lengths of the
+ * knots, quats, and positions arrays are not all the same.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setPathArrays(float[] knots,
+ Quat4f[] quats,
+ Point3f[] positions) {
+
+ if (knots.length != quats.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosPathInterpolator0"));
+
+ if (knots.length != positions.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosPathInterpolator0"));
+
+ setKnots(knots);
+ setPathArrays(quats, positions);
+ }
+
+
+ // Set the specific arrays for this path interpolator
+ private void setPathArrays(Quat4f[] quats,
+ Point3f[] positions) {
+
+ this.quats = new Quat4f[quats.length];
+ for(int i = 0; i < quats.length; i++) {
+ this.quats[i] = new Quat4f();
+ this.quats[i].set(quats[i]);
+ }
+
+ this.positions = new Point3f[positions.length];
+ for(int i = 0; i < positions.length; i++) {
+ this.positions[i] = new Point3f();
+ this.positions[i].set(positions[i]);
+ }
+ }
+
+
+ /**
+ * Copies the array of quaternion values from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the quats.
+ * The individual array elements must be allocated by the caller.
+ * @param quats array that will receive the quats.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getQuats(Quat4f[] quats) {
+ for (int i = 0; i < this.quats.length; i++) {
+ quats[i].set(this.quats[i]);
+ }
+ }
+
+
+ /**
+ * Copies the array of position values from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the positions.
+ * The individual array elements must be allocated by the caller.
+ * @param positions array that will receive the positions.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getPositions(Point3f[] positions) {
+ for (int i = 0; i < this.positions.length; i++) {
+ positions[i].set(this.positions[i]);
+ }
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.setTransformAxis(Transform3D)</code>
+ */
+
+ public void setAxisOfRotPos(Transform3D axisOfRotPos) {
+ setTransformAxis(axisOfRotPos);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfRotPos() {
+ 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) {
+ double quatDot;
+
+ computePathInterpolation(alphaValue);
+
+ if (currentKnotIndex == 0 &&
+ currentInterpolationValue == 0f) {
+ tQuat.x = quats[0].x;
+ tQuat.y = quats[0].y;
+ tQuat.z = quats[0].z;
+ tQuat.w = quats[0].w;
+ pos.x = positions[0].x;
+ pos.y = positions[0].y;
+ pos.z = positions[0].z;
+ } else {
+ quatDot = quats[currentKnotIndex].x *
+ quats[currentKnotIndex+1].x +
+ quats[currentKnotIndex].y *
+ quats[currentKnotIndex+1].y +
+ quats[currentKnotIndex].z *
+ quats[currentKnotIndex+1].z +
+ quats[currentKnotIndex].w *
+ quats[currentKnotIndex+1].w;
+ if (quatDot < 0) {
+ tQuat.x = quats[currentKnotIndex].x +
+ (-quats[currentKnotIndex+1].x -
+ quats[currentKnotIndex].x)*currentInterpolationValue;
+ tQuat.y = quats[currentKnotIndex].y +
+ (-quats[currentKnotIndex+1].y -
+ quats[currentKnotIndex].y)*currentInterpolationValue;
+ tQuat.z = quats[currentKnotIndex].z +
+ (-quats[currentKnotIndex+1].z -
+ quats[currentKnotIndex].z)*currentInterpolationValue;
+ tQuat.w = quats[currentKnotIndex].w +
+ (-quats[currentKnotIndex+1].w -
+ quats[currentKnotIndex].w)*currentInterpolationValue;
+ } else {
+ tQuat.x = quats[currentKnotIndex].x +
+ (quats[currentKnotIndex+1].x -
+ quats[currentKnotIndex].x)*currentInterpolationValue;
+ tQuat.y = quats[currentKnotIndex].y +
+ (quats[currentKnotIndex+1].y -
+ quats[currentKnotIndex].y)*currentInterpolationValue;
+ tQuat.z = quats[currentKnotIndex].z +
+ (quats[currentKnotIndex+1].z -
+ quats[currentKnotIndex].z)*currentInterpolationValue;
+ tQuat.w = quats[currentKnotIndex].w +
+ (quats[currentKnotIndex+1].w -
+ quats[currentKnotIndex].w)*currentInterpolationValue;
+ }
+ pos.x = positions[currentKnotIndex].x +
+ (positions[currentKnotIndex+1].x -
+ positions[currentKnotIndex].x) * currentInterpolationValue;
+ pos.y = positions[currentKnotIndex].y +
+ (positions[currentKnotIndex+1].y -
+ positions[currentKnotIndex].y) * currentInterpolationValue;
+ pos.z = positions[currentKnotIndex].z +
+ (positions[currentKnotIndex+1].z -
+ positions[currentKnotIndex].z) * currentInterpolationValue;
+ }
+ tQuat.normalize();
+
+ // Set the rotation components
+ tMat.set(tQuat);
+
+ // Set the translation components.
+ tMat.m03 = pos.x;
+ tMat.m13 = pos.y;
+ tMat.m23 = pos.z;
+ rotation.set(tMat);
+
+ // construct a Transform3D from: axis * rotation * axisInverse
+ transform.mul(axis, rotation);
+ 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) {
+ RotPosPathInterpolator rppi = new RotPosPathInterpolator();
+ rppi.duplicateNode(this, forceDuplicate);
+ return rppi;
+ }
+
+
+
+ /**
+ * Copies all RotPosPathInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ RotPosPathInterpolator ri = (RotPosPathInterpolator) originalNode;
+
+ int len = ri.getArrayLengths();
+
+ // No API availble to set array size, so explicitly set it here
+ positions = new Point3f[len];
+ quats = new Quat4f[len];
+
+ Point3f point = new Point3f();
+ Quat4f quat = new Quat4f();
+
+ for (int i = 0; i < len; i++) {
+ positions[i] = new Point3f();
+ ri.getPosition(i, point);
+ setPosition(i, point);
+
+ quats[i] = new Quat4f();
+ ri.getQuat(i, quat);
+ setQuat(i, quat);
+ }
+
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/RotPosScalePathInterpolator.java b/src/classes/share/javax/media/j3d/RotPosScalePathInterpolator.java
new file mode 100644
index 0000000..e5f3ff9
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RotPosScalePathInterpolator.java
@@ -0,0 +1,450 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Matrix4d;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+import javax.vecmath.Point3f;
+
+
+/**
+ * RotPosScalePathInterpolation behavior. This class defines a behavior
+ * that varies the rotational, translational, and scale components of its
+ * target TransformGroup by linearly interpolating among a series of
+ * predefined knot/position, knot/orientation, and knot/scale pairs
+ * (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. The
+ * first knot must have a value of 0.0. The last knot must have a value
+ * of 1.0. An intermediate knot with index k must have a value strictly
+ * greater than any knot with index less than k.
+ */
+
+public class RotPosScalePathInterpolator extends PathInterpolator {
+ private Transform3D rotation = new Transform3D();
+
+ private Vector3f pos = new Vector3f();
+ private Quat4f tQuat = new Quat4f();
+ private Matrix4d tMat = new Matrix4d();
+ private Matrix4d sMat = new Matrix4d();
+
+ // Arrays of quaternions, positions, and scales at each knot
+ private Quat4f quats[];
+ private Point3f positions[];
+ private float scales[];
+
+ private float prevInterpolationValue = Float.NaN;
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ RotPosScalePathInterpolator() {
+ }
+
+
+ /**
+ * Constructs a new RotPosScalePathInterpolator object that varies the
+ * rotation, translation, and scale of the target TransformGroup's
+ * transform.
+ * @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 knots an array of knot values that specify interpolation points.
+ * @param quats an array of quaternion values at the knots.
+ * @param positions an array of position values at the knots.
+ * @param scales an array of scale component values at the knots.
+ * @exception IllegalArgumentException if the lengths of the
+ * knots, quats, positions, and scales arrays are not all the same.
+ */
+ public RotPosScalePathInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ float[] knots,
+ Quat4f[] quats,
+ Point3f[] positions,
+ float[] scales) {
+ super(alpha, target, axisOfTransform, knots);
+
+ if (knots.length != quats.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosScalePathInterpolator1"));
+
+ if (knots.length != positions.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosScalePathInterpolator0"));
+
+ if (knots.length != scales.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosScalePathInterpolator2"));
+
+ setPathArrays(quats, positions, scales);
+ }
+
+
+ /**
+ * Sets the quat value at the specified index for this
+ * interpolator.
+ * @param index the index to be changed
+ * @param quat the new quat value at index
+ */
+ public void setQuat(int index, Quat4f quat) {
+ this.quats[index].set(quat);
+ }
+
+
+ /**
+ * Retrieves the quat value at the specified index.
+ * @param index the index of the value requested
+ * @return the interpolator's quat value at the index
+ */
+ public void getQuat(int index, Quat4f quat) {
+ quat.set(this.quats[index]);
+ }
+
+
+ /**
+ * Sets the position value at the specified index for
+ * this interpolator.
+ * @param index the index to be changed
+ * @param position the new position value at index
+ */
+ public void setPosition(int index, Point3f position) {
+ this.positions[index].set(position);
+ }
+
+
+ /**
+ * Retrieves the position value at the specified index.
+ * @param index the index of the value requested
+ * @return the interpolator's position value at the index
+ */
+ public void getPosition(int index, Point3f position) {
+ position.set(this.positions[index]);
+ }
+
+
+ /**
+ * Sets the scale at the specified index for this
+ * interpolator.
+ * @param index the index to be changed
+ * @param scale the new scale at index
+ */
+ public void setScale(int index, float scale) {
+ this.scales[index] = scale;
+ }
+
+
+ /**
+ * Retrieves the scale at the specified index.
+ * @param index the index of the value requested
+ * @return the interpolator's scale value at index
+ */
+ public float getScale(int index) {
+ return this.scales[index];
+ }
+
+
+ /**
+ * Replaces the existing arrays of knot values, quaternion
+ * values, position values, and scale values with the specified arrays.
+ * The arrays of knots, quats, positions, and scales are copied
+ * into this interpolator object.
+ * @param knots a new array of knot values that specify
+ * interpolation points.
+ * @param quats a new array of quaternion values at the knots.
+ * @param positions a new array of position values at the knots.
+ * @param scales a new array of scale component values at the knots.
+ * @exception IllegalArgumentException if the lengths of the
+ * knots, quats, positions, and scales arrays are not all the same.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setPathArrays(float[] knots,
+ Quat4f[] quats,
+ Point3f[] positions,
+ float[] scales) {
+ if (knots.length != quats.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosScalePathInterpolator1"));
+
+ if (knots.length != positions.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosScalePathInterpolator0"));
+
+ if (knots.length != scales.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotPosScalePathInterpolator2"));
+
+ setKnots(knots);
+ setPathArrays(quats, positions, scales);
+ }
+
+
+ // Set the specific arrays for this path interpolator
+ private void setPathArrays(Quat4f[] quats,
+ Point3f[] positions,
+ float[] scales) {
+
+ this.quats = new Quat4f[quats.length];
+ for(int i = 0; i < quats.length; i++) {
+ this.quats[i] = new Quat4f();
+ this.quats[i].set(quats[i]);
+ }
+
+ this.positions = new Point3f[positions.length];
+ for(int i = 0; i < positions.length; i++) {
+ this.positions[i] = new Point3f();
+ this.positions[i].set(positions[i]);
+ }
+
+ this.scales = new float[scales.length];
+ for(int i = 0; i < scales.length; i++) {
+ this.scales[i] = scales[i];
+ }
+ }
+
+
+ /**
+ * Copies the array of quaternion values from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the quats.
+ * The individual array elements must be allocated by the caller.
+ * @param quats array that will receive the quats.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getQuats(Quat4f[] quats) {
+ for (int i = 0; i < this.quats.length; i++) {
+ quats[i].set(this.quats[i]);
+ }
+ }
+
+
+ /**
+ * Copies the array of position values from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the positions.
+ * The individual array elements must be allocated by the caller.
+ * @param positions array that will receive the positions.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getPositions(Point3f[] positions) {
+ for (int i = 0; i < this.positions.length; i++) {
+ positions[i].set(this.positions[i]);
+ }
+ }
+
+
+ /**
+ * Copies the array of scale values from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the scales.
+ * @param scales array that will receive the scales.
+ *
+ * @since Java 3D 1.2
+ */
+ public void getScales(float[] scales) {
+ for (int i = 0; i < this.scales.length; i++) {
+ scales[i] = this.scales[i];
+ }
+ }
+
+ /**
+ * @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.geTransformAxis()</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) {
+
+ float scale;
+ double quatDot;
+
+ computePathInterpolation(alphaValue);
+
+ if (currentKnotIndex == 0 &&
+ currentInterpolationValue == 0f) {
+ tQuat.x = quats[0].x;
+ tQuat.y = quats[0].y;
+ tQuat.z = quats[0].z;
+ tQuat.w = quats[0].w;
+ pos.x = positions[0].x;
+ pos.y = positions[0].y;
+ pos.z = positions[0].z;
+ scale = scales[0];
+ } else {
+ quatDot = quats[currentKnotIndex].x *
+ quats[currentKnotIndex+1].x +
+ quats[currentKnotIndex].y *
+ quats[currentKnotIndex+1].y +
+ quats[currentKnotIndex].z *
+ quats[currentKnotIndex+1].z +
+ quats[currentKnotIndex].w *
+ quats[currentKnotIndex+1].w;
+ if (quatDot < 0) {
+ tQuat.x = quats[currentKnotIndex].x +
+ (-quats[currentKnotIndex+1].x -
+ quats[currentKnotIndex].x)*currentInterpolationValue;
+ tQuat.y = quats[currentKnotIndex].y +
+ (-quats[currentKnotIndex+1].y -
+ quats[currentKnotIndex].y)*currentInterpolationValue;
+ tQuat.z = quats[currentKnotIndex].z +
+ (-quats[currentKnotIndex+1].z -
+ quats[currentKnotIndex].z)*currentInterpolationValue;
+ tQuat.w = quats[currentKnotIndex].w +
+ (-quats[currentKnotIndex+1].w -
+ quats[currentKnotIndex].w)*currentInterpolationValue;
+ } else {
+ tQuat.x = quats[currentKnotIndex].x +
+ (quats[currentKnotIndex+1].x -
+ quats[currentKnotIndex].x)*currentInterpolationValue;
+ tQuat.y = quats[currentKnotIndex].y +
+ (quats[currentKnotIndex+1].y -
+ quats[currentKnotIndex].y)*currentInterpolationValue;
+ tQuat.z = quats[currentKnotIndex].z +
+ (quats[currentKnotIndex+1].z -
+ quats[currentKnotIndex].z)*currentInterpolationValue;
+ tQuat.w = quats[currentKnotIndex].w +
+ (quats[currentKnotIndex+1].w -
+ quats[currentKnotIndex].w)*currentInterpolationValue;
+ }
+ pos.x = positions[currentKnotIndex].x +
+ (positions[currentKnotIndex+1].x -
+ positions[currentKnotIndex].x) * currentInterpolationValue;
+ pos.y = positions[currentKnotIndex].y +
+ (positions[currentKnotIndex+1].y -
+ positions[currentKnotIndex].y) * currentInterpolationValue;
+ pos.z = positions[currentKnotIndex].z +
+ (positions[currentKnotIndex+1].z -
+ positions[currentKnotIndex].z) * currentInterpolationValue;
+ scale = scales[currentKnotIndex] +
+ (scales[currentKnotIndex+1] -
+ scales[currentKnotIndex]) * currentInterpolationValue;
+ }
+ tQuat.normalize();
+
+ sMat.set(scale);
+ tMat.set(tQuat);
+ tMat.mul(sMat);
+ // Set the translation components.
+
+ tMat.m03 = pos.x;
+ tMat.m13 = pos.y;
+ tMat.m23 = pos.z;
+ rotation.set(tMat);
+
+ // construct a Transform3D from: axis * rotation * axisInverse
+ transform.mul(axis, rotation);
+ 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) {
+ RotPosScalePathInterpolator ri = new RotPosScalePathInterpolator();
+ ri.duplicateNode(this, forceDuplicate);
+ return ri;
+ }
+
+
+ /**
+ * Copies all RotPosScalePathInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ RotPosScalePathInterpolator ri =
+ (RotPosScalePathInterpolator) originalNode;
+
+ int len = ri.getArrayLengths();
+
+ // No API available to change size of array, so set here explicitly
+ positions = new Point3f[len];
+ quats = new Quat4f[len];
+ scales = new float[len];
+
+ Point3f point = new Point3f();
+ Quat4f quat = new Quat4f();
+
+ for (int i = 0; i < len; i++) {
+ positions[i] = new Point3f();
+ ri.getPosition(i, point);
+ setPosition(i, point);
+
+ quats[i] = new Quat4f();
+ ri.getQuat(i, quat);
+ setQuat(i, quat);
+
+ setScale(i, ri.getScale(i));
+ }
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/RotationInterpolator.java b/src/classes/share/javax/media/j3d/RotationInterpolator.java
new file mode 100644
index 0000000..c88ce23
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RotationInterpolator.java
@@ -0,0 +1,202 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * Rotation interpolator behavior. This class defines a behavior
+ * that modifies the rotational component of its target TransformGroup
+ * by linearly interpolating between a pair of specified angles
+ * (using the value generated by the specified Alpha object).
+ * The interpolated angle is used to generate a rotation transform
+ * about the local Y-axis of this interpolator.
+ */
+
+public class RotationInterpolator extends TransformInterpolator {
+
+ float minimumAngle;
+ float maximumAngle;
+ private Transform3D rotation = new Transform3D();
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ RotationInterpolator() {
+ }
+
+ /**
+ * Constructs a trivial rotation interpolator with a specified target,
+ * an default axisOfTranform set to identity, a minimum angle of 0.0f, and
+ * a maximum angle of 2*pi radians.
+ * @param alpha The alpha object for this Interpolator
+ * @param target The target for this rotation Interpolator
+ */
+ public RotationInterpolator(Alpha alpha, TransformGroup target) {
+ super(alpha, target);
+ this.minimumAngle = 0.0f;
+ this.maximumAngle = 2.0f*(float)Math.PI;
+ }
+
+
+ /**
+ * Constructs a new rotation interpolator that varies the target
+ * transform node's rotational component.
+ * @param alpha the alpha generator to use in the rotation computation
+ * @param target the TransformGroup node affected by this interpolator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * system in which this interpolator operates. The rotation is done
+ * about the Y-axis of this local coordinate system.
+ * @param minimumAngle the starting angle in radians
+ * @param maximumAngle the ending angle in radians
+ */
+ public RotationInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ float minimumAngle,
+ float maximumAngle) {
+ super(alpha, target, axisOfTransform);
+ this.minimumAngle = minimumAngle;
+ this.maximumAngle = maximumAngle;
+ }
+
+ /**
+ * This method sets the minimumAngle for this interpolator, in
+ * radians.
+ * @param angle the new minimal angle
+ */
+ public void setMinimumAngle(float angle) {
+ this.minimumAngle = angle;
+ }
+
+ /**
+ * This method retrieves this interpolator's minimumAngle, in
+ * radians.
+ * @return the interpolator's minimal angle value
+ */
+ public float getMinimumAngle() {
+ return this.minimumAngle;
+ }
+
+ /**
+ * This method sets the maximumAngle for this interpolator, in
+ * radians.
+ * @param angle the new maximal angle value
+ */
+ public void setMaximumAngle(float angle) {
+ this.maximumAngle = angle;
+ }
+
+ /**
+ * This method retrieves this interpolator's maximumAngle, in
+ * radians.
+ * @return the interpolator's maximal angle value
+ */
+ public float getMaximumAngle() {
+ return this.maximumAngle;
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.setTransformAxis(Transform3D)</code>
+ */
+ public void setAxisOfRotation(Transform3D axisOfRotation) {
+ setTransformAxis(axisOfRotation);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfRotation() {
+ 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) {
+ double val = (1.0-alphaValue)*minimumAngle + alphaValue*maximumAngle;
+
+ // construct a Transform3D from: axis * rotation * axisInverse
+ rotation.rotY(val);
+ transform.mul(axis, rotation);
+ 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) {
+ RotationInterpolator ri = new RotationInterpolator();
+ ri.duplicateNode(this, forceDuplicate);
+ return ri;
+ }
+
+
+ /**
+ * Copies all RotationInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ RotationInterpolator ri = (RotationInterpolator) originalNode;
+
+ setMinimumAngle(ri.getMinimumAngle());
+ setMaximumAngle(ri.getMaximumAngle());
+
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/RotationPathInterpolator.java b/src/classes/share/javax/media/j3d/RotationPathInterpolator.java
new file mode 100644
index 0000000..6c4446b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/RotationPathInterpolator.java
@@ -0,0 +1,296 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Quat4f;
+
+
+/**
+ * RotationPathInterpolator behavior. This class defines a behavior
+ * that varies the rotational component of its target TransformGroup
+ * by linearly interpolating among a series of predefined knot/orientation
+ * pairs (using the value generated by the specified Alpha object). The
+ * interpolated orientation is used to generate a rotation transform in
+ * the local coordinate system. The first knot must have a value of 0.0.
+ * The last knot must have a value
+ * of 1.0. An intermediate knot with index k must have a value strictly
+ * greater than any knot with index less than k.
+ */
+
+public class RotationPathInterpolator extends PathInterpolator {
+ private Transform3D rotation = new Transform3D();
+
+ private Quat4f tQuat = new Quat4f();
+
+ // Array of quaternions at each knot
+ private Quat4f quats[];
+ private float prevInterpolationValue = Float.NaN;
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+ // non-public, default constructor used by cloneNode
+ RotationPathInterpolator() {
+ }
+
+
+ /**
+ * Constructs a new RotationPathInterpolator object that varies the
+ * target TransformGroup node's transform.
+ * @param alpha the alpha object of this interpolator
+ * @param target the TransformGroup node affected by this interpolator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * system in which this interpolator operates
+ * @param knots an array of knot values that specify interpolation points
+ * @param quats an array of quaternion values at the knots
+ * @exception IllegalArgumentException if the lengths of the
+ * knots and quats arrays are not the same.
+ */
+ public RotationPathInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ float[] knots,
+ Quat4f[] quats) {
+ super(alpha,target, axisOfTransform, knots);
+
+ if (knots.length != quats.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotationPathInterpolator0"));
+
+ setPathArrays(quats);
+ }
+
+
+ /**
+ * Sets the quat value at the specified index for this
+ * interpolator.
+ * @param index the index to be changed
+ * @param quat the new quat value at the index
+ */
+ public void setQuat(int index, Quat4f quat) {
+ this.quats[index].set(quat);
+ }
+
+
+ /**
+ * Retrieves the quat value at the specified index.
+ * @param index the index of the value requested
+ * @param quat the quat object that will have the
+ * quat value at index copied into it.
+ */
+ public void getQuat(int index, Quat4f quat) {
+ quat.set(this.quats[index]);
+ }
+
+
+ /**
+ * Replaces the existing arrays of knot values and quaternion
+ * values with the specified arrays.
+ * The arrays of knots and quats are copied
+ * into this interpolator object.
+ * @param knots a new array of knot values that specify
+ * interpolation points
+ * @param quats a new array of quaternion values at the knots
+ * @exception IllegalArgumentException if the lengths of the
+ * knots and quats arrays are not the same.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setPathArrays(float[] knots,
+ Quat4f[] quats) {
+ if (knots.length != quats.length)
+ throw new IllegalArgumentException(J3dI18N.getString("RotationPathInterpolator0"));
+
+ setKnots(knots);
+ setPathArrays(quats);
+ }
+
+
+ // Set the specific arrays for this path interpolator
+ private void setPathArrays(Quat4f[] quats) {
+ this.quats = new Quat4f[quats.length];
+ for(int i = 0; i < quats.length; i++) {
+ this.quats[i] = new Quat4f();
+ this.quats[i].set(quats[i]);
+ }
+ }
+
+
+ /**
+ * Copies the array of quaternion values from this interpolator
+ * into the specified array.
+ * The array must be large enough to hold all of the quats.
+ * The individual array elements must be allocated by the caller.
+ * @param quats array that will receive the quats
+ *
+ * @since Java 3D 1.2
+ */
+ public void getQuats(Quat4f[] quats) {
+ for (int i = 0; i < this.quats.length; i++) {
+ quats[i].set(this.quats[i]);
+ }
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.seTransformAxis(Transform3D)</code>
+ */
+ public void setAxisOfRotation(Transform3D axisOfRotation) {
+ setTransformAxis(axisOfRotation);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfRotation() {
+ return getTransformAxis();
+ }
+
+ // The RotationPathInterpolator's initialize routine uses the default
+ // initialization routine.
+
+
+ /**
+ * 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) {
+ float tt;
+ double quatDot;
+ computePathInterpolation(alphaValue);
+ // For RPATH, take quaternion average and set rotation in TransformGroup
+
+ if (currentKnotIndex == 0 &&
+ currentInterpolationValue == 0f) {
+ tQuat.x = quats[0].x;
+ tQuat.y = quats[0].y;
+ tQuat.z = quats[0].z;
+ tQuat.w = quats[0].w;
+ } else {
+ quatDot = quats[currentKnotIndex].x *
+ quats[currentKnotIndex+1].x +
+ quats[currentKnotIndex].y *
+ quats[currentKnotIndex+1].y +
+ quats[currentKnotIndex].z *
+ quats[currentKnotIndex+1].z +
+ quats[currentKnotIndex].w *
+ quats[currentKnotIndex+1].w;
+ if (quatDot < 0) {
+ tQuat.x = quats[currentKnotIndex].x +
+ (-quats[currentKnotIndex+1].x -
+ quats[currentKnotIndex].x)*currentInterpolationValue;
+ tQuat.y = quats[currentKnotIndex].y +
+ (-quats[currentKnotIndex+1].y -
+ quats[currentKnotIndex].y)*currentInterpolationValue;
+ tQuat.z = quats[currentKnotIndex].z +
+ (-quats[currentKnotIndex+1].z -
+ quats[currentKnotIndex].z)*currentInterpolationValue;
+ tQuat.w = quats[currentKnotIndex].w +
+ (-quats[currentKnotIndex+1].w -
+ quats[currentKnotIndex].w)*currentInterpolationValue;
+ } else {
+ tQuat.x = quats[currentKnotIndex].x +
+ (quats[currentKnotIndex+1].x -
+ quats[currentKnotIndex].x)*currentInterpolationValue;
+ tQuat.y = quats[currentKnotIndex].y +
+ (quats[currentKnotIndex+1].y -
+ quats[currentKnotIndex].y)*currentInterpolationValue;
+ tQuat.z = quats[currentKnotIndex].z +
+ (quats[currentKnotIndex+1].z -
+ quats[currentKnotIndex].z)*currentInterpolationValue;
+ tQuat.w = quats[currentKnotIndex].w +
+ (quats[currentKnotIndex+1].w -
+ quats[currentKnotIndex].w)*currentInterpolationValue;
+ }
+ }
+
+ tQuat.normalize();
+ rotation.set(tQuat);
+
+ // construct a Transform3D from: axis * rotation * axisInverse
+ transform.mul(axis, rotation);
+ 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) {
+ RotationPathInterpolator rpi = new RotationPathInterpolator();
+ rpi.duplicateNode(this, forceDuplicate);
+ return rpi;
+ }
+
+
+ /**
+ * Copies all RotationPathInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ RotationPathInterpolator ri =
+ (RotationPathInterpolator) originalNode;
+
+ int len = ri.getArrayLengths();
+
+ // No API available to change size of array, so set here explicitly
+ quats = new Quat4f[len];
+ Quat4f quat = new Quat4f();
+
+ for (int i = 0; i < len; i++) {
+ quats[i] = new Quat4f();
+ ri.getQuat(i, quat);
+ setQuat(i, quat);
+ }
+
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ScaleInterpolator.java b/src/classes/share/javax/media/j3d/ScaleInterpolator.java
new file mode 100644
index 0000000..7897525
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ScaleInterpolator.java
@@ -0,0 +1,205 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Scale interpolation behavior. This class defines a behavior
+ * that modifies the uniform scale component of its target
+ * TransformGroup by linearly interpolating between a pair of
+ * specified scale values (using the value generated by the
+ * specified Alpha object). The interpolated scale value is
+ * used to generate a scale transform in the local coordinate
+ * system of this interpolator.
+ */
+
+public class ScaleInterpolator extends TransformInterpolator {
+
+ float minimumScale;
+ float maximumScale;
+ private Transform3D scale = new Transform3D();
+
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ ScaleInterpolator() {
+ }
+
+ /**
+ * Constructs a trivial scale interpolator that varies its target
+ * TransformGroup node between the two specified alpha values
+ * using the specified alpha, an identity matrix,
+ * a minimum scale = 0.1f, and a maximum scale = 1.0f.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node affected by this interpolator
+ */
+ public ScaleInterpolator(Alpha alpha,
+ TransformGroup target) {
+
+ super(alpha, target);
+ this.minimumScale = 0.1f;
+ this.maximumScale = 1.0f;
+ }
+
+ /**
+ * Constructs a new scaleInterpolator object that varies its target
+ * TransformGroup node's scale component between two scale values
+ * (minimumScale and maximumScale).
+ * @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
+ * system in which this interpolator operates; the scale is done
+ * about the origin of this local coordinate system.
+ * @param minimumScale the starting scale
+ * @param maximumScale the ending scale
+ */
+ public ScaleInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ float minimumScale,
+ float maximumScale) {
+
+ super(alpha, target, axisOfTransform);
+
+ this.minimumScale = minimumScale;
+ this.maximumScale = maximumScale;
+ }
+
+ /**
+ * This method sets the minimumScale for this interpolator.
+ * @param scale The new minimal scale
+ */
+ public void setMinimumScale(float scale) {
+ this.minimumScale = scale;
+ }
+
+ /**
+ * This method retrieves this interpolator's minimumScale.
+ * @return the interpolator's minimal scale value
+ */
+ public float getMinimumScale() {
+ return this.minimumScale;
+ }
+
+ /**
+ * This method sets the maximumScale for this interpolator.
+ * @param scale the new maximum scale
+ */
+ public void setMaximumScale(float scale) {
+ this.maximumScale = scale;
+ }
+
+ /**
+ * This method retrieves this interpolator's maximumScale.
+ * @return the interpolator's maximum scale vslue
+ */
+ public float getMaximumScale() {
+ return this.maximumScale;
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.setTransformAxis(Transform3D)</code>
+ */
+ public void setAxisOfScale(Transform3D axisOfScale) {
+ setTransformAxis(axisOfScale);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfScale() {
+ 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) {
+
+ double val = (1.0-alphaValue)*minimumScale + alphaValue*maximumScale;
+
+ // construct a Transform3D from: axis * scale * axisInverse
+ scale.set(val);
+ transform.mul(axis, scale);
+ 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) {
+ ScaleInterpolator si = new ScaleInterpolator();
+ si.duplicateNode(this, forceDuplicate);
+ return si;
+ }
+
+
+ /**
+ * Copies all ScaleInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ScaleInterpolator si = (ScaleInterpolator) originalNode;
+
+ setMinimumScale(si.getMinimumScale());
+ setMaximumScale(si.getMaximumScale());
+
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SceneGraphCycleException.java b/src/classes/share/javax/media/j3d/SceneGraphCycleException.java
new file mode 100644
index 0000000..52dacaf
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SceneGraphCycleException.java
@@ -0,0 +1,44 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates a graph that contains a cycle.
+ * Java 3D scene graphs are directed acyclic graphs and, as such, do not
+ * permit cycles.
+ * This exception is thrown when a graph containing a cycle:
+ * <ul>
+ * <li> is made live
+ * <li> is compiled
+ * <li> is cloned
+ * <li> has getBounds() called on it.
+ * </ul>
+ */
+public class SceneGraphCycleException extends IllegalSceneGraphException{
+
+/**
+ * Create the exception object with default values.
+ */
+ public SceneGraphCycleException(){
+ }
+
+/**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public SceneGraphCycleException(String str){
+
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SceneGraphObject.java b/src/classes/share/javax/media/j3d/SceneGraphObject.java
new file mode 100644
index 0000000..71052f2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SceneGraphObject.java
@@ -0,0 +1,405 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+
+/**
+ * SceneGraphObject is a common superclass for
+ * all scene graph component objects. This includes Node,
+ * Geometry, Appearance, etc.
+ */
+public abstract class SceneGraphObject extends Object {
+ // Any global flags? (e.g., execution cullable, collideable)
+
+ // Reference to the retained-mode scene-graph element.
+ SceneGraphObjectRetained retained;
+
+ // This object's capability bits
+ private long capabilityBits = 0L;
+
+ // This object's capabilityIsFrequent bits
+ private long capabilityIsFrequentBits = ~0L;
+
+ //boolean indicating is Scene Graph is compiled
+ private boolean compiled = false;
+
+ //boolean indicating if Scene Graph is live.
+ private boolean live = false;
+
+ //boolean indicating if Scene Graph is live or compiled
+ private boolean liveOrCompiled = false;
+
+ // A reference to user data
+ private Object userData = null;
+
+ // use for cloneTree/cloneNode only, set to null after the operation
+ Hashtable nodeHashtable = null;
+
+
+
+ /**
+ * Constructs a SceneGraphObject with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * capability bits : clear (all bits)<br>
+ * isLive : false<br>
+ * isCompiled : false<br>
+ * user data : null<br>
+ * </ul>
+ */
+ public SceneGraphObject() {
+ createRetained();
+ }
+
+ /**
+ * Creates the retained mode object that this scene graph object
+ * will point to. This should be overridden by those classes
+ * that have a specific retained mode object.
+ */
+ void createRetained() {
+ this.retained = null;
+
+ // Non-abstract subclasses of SceneGraphObject should override
+ // this function with code which is something like the following:
+ //
+ // this.retained = new <ClassName>Retained();
+ // this.retained.setSource(this);
+ }
+
+ /**
+ * Retrieves the specified capability bit. Note that only one capability
+ * bit may be retrieved per method invocation--capability bits cannot
+ * be ORed together.
+ * @param bit the bit whose value is returned
+ * @return true if the bit is set, false if the bit is clear
+ */
+ public final boolean getCapability(int bit) {
+ return (capabilityBits & (1L << bit)) != 0L;
+ }
+
+ /**
+ * Sets the specified capability bit. Note that only one capability bit
+ * may be set per method invocation--capability bits cannot be ORed
+ * together.
+ * @param bit the bit to set
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ */
+ public final void setCapability(int bit) {
+ if (isLiveOrCompiled()) {
+ throw new RestrictedAccessException(J3dI18N.getString("SceneGraphObject0"));
+ }
+
+ capabilityBits |= (1L << bit);
+ retained.handleFrequencyChange(bit);
+
+ }
+
+ /**
+ * Clear the specified capability bit. Note that only one capability bit
+ * may be cleared per method invocation--capability bits cannot be ORed
+ * together.
+ * @param bit the bit to clear
+ * @exception RestrictedAccessException if this object is part of live
+ * or compiled scene graph
+ */
+ public final void clearCapability(int bit) {
+ if (isLiveOrCompiled())
+ throw new RestrictedAccessException(J3dI18N.getString("SceneGraphObject0"));
+
+ capabilityBits &= ~(1L << bit);
+ retained.handleFrequencyChange(bit);
+ }
+
+
+ // Internal method, returns true if no capability bits are set
+ final boolean capabilityBitsEmpty() {
+ return capabilityBits == 0L;
+ }
+
+
+ /**
+ * Retrieves the isFrequent bit associated with the specified capability
+ * bit.
+ *
+ * Note that only one isFrequent bit, for a single capability
+ * bit, may be retrieved per method invocation--capability bits cannot
+ * be ORed together.
+ *
+ * @param bit the bit whose value is returned
+ *
+ * @return true if the isFrequent bit is set, false if the isFrequent
+ * bit is clear
+ *
+ * @since Java 3D 1.3
+ */
+ public final boolean getCapabilityIsFrequent(int bit) {
+ return (capabilityIsFrequentBits & (1L << bit)) != 0L;
+ }
+
+ /**
+ * Sets the isFrequent bit associated with the specified
+ * capability bit. Setting the isFrequent bit indicates that the
+ * application may frequently access or modify those attributes
+ * permitted by the associated capability bit. This can be used
+ * by Java 3D as a hint to avoid certain optimizations that could
+ * cause those accesses or modifications to be expensive. By
+ * default the isFrequent bit associated with each capability bit
+ * is set.
+ *
+ * <p>
+ * Unlike setCapability, this method may be called on a live scene
+ * graph object (but not on a compiled object).
+ *
+ * <p>
+ * Note that only one isFrequent bit, for a single capability bit,
+ * may be set per method invocation--capability bits cannot be ORed
+ * together.
+ *
+ * @param bit the capability bit for which to set the associated
+ * isFrequent bit
+ *
+ * @exception RestrictedAccessException if this object is part of a
+ * compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public final void setCapabilityIsFrequent(int bit) {
+ if (isCompiled())
+ throw new RestrictedAccessException(J3dI18N.getString("SceneGraphObject1"));
+
+ capabilityIsFrequentBits |= (1L << bit);
+ retained.handleFrequencyChange(bit);
+ }
+
+ /**
+ * Clears the isFrequent bit associated with the specified
+ * capability bit. Clearing the isFrequent bit indicates that the
+ * application will infrequently access or modify those attributes
+ * permitted by the associated capability bit. This can be used
+ * by Java 3D as a hint to enable certain optimizations that it
+ * might otherwise avoid, for example, optimizations that could
+ * cause those accesses or modifications to be expensive.
+ *
+ * <p>
+ * Unlike clearCapability, this method may be called on a live scene
+ * graph object (but not on a compiled object).
+ *
+ * <p>
+ * Note that only one isFrequent bit, for a single capability bit,
+ * may be cleared per method invocation--capability bits cannot be ORed
+ * together.
+ *
+ * @param bit the capability bit for which to clear the associated
+ * isFrequent bit
+ *
+ * @exception RestrictedAccessException if this object is part of a
+ * compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public final void clearCapabilityIsFrequent(int bit) {
+ if (isCompiled())
+ throw new RestrictedAccessException(J3dI18N.getString("SceneGraphObject1"));
+
+ capabilityIsFrequentBits &= ~(1L << bit);
+ retained.handleFrequencyChange(bit);
+ }
+
+
+ /**
+ * Sets an internal flag which indicates that this scene graph object
+ * has been compiled.
+ */
+ final void setCompiled() {
+ this.compiled = true;
+ this.liveOrCompiled = this.live || this.compiled;
+ }
+
+ /**
+ * Returns a flag indicating whether the node is part of a scene graph
+ * that has been compiled. If so, then only those capabilities explicitly
+ * allowed by the object's capability bits are allowed.
+ * @return true if node is part of a compiled scene graph, else false
+ */
+
+ public final boolean isCompiled() {
+ return this.compiled;
+ }
+
+ /**
+ * Sets an internal flag which indicates that this scene graph object
+ * is part of a live scene graph.
+ */
+ final void setLive() {
+ this.live = true;
+ this.liveOrCompiled = this.live || this.compiled;
+ }
+
+ /**
+ * Clears an internal flag which indicates that this scene graph object
+ * is no longer part of a live scene graph.
+ */
+ final void clearLive() {
+ this.live = false;
+ this.liveOrCompiled = this.live || this.compiled;
+ }
+
+ /**
+ * Returns a flag indicating whether the node is part of a live
+ * scene graph.
+ * @return true if node is part of a live scene graph, else false
+ */
+ public final boolean isLive() {
+ return this.live;
+ }
+
+ /**
+ * Returns a flag indicating whether the node is part of a live
+ * scene graph or a compiled scene graph.
+ * @return true if either live or compiled
+ */
+ final boolean isLiveOrCompiled() {
+ return liveOrCompiled;
+ }
+
+ final void checkForLiveOrCompiled() {
+ if (isLiveOrCompiled())
+ throw new RestrictedAccessException(J3dI18N.getString("SceneGraphObject2"));
+ }
+
+ /**
+ * Sets the userData field associated with this scene graph object.
+ * The userData field is a reference to an arbitrary object
+ * and may be used to store any user-specific data associated
+ * with this scene graph object--it is not used by the Java 3D API.
+ * If this object is cloned, the userData field is copied
+ * to the newly cloned object.
+ * @param userData a reference to the new userData field
+ */
+ public void setUserData(Object userData) {
+ this.userData = userData;
+ }
+
+ /**
+ * Retrieves the userData field from this scene graph object.
+ * @return the current userData field
+ */
+ public Object getUserData() {
+ return this.userData;
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced by that node have been duplicated via a call to
+ * <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf
+ * node and cloned NodeComponent's method
+ * will be called and the Leaf node/NodeComponent can then look up
+ * any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ }
+
+
+ /**
+ * Copies all SceneGraphObject 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>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @param originalNode the original node to duplicate.
+ *
+ * @see Group#cloneNode
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ protected void duplicateSceneGraphObject(SceneGraphObject originalNode) {
+ // Duplicate any class specific data here.
+ capabilityBits = originalNode.capabilityBits;
+ userData = originalNode.userData;
+ }
+
+
+ /**
+ * If <code>forceDuplicate</code> is <code>true</code> or
+ * <code>duplicateOnCloneTree</code> flag is true. This procedure
+ * will return a clone of originalNode or the value in
+ * in <code>nodeHashtable</code> if found. Otherwise return
+ * <code>originalNode</code>
+ *
+ * This method is called from the
+ * <code>duplicateAttributes</code> method during cloneNodeComponent.
+ *
+ * @param originalNodeComponent 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.
+ * @param nodeHashtable is used to keep track of mapping between old and
+ * new node references.
+ */
+ NodeComponent getNodeComponent(NodeComponent originalNodeComponent,
+ boolean forceDuplicate,
+ Hashtable hashtable) {
+ if ((originalNodeComponent != null) &&
+ (forceDuplicate ||
+ originalNodeComponent.duplicateChild())) {
+ NodeComponent nc = (NodeComponent)
+ hashtable.get(originalNodeComponent);
+ if (nc == null) {
+ originalNodeComponent.nodeHashtable = hashtable;
+ try {
+ nc = originalNodeComponent.
+ cloneNodeComponent(forceDuplicate);
+ } catch (RuntimeException e) {
+ // must reset nodeHashtable in any case
+ originalNodeComponent.nodeHashtable = null;
+ throw e;
+ }
+ originalNodeComponent.nodeHashtable = null;
+ // put link to be shared by other Node
+ hashtable.put(originalNodeComponent, nc);
+ } // use the share clone node otherwise
+ return nc;
+ } else {
+ return originalNodeComponent;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SceneGraphObjectRetained.java b/src/classes/share/javax/media/j3d/SceneGraphObjectRetained.java
new file mode 100644
index 0000000..b776be0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SceneGraphObjectRetained.java
@@ -0,0 +1,170 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+import java.util.Hashtable;
+
+/**
+ * SceneGraphObjectRetained is a superclass, which has things that
+ * are common to all retained scene graph component objects.
+ */
+abstract class SceneGraphObjectRetained extends IndexedObject
+ implements Cloneable {
+
+ // The object which created this retained mode object
+ SceneGraphObject source;
+
+ // This boolean is true when the object is in a Background BranchGroup
+ boolean inBackgroundGroup = false;
+
+ // This boolean is true when the object is in the update list
+ boolean onUpdateList = false;
+
+ // A flag to indicate if the node is in setLive, note that
+ // since the live is set to true only at the end, this flag
+ // is need for scoping to mark the nodes that are inSetLive
+
+ boolean inSetLive = false;
+
+ // A flag used in compile to indicate if this node needs to go
+ // through the second pass
+
+ final static int DONT_MERGE = 0;
+ final static int MERGE = 1;
+ final static int MERGE_DONE = 2;
+
+ int mergeFlag = 0;
+
+ /**
+ * Caches the source object that created this retained mode object.
+ * @param source the object which created this retained mode object.
+ */
+ void setSource(SceneGraphObject source) {
+ this.source = source;
+ }
+
+ /**
+ * Returns the cached source object that created this retained mode
+ * object.
+ * @return the object which created this retained mode object.
+ */
+ SceneGraphObject getSource() {
+ return this.source;
+ }
+
+ void markAsLive() {
+ this.source.setLive();
+ inSetLive = false;
+ }
+
+ void setLive(boolean inBackgroundGroup) {
+ doSetLive(inBackgroundGroup);
+ markAsLive();
+ }
+ boolean isInSetLive() {
+ return inSetLive;
+ }
+
+ /**
+ * Makes the internal node live.
+ */
+ void doSetLive(boolean inBackgroundGroup) {
+ inSetLive = true;
+ this.inBackgroundGroup = inBackgroundGroup;
+ }
+
+ void setLive(SetLiveState s) {
+ doSetLive(s);
+ markAsLive();
+ }
+
+ /**
+ * Makes the internal node live.
+ */
+ void doSetLive(SetLiveState s) {
+ inSetLive = true;
+ inBackgroundGroup = s.inBackgroundGroup;
+ }
+
+ /**
+ * Makes the internal node not live
+ */
+ void clearLive(VirtualUniverse univ, int index,
+ boolean sharedGroup, HashKey [] keys) {
+ inBackgroundGroup = false;
+ this.source.clearLive();
+ }
+
+ /**
+ * Makes the internal node not live
+ */
+ void clearLive() {
+ inBackgroundGroup = false;
+ this.source.clearLive();
+ }
+
+ /**
+ * This marks this object as compiled.
+ */
+ void setCompiled() {
+ this.source.setCompiled();
+ }
+
+
+ /**
+ * This is the default compile() method, which just marks the sgo as
+ * compiled.
+ */
+ void compile(CompileState compState) {
+ setCompiled();
+ }
+
+ void merge(CompileState compState) {
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ }
+
+ void traverse(boolean sameLevel, int level) {
+
+ System.out.println();
+ for (int i = 0; i < level; i++) {
+ System.out.print(".");
+ }
+ System.out.print(this);
+ }
+
+ /**
+ * true if component can't be read or written after compile or setlive()
+ */
+ boolean isStatic() {
+ return source.capabilityBitsEmpty();
+ }
+
+ protected Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+ void handleFrequencyChange(int bit) {
+ }
+
+ VirtualUniverse getVirtualUniverse() {
+ return null;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SceneGraphPath.java b/src/classes/share/javax/media/j3d/SceneGraphPath.java
new file mode 100644
index 0000000..123d3b8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SceneGraphPath.java
@@ -0,0 +1,652 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Point3d;
+import javax.vecmath.Point4d;
+
+/**
+ * A SceneGraphPath object represents the path from a Locale to a
+ * terminal node in the scene graph. This path consists of a Locale, a
+ * terminal node, and an array of internal nodes that are in the path
+ * from the Locale to the terminal node. The terminal node may be
+ * either a Leaf node or a Group node. A valid SceneGraphPath must
+ * uniquely identify a specific instance of the terminal node. For
+ * nodes that are not under a SharedGroup, the minimal SceneGraphPath
+ * consists of the Locale and the terminal node itself. For nodes that
+ * are under a SharedGroup, the minimal SceneGraphPath consists of the
+ * Locale, the terminal node, and a list of all Link nodes in the path
+ * from the Locale to the terminal node. A SceneGraphPath may optionally
+ * contain other interior nodes that are in the path.
+ * A SceneGraphPath is verified for correctness and uniqueness when
+ * it is sent as an argument to other methods of Java 3D.
+ * <p>
+ * In the array of internal nodes, the node at index 0 is the node
+ * closest to the Locale. The indices increase along the path to the
+ * terminal node, with the node at index length-1 being the node closest
+ * to the terminal node. The array of nodes does not contain either the
+ * Locale (which is not a node) or the terminal node.
+ * <p>
+ * When a SceneGraphPath is returned from the picking or collision
+ * methods of Java 3D, it will also contain the value of the
+ * LocalToVworld transform of the terminal node that was in effect at
+ * the time the pick or collision occurred.
+ * Note that ENABLE_PICK_REPORTING and ENABLE_COLLISION_REPORTING are
+ * disabled by default. This means that the picking and collision
+ * methods will return the minimal SceneGraphPath by default.
+ *
+ * @see Node#ENABLE_PICK_REPORTING
+ * @see Node#ENABLE_COLLISION_REPORTING
+ * @see BranchGroup#pickAll
+ * @see BranchGroup#pickAllSorted
+ * @see BranchGroup#pickClosest
+ * @see BranchGroup#pickAny
+ */
+
+public class SceneGraphPath {
+
+ Locale root = null;
+ Node[] interior = null;
+ Node item = null;
+ Transform3D transform = new Transform3D();
+
+ // Intersect Point for item when picked
+ Point3d intersectPoint = new Point3d();
+
+ double pickDistance; // distance to pick location
+
+ /**
+ * Constructs a SceneGraphPath object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * root : null<br>
+ * object : null<br>
+ * list of (interior) nodes : null<br>
+ * transform : identity<br>
+ * </ul>
+ */
+ public SceneGraphPath() {
+ // Just use defaults
+ }
+
+ /**
+ * Constructs a new SceneGraphPath object.
+ * @param root the Locale object of this path
+ * @param object the terminal node of this path
+ */
+ public SceneGraphPath(Locale root, Node object) {
+
+ this.item = object;
+ this.root = root;
+ }
+
+ /**
+ * Constructs a new SceneGraphPath object.
+ * @param root the Locale object of this path
+ * @param nodes an array of node objects in the path from
+ * the Locale to the terminal node
+ * @param object the terminal node of this path
+ */
+ public SceneGraphPath(Locale root, Node nodes[], Node object) {
+
+ this.item = object;
+ this.root = root;
+ this.interior = new Node[nodes.length];
+ for (int i = 0; i < nodes.length; i++)
+ this.interior[i] = nodes[i];
+ }
+
+
+ /**
+ * Constructs a new SceneGraphPath object
+ * @param sgp the SceneGraphPath to copy from
+ */
+ SceneGraphPath(SceneGraphPath sgp) {
+ set(sgp);
+ }
+
+ /**
+ * Sets this path's values to that of the specified path.
+ * @param newPath the SceneGraphPath to copy
+ */
+ public final void set(SceneGraphPath newPath) {
+ this.root = newPath.root;
+ this.item = newPath.item;
+ this.transform.set(newPath.transform);
+ if(newPath.interior != null && newPath.interior.length > 0) {
+ interior = new Node[newPath.interior.length];
+ for (int i = 0; i < interior.length; i++)
+ this.interior[i] = newPath.interior[i];
+ }
+ else
+ interior = null;
+ }
+
+ /**
+ * Sets this path's Locale to the specified Locale.
+ * @param newLocale The new Locale
+ */
+ public final void setLocale(Locale newLocale) {
+ root = newLocale;
+ }
+
+ /**
+ * Sets this path's terminal node to the specified node object.
+ * @param object the new terminal node
+ */
+ public final void setObject(Node object) {
+ this.item = object;
+ }
+
+ /**
+ * Sets this path's node objects to the specified node objects.
+ * @param nodes an array of node objects in the path from
+ * the Locale to the terminal node
+ */
+ public final void setNodes(Node nodes[]) {
+
+ if(nodes != null && nodes.length > 0) {
+ interior = new Node[nodes.length];
+ for (int i = 0; i < nodes.length; i++)
+ this.interior[i] = nodes[i];
+ }
+ else
+ interior = null;
+ }
+
+ /**
+ * Replaces the node at the specified index with newNode.
+ * @param index the index of the node to replace
+ * @param newNode the new node
+ * @exception NullPointerException if the node array pointer is null.
+ *
+ */
+ public final void setNode(int index, Node newNode) {
+ if(interior == null)
+ throw new NullPointerException(J3dI18N.getString("SceneGraphPath0"));
+
+ interior[index] = newNode;
+ }
+
+ /**
+ * Sets the transform component of this SceneGraphPath to the value of
+ * the passed transform.
+ * @param trans the transform to be copied. trans should be the
+ * localToVworld matrix of this SceneGraphPath object.
+ */
+ public final void setTransform(Transform3D trans) {
+ transform.set(trans);
+ }
+
+ /**
+ * Returns a copy of the transform associated with this SceneGraphPath;
+ * returns null if there is no transform associated.
+ * If this SceneGraphPath was returned by a Java 3D picking or
+ * collision method, the local coordinate to virtual world
+ * coordinate transform for this scene graph object at the
+ * time of the pick or collision is recorded.
+ * @return the local to VWorld transform
+ */
+ public final Transform3D getTransform() {
+ return new Transform3D(transform);
+ }
+
+ /**
+ * Retrieves the path's Locale
+ * @return this path's Locale
+ */
+ public final Locale getLocale() {
+ return this.root;
+ }
+
+ /**
+ * Retrieves the path's terminal node object.
+ * @return the terminal node
+ */
+ public final Node getObject() {
+ return this.item;
+ }
+
+ /**
+ * Retrieves the number of nodes in this path. The number of nodes
+ * does not include the Locale or the terminal node object itself.
+ * @return a count of the number of nodes in this path
+ */
+ public final int nodeCount() {
+ if(interior == null)
+ return 0;
+ return interior.length;
+ }
+
+ /**
+ * Retrieves the node at the specified index.
+ * @param index the index specifying which node to retrieve
+ * @return the specified node
+ */
+ public final Node getNode(int index) {
+ if(interior == null)
+ throw new
+ ArrayIndexOutOfBoundsException(J3dI18N.getString("SceneGraphPath1"));
+ return interior[index];
+ }
+
+ /**
+ * Returns true if all of the data members of path testPath are
+ * equal to the corresponding data members in this SceneGraphPath and
+ * if the values of the transforms is equal.
+ * @param testPath the path we will compare this object's path against.
+ * @return true or false
+ */
+ public boolean equals(SceneGraphPath testPath) {
+ boolean result = true;
+ try {
+
+ if(testPath == null || root != testPath.root || item != testPath.item)
+ return false;
+
+ result = transform.equals(testPath.transform);
+
+ if(result == false)
+ return false;
+
+ if(interior == null || testPath.interior == null) {
+ if(interior != testPath.interior)
+ return false;
+ else
+ result = (root == testPath.root && item == testPath.item);
+
+ } else {
+ if (interior.length == testPath.interior.length) {
+ for (int i = 0; i < interior.length; i++)
+ if (interior[i] != testPath.interior[i]) {
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+
+ }
+ catch (NullPointerException e2) {return false;}
+
+ return result;
+ }
+
+ /**
+ * Returns true if the Object o1 is of type SceneGraphPath and all of the
+ * data members of o1 are equal to the corresponding data members in
+ * this SceneGraphPath and if the values of the transforms is equal.
+ * @param o1 the object we will compare this SceneGraphPath's path against.
+ * @return true or false
+ */
+ public boolean equals(Object o1) {
+ boolean result = true;
+
+ try {
+ SceneGraphPath testPath = (SceneGraphPath)o1;
+ if(testPath == null || root != testPath.root || item != testPath.item)
+ return false;
+
+ result = transform.equals(testPath.transform);
+
+ if(result == false)
+ return false;
+
+ if(interior == null || testPath.interior == null) {
+ if(interior != testPath.interior)
+ return false;
+ else
+ result = (root == testPath.root && item == testPath.item);
+
+ } else {
+ if (interior.length == testPath.interior.length) {
+ for (int i = 0; i < interior.length; i++)
+ if (interior[i] != testPath.interior[i]) {
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+
+ return result;
+ }
+ catch (NullPointerException e2) {return false;}
+ catch (ClassCastException e1) {return false;}
+ }
+
+
+ /**
+ * Returns a hash number based on the data values in this
+ * object. Two different SceneGraphPath objects with identical data
+ * values (ie, returns true for trans.equals(SceneGraphPath) ) will
+ * return the same hash number. Two Paths with different data members
+ * may return the same hash value, although this is not likely.
+ * @return the integer hash value
+ */
+ public int hashCode() {
+ HashKey key = new HashKey(250);
+ // NOTE: Needed to add interior != null because this method is called
+ // by object.toString() when interior is null.
+ if(interior != null && item != null) {
+ for(int i=0; i<interior.length; i++) {
+ key.append(LinkRetained.plus).append( item.toString() );
+ }
+ }
+ return( key.hashCode() + transform.hashCode() );
+ }
+
+ /**
+ * Determines whether two SceneGraphPath objects represent the same
+ * path in the scene graph; either object might include a different
+ * subset of internal nodes; only the internal link nodes, the Locale,
+ * and the Node itself are compared. The paths are not validated for
+ * correctness or uniqueness.
+ * @param testPath the SceneGraphPath to be compared to this SceneGraphPath
+ * @return true or false
+ */
+ public final boolean isSamePath(SceneGraphPath testPath) {
+ int count=0, i;
+
+ if(testPath == null || testPath.item != this.item || root != testPath.root)
+ return false;
+
+ if(interior != null && testPath.interior != null) {
+ for(i=0 ; i<interior.length ; i++) {
+ if(interior[i] instanceof Link) {
+ // found Link in this, now check for matching in testPath
+ while(count < testPath.interior.length) {
+ if(testPath.interior[count] instanceof Link) {
+ if(testPath.interior[count] != interior[i]) {
+ return false;
+ }
+ count++;
+ break;
+ }
+ count++;
+ // if true, this had an extra Link
+ if(count == testPath.interior.length)
+ return false;
+ }
+ }
+ }
+ // make sure testPath doesn't have any extra Links
+ while(count < testPath.interior.length) {
+ if(testPath.interior[count] instanceof Link)
+ return false;
+ count++;
+ }
+ } else if(interior != testPath.interior) // ==> they are not both null
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this object;
+ * the string contains the class names of all Nodes in the SceneGraphPath,
+ * the toString() method of any associated user data provided by
+ * SceneGraphObject.getUserData(), and also prints out the transform,
+ * if it is not null.
+ * @return String representation of this object
+ */
+ public String toString() {
+
+ StringBuffer str = new StringBuffer();
+ Object obj;
+
+ if(root == null && interior == null && item == null)
+ return (super.toString());
+
+ if(root != null)
+ str.append(root + " : ");
+
+ if(interior != null) {
+ for(int i=0; i<interior.length; i++) {
+
+ str.append( interior[i].getClass().getName());
+ obj = interior[i].getUserData();
+ if(obj == null)
+ str.append(" : ");
+ else
+ str.append(", " + obj + " : ");
+ }
+ }
+
+ if(item != null) {
+ // str.append( item + ", "+ item.getUserData() + "--"+intersectPoint );
+ str.append( item.getClass().getName() );
+ obj = item.getUserData();
+ if(obj != null)
+ str.append(", " + obj);
+
+ try {
+ if (item.getClass().getName().equals("javax.media.j3d.Shape3D"))
+ str.append( ((Shape3D)item).getGeometry() );
+ }
+ catch( CapabilityNotSetException e) {}
+ }
+
+ str.append("\n" + "LocalToVworld Transform:\n" + transform);
+
+ return new String(str);
+ }
+
+ /**
+ * Determine if this SceneGraphPath is unique and valid
+ * The graph don't have to be live for this checking.
+ * Set Locale when it is null.
+ * Only the essential link node which led to the Locale
+ * is validated.
+ */
+ boolean validate() {
+ NodeRetained node = (NodeRetained) item.retained;
+
+ Locale locale = node.locale;
+
+ if (root != null) {
+ if (item.isLive()) {
+ if (locale != root) {
+ return false;
+ }
+ }
+ } else {
+ root = locale;
+ }
+
+ int idx = (interior == null ? 0: interior.length);
+
+ do {
+ if (node instanceof SharedGroupRetained) {
+ if (interior == null)
+ return false;
+ while (--idx > 0) {
+ if (((SharedGroupRetained)
+ node).parents.contains(interior[idx].retained)) {
+ break;
+ }
+ }
+ if (idx < 0) {
+ return false;
+ }
+ node = (NodeRetained) interior[idx].retained;
+ } else {
+ node = node.parent;
+ }
+ } while (node != null);
+
+ return true;
+ }
+
+
+ // return key of this path or null is not in SharedGroup
+ void getHashKey(HashKey key) {
+ if (interior != null) {
+ key.reset();
+ key.append(root.nodeId);
+ for(int i=0; i<interior.length; i++) {
+ Node node = interior[i];
+
+ if (!node.isLive()) {
+ throw new RuntimeException(J3dI18N.getString("SceneGraphPath3"));
+ }
+
+ NodeRetained nodeR = (NodeRetained) node.retained;
+ if (nodeR.nodeType == NodeRetained.LINK) {
+ key.append("+").append(nodeR.nodeId);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines whether this SceneGraphPath is unique and valid. The
+ * verification determines that all of the nodes are live, that the
+ * specified path is unique, that the correct Locale is specified, and
+ * that there is a Node specified.
+ */
+ boolean validate(HashKey key) {
+
+ int i;
+
+ // verify that there is at least a Locale and Node specified
+ if( root == null )
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath2"));
+
+ if( item == null )
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath10"));
+
+ // verify liveness
+ if( !item.isLive() )
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath3"));
+
+ try {
+ getHashKey(key);
+ } catch (RuntimeException ex) {
+ throw new IllegalArgumentException(ex.getMessage());
+ }
+
+ // The rest of the code verifies uniqueness; it traverses the retained
+ // hierarchy of the scene graph. This could be problematic later in
+ // when certain compile mode optimizations are added. */
+
+ NodeRetained bottomNR, currentNR, nextNR=null;
+ Node currentNode;
+ int count = 0;
+
+ // Need to traverse the retained hierarchy on a live scene graph
+ // from bottom to top
+ //
+ // bottomNR = last verified node; as nodes are verified, bottomNR
+ // moves up the scen graph
+ // nextNR = Next node that the user has specified after bottomNR
+ // currentNR = current node; is changing as it covers all the
+ // nodes from bottomNR to nextNR
+
+ // If the parent of a NodeRetained is null, we know that the parent
+ // is either a BranchGroupRetained at the top of a scene graph or
+ // it is a SharedGroupRetained, potentially with multiple parents.
+
+ bottomNR = (NodeRetained)(item.retained);
+
+ if(interior != null) {
+ for(i=interior.length-1; i >=0 ; i--) {
+ nextNR = (NodeRetained)(interior[i].retained);
+ currentNR = bottomNR.parent;
+ if(currentNR == null && bottomNR instanceof SharedGroupRetained) {
+ if(((SharedGroupRetained)(bottomNR)).parents.contains(nextNR) )
+ currentNR = nextNR;
+ else
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5"));
+
+ }
+
+ while(currentNR != nextNR) {
+ if(currentNR == null) {
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath11"));
+ }
+
+ if(currentNR instanceof SharedGroupRetained) {
+ if(((SharedGroupRetained)
+ (currentNR)).parents.contains(nextNR) )
+ currentNR = nextNR;
+ else
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5"));
+
+ } else {
+ currentNR = currentNR.parent;
+ }
+ }
+ bottomNR = currentNR;
+ }
+ }
+
+ // Now go from bottomNR to Locale
+ currentNR = bottomNR.parent;
+ if(currentNR == null && bottomNR instanceof SharedGroupRetained) {
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5"));
+ }
+
+ while(currentNR != null) {
+ if(currentNR instanceof LinkRetained) {
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5"));
+ }
+
+ bottomNR = currentNR;
+ currentNR = currentNR.parent;
+ if(currentNR == null && bottomNR instanceof SharedGroupRetained) {
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5"));
+ }
+ }
+
+ // get the real BranchGroup from the BranchGroupRetained
+ currentNode = (Node)(bottomNR.source);
+ // now bottomNR should be a BranchGroup -- should try an assert here
+ if(!root.branchGroups.contains(currentNode)) {
+ throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath9"));
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the distance from the intersectPoint for item and
+ * origin.
+ */
+ double getDistanceFrom( Point3d origin ) {
+ return intersectPoint.distance(origin);
+ }
+
+ /**
+ * Returns the distance of the pick
+ */
+ double getDistance() {
+ return pickDistance;
+ }
+
+ final void setIntersectPoint( Point3d point ) {
+ intersectPoint.set(point);
+ }
+
+ final void setIntersectPointDis( Point4d pickLocation ) {
+ // System.out.println( "setIntersectPointDis pickLocation= "+pickLocation);
+ intersectPoint.x = pickLocation.x;
+ intersectPoint.y = pickLocation.y;
+ intersectPoint.z = pickLocation.z;
+ pickDistance = pickLocation.w;
+ }
+
+ final Point3d getIntersectPoint() {
+ return intersectPoint;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Screen3D.java b/src/classes/share/javax/media/j3d/Screen3D.java
new file mode 100644
index 0000000..dc9cf82
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Screen3D.java
@@ -0,0 +1,490 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+/**
+ * The Screen3D Object contains all information about a particular screen.
+ * All Canvas3D objects on the same physical screen (display device)
+ * refer to the same Screen3D object. Note that Screen3D has no public
+ * constructors--it is obtained from the Canvas3D via the getScreen3D
+ * method.
+ * <p>
+ * Default values for Screen3D parameters are as follows:
+ * <ul>
+ * physical screen width : 0.0254/90.0 * screen width (in pixels)<br>
+ * physical screen height : 0.0254/90.0 * screen height (in pixels)<br>
+ * tracker base to image plate transform : identity<br>
+ * head tracker to left image plate transform : identity<br>
+ * head tracker to right image plate transform : identity<br>
+ * off-screen size : (0,0)<br>
+ * </ul>
+ * <P>
+ * <b>Offscreen Rendering</b><P>
+ * New for Java 3D 1.2, an off-screen rendering mode allows rendering
+ * to a memory image, which is possibly larger than the screen. The
+ * setSize and getSize methods are defined specifically for this
+ * mode. Note that the off-screen size, physical width, and physical height
+ * must be set prior to rendering
+ * to the associated off-screen canvas. Failure to do so will result
+ * in an exception.<P>
+ * <b>Calibration Parameters</b><P>
+ * The Screen3D object must be calibrated with the coexistence volume.
+ * The Screen3D class provides several methods for defining the
+ * calibration parameters.<P>
+ * <UL>Measured Parameters<P>
+ * The screen's (image plate's) physical width and height (in meters)
+ * is set once, typically by a browser, calibration program, system
+ * administrator, or system calibrator, not by an applet. These values
+ * must be determined by measuring the display's active image width
+ * and height. In the case of a head-mounted display, this should be
+ * the display's apparent width and height at the focal plane. These
+ * values are defined by the setPhysicalScreenWidth and
+ * setPhysicalScreenHeight methods.<P>
+ *
+ * Head-tracker Coordinate System<P>
+ * If head tracking is enabled, one of two parameters need to be specified:<P>
+ * <UL><LI>If the view policy is SCREEN_VIEW, the tracker-base-to-image-plate
+ * coordinate system must be specified (setTrackerBaseToImagePlate method).
+ * This coordinate system must be recalibrated whenever the image
+ * plate moves relative to the tracker.</LI><P>
+ *
+ * <LI>If the view policy is HMD_VIEW, the head-tracker-to-left-image-plate
+ * and head-tracker-to-right-image-plate coordinate systems must be
+ * specified (setHeadTrackerToLeftImagePlate and
+ * setHeadTrackerToRightImagePlate methods).</LI><P></UL>
+ * </UL><P>
+ * @see Canvas3D
+ * @see Canvas3D#getScreen3D
+ */
+
+public class Screen3D extends Object {
+ private static final boolean debug = false;
+
+ // Assume a default of 90 DPI: 90 pix/inch = 1/90 inch/pix =
+ // 0.0254/90 meter/pix
+ private static final double METERS_PER_PIXEL = 0.0254/90.0;
+
+ // GraphicsDevice associated with this Screen3D object. Note that
+ // all on-screen Canvas3D objects that are created on the same
+ // GraphicsDevice will share the same Screen3D.
+ GraphicsDevice graphicsDevice;
+
+ // Flag indicating whether this Screen3D is associated with
+ // an off-screen Canvas3D or with one or more on-screen Canvas3Ds
+ boolean offScreen;
+
+ // The display connection (X11 only) and the screen ID
+ long display;
+ int screen;
+
+ // The width and height of the screen in meters.
+ double physicalScreenWidth;
+ double physicalScreenHeight;
+
+ // Screen size in pixels
+ Dimension screenSize = new Dimension(0, 0);
+
+ //
+ // Tracker-base coordinate system to image-plate coordinate
+ // system transform. This transform
+ // is typically a calibration constant.
+ // This is used only in SCREEN_VIEW mode.
+ //
+ Transform3D trackerBaseToImagePlate = new Transform3D();
+
+ //
+ // Head-tracker coordinate system to left and right image-plate
+ // coordinate system transforms. These transforms are typically
+ // calibration constants. These are used only in HMD_VIEW mode.
+ //
+ Transform3D headTrackerToLeftImagePlate = new Transform3D();
+ Transform3D headTrackerToRightImagePlate = new Transform3D();
+
+
+ // Physical screen size related field has changed.
+ static final int PHYSICAL_SCREEN_SIZE_DIRTY = 0x01;
+ // Screen size field has changed.
+ static final int SCREEN_SIZE_DIRTY_DIRTY = 0x02;
+ // Tracker base to image plate field has changed.
+ static final int TRACKER_BASE_TO_IMAGE_PLATE_DIRTY = 0x04;
+ // Head tracker to image plate field has changed.
+ static final int HEAD_TRACKER_TO_IMAGE_PLATE_DIRTY = 0x08;
+
+ // Mask that indicates this Screen3D view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int scrDirtyMask = (PHYSICAL_SCREEN_SIZE_DIRTY | SCREEN_SIZE_DIRTY_DIRTY
+ | TRACKER_BASE_TO_IMAGE_PLATE_DIRTY
+ | HEAD_TRACKER_TO_IMAGE_PLATE_DIRTY);
+
+ //
+ // View cache for this screen
+ //
+ ScreenViewCache screenViewCache = null;
+
+ // The renderer for this screen
+ Renderer renderer = null;
+
+ // Hashtable that maps a GraphicsDevice to its associated renderer
+ static Hashtable deviceRendererMap = new Hashtable();
+
+ // A count of the number of canvases associated with this screen
+ int canvasCount = 0;
+
+ // A count of the number of active View associated with this screen
+ UnorderList activeViews = new UnorderList(1, View.class);
+
+ // A list of Canvas3D Objects that refer to this
+ ArrayList users = new ArrayList();
+
+ void addActiveView(View v) {
+ activeViews.addUnique(v);
+ }
+
+ void removeActiveView(View v) {
+ activeViews.remove(v);
+ }
+
+ boolean activeViewEmpty() {
+ return activeViews.isEmpty();
+ }
+
+ // Add a user to the list of users
+ synchronized void removeUser(Canvas3D c) {
+ int idx = users.indexOf(c);
+ if (idx >= 0) {
+ users.remove(idx);
+ }
+ }
+
+ // Add a user to the list of users
+ synchronized void addUser(Canvas3D c) {
+ int idx = users.indexOf(c);
+ if (idx < 0) {
+ users.add(c);
+ }
+ }
+
+ // Add a user to the list of users
+ synchronized void notifyUsers() {
+ int i;
+ Canvas3D c;
+
+ for (i=0; i<users.size(); i++) {
+ c = (Canvas3D)users.get(i);
+ c.redraw();
+ }
+ }
+
+ /**
+ * Retrieves the width and height (in pixels) of this Screen3D.
+ *
+ * @return a new Dimension object containing the width and height
+ * of this Screen3D.
+ */
+ public Dimension getSize() {
+ return new Dimension(screenSize);
+ }
+
+ /**
+ * Retrieves the width and height (in pixels) of this Screen3D
+ * and copies it into the specified Dimension object.
+ *
+ * @param rv Dimension object into which the size of
+ * this Screen3D is copied.
+ * If <code>rv</code> is null, a new Dimension object is allocated.
+ *
+ * @return <code>rv</code>
+ *
+ * @since Java 3D 1.2
+ */
+ public Dimension getSize(Dimension rv) {
+ if (rv == null) {
+ return new Dimension(screenSize);
+ }
+ else {
+ rv.setSize(screenSize);
+ return rv;
+ }
+ }
+
+ /**
+ * Sets the width and height (in pixels) of this off-screen Screen3D.
+ * The default size for off-screen Screen3D objects is (0,0).
+ * <br>
+ * NOTE: the size must be
+ * set prior to rendering to the associated off-screen canvas.
+ * Failure to do so will result in an exception.
+ *
+ * @param width the new width of this Screen3D object
+ * @param height the new height of this Screen3D object
+ *
+ * @exception IllegalStateException if this Screen3D is not in
+ * off-screen mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setSize(int width, int height) {
+
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Screen3D1"));
+
+ synchronized(this) {
+ screenSize.width = width;
+ screenSize.height = height;
+ scrDirtyMask |= SCREEN_SIZE_DIRTY_DIRTY;
+ }
+ }
+
+ /**
+ * Sets the width and height (in pixels) of this off-screen Screen3D.
+ * The default size for off-screen Screen3D objects is (0,0).
+ * <br>
+ * NOTE: the size must be
+ * set prior to rendering to the associated off-screen canvas.
+ * Failure to do so will result in an exception.
+ *
+ * @param d the new dimension of this Screen3D object
+ *
+ * @exception IllegalStateException if this Screen3D is not in
+ * off-screen mode.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setSize(Dimension d) {
+ if (!offScreen)
+ throw new IllegalStateException(J3dI18N.getString("Screen3D1"));
+
+ synchronized(this) {
+ screenSize.width = d.width;
+ screenSize.height = d.height;
+ scrDirtyMask |= SCREEN_SIZE_DIRTY_DIRTY;
+ }
+ }
+
+ /**
+ * Sets the screen physical width in meters. In the case of a
+ * head-mounted display, this should be the apparent width
+ * at the focal plane.
+ * @param width the screen's physical width in meters
+ */
+ public void setPhysicalScreenWidth(double width) {
+ synchronized(this) {
+ physicalScreenWidth = width;
+ scrDirtyMask |= PHYSICAL_SCREEN_SIZE_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the screen's physical width in meters.
+ * @return the screen's physical width in meters
+ */
+ public double getPhysicalScreenWidth() {
+ return physicalScreenWidth;
+ }
+
+ /**
+ * Sets the screen physical height in meters. In the case of a
+ * head-mounted display, this should be the apparent height
+ * at the focal plane.
+ * @param height the screen's physical height in meters
+ */
+ public void setPhysicalScreenHeight(double height) {
+ synchronized(this) {
+ physicalScreenHeight = height;
+ scrDirtyMask |= PHYSICAL_SCREEN_SIZE_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the the screen's physical height in meters.
+ * @return the screen's physical height in meters
+ */
+ public double getPhysicalScreenHeight() {
+ return physicalScreenHeight;
+ }
+
+ public String toString() {
+ return "Screen3D: size = " +
+ "(" + getSize().width + " x " + getSize().height + ")" +
+ ", physical size = " +
+ "(" + getPhysicalScreenWidth() + "m x " +
+ getPhysicalScreenHeight() + "m)";
+ }
+
+ // Static initializer for Screen3D class
+ static {
+ VirtualUniverse.loadLibraries();
+ }
+
+ /**
+ * Construct a new Screen3D object with the specified size in pixels.
+ * Note that currently, there is no AWT equivalent of screen so Java 3D
+ * users need to get this through the Canvas3D object (via getScreen()) if
+ * they need it.
+ * @param graphicsConfiguration the AWT graphics configuration associated
+ * with this Screen3D
+ * @param offScreen a flag that indicates whether this Screen3D is
+ * associated with an off-screen Canvas3D
+ */
+ Screen3D(GraphicsConfiguration graphicsConfiguration, boolean offScreen) {
+ NativeScreenInfo nativeScreenInfo;
+
+ this.offScreen = offScreen;
+ this.graphicsDevice = graphicsConfiguration.getDevice();
+
+ screenViewCache = new ScreenViewCache(this);
+ nativeScreenInfo = new NativeScreenInfo(graphicsDevice);
+
+ // Get the display from the native code (X11 only) and the
+ // screen ID
+ display = nativeScreenInfo.getDisplay();
+ screen = nativeScreenInfo.getScreen();
+
+ if (debug)
+ System.out.println("Screen3D: display " + display +
+ " screen " + screen + " hashcode " +
+ this.hashCode());
+
+ if (!offScreen) {
+ // Store the information in this screen object
+ Rectangle bounds = graphicsConfiguration.getBounds();
+ screenSize.width = bounds.width;
+ screenSize.height = bounds.height;
+ }
+
+ // Set the default physical size based on size in pixels
+ physicalScreenWidth = screenSize.width * METERS_PER_PIXEL;
+ physicalScreenHeight = screenSize.height * METERS_PER_PIXEL;
+ }
+
+
+ /**
+ * Sets the tracker-base coordinate system to image-plate coordinate
+ * system transform. This transform
+ * is typically a calibration constant.
+ * This is used only in SCREEN_VIEW mode.
+ * @param t the new transform
+ * @exception BadTransformException if the transform is not rigid
+ */
+ public void setTrackerBaseToImagePlate(Transform3D t) {
+ synchronized(this) {
+ if (!t.isRigid()) {
+ throw new BadTransformException(J3dI18N.getString("Screen3D0"));
+ }
+ trackerBaseToImagePlate.setWithLock(t);
+ scrDirtyMask |= Screen3D.TRACKER_BASE_TO_IMAGE_PLATE_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the tracker-base coordinate system to image-plate
+ * coordinate system transform and copies it into the specified
+ * Transform3D object.
+ * @param t the object that will receive the transform
+ */
+ public void getTrackerBaseToImagePlate(Transform3D t) {
+ t.set(trackerBaseToImagePlate);
+ }
+
+ /**
+ * Sets the head-tracker coordinate system to left image-plate coordinate
+ * system transform. This transform
+ * is typically a calibration constant.
+ * This is used only in HMD_VIEW mode.
+ * @param t the new transform
+ * @exception BadTransformException if the transform is not rigid
+ */
+ public void setHeadTrackerToLeftImagePlate(Transform3D t) {
+ synchronized(this) {
+ if (!t.isRigid()) {
+ throw new BadTransformException(J3dI18N.getString("Screen3D0"));
+ }
+ headTrackerToLeftImagePlate.setWithLock(t);
+ scrDirtyMask |= Screen3D.HEAD_TRACKER_TO_IMAGE_PLATE_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the head-tracker coordinate system to left image-plate
+ * coordinate system transform and copies it into the specified
+ * Transform3D object.
+ * @param t the object that will receive the transform
+ */
+ public void getHeadTrackerToLeftImagePlate(Transform3D t) {
+ t.set(headTrackerToLeftImagePlate);
+ }
+
+ /**
+ * Sets the head-tracker coordinate system to right image-plate coordinate
+ * system transform. This transform
+ * is typically a calibration constant.
+ * This is used only in HMD_VIEW mode.
+ * @param t the new transform
+ * @exception BadTransformException if the transform is not rigid
+ */
+ public void setHeadTrackerToRightImagePlate(Transform3D t) {
+ synchronized(this) {
+ if (!t.isRigid()) {
+ throw new BadTransformException(J3dI18N.getString("Screen3D0"));
+ }
+ headTrackerToRightImagePlate.setWithLock(t);
+ scrDirtyMask |= Screen3D.HEAD_TRACKER_TO_IMAGE_PLATE_DIRTY;
+ }
+ notifyUsers();
+ }
+
+ /**
+ * Retrieves the head-tracker coordinate system to right image-plate
+ * coordinate system transform and copies it into the specified
+ * Transform3D object.
+ * @param t the object that will receive the transform
+ */
+ public void getHeadTrackerToRightImagePlate(Transform3D t) {
+ t.set(headTrackerToRightImagePlate);
+ }
+
+ /**
+ * Update the view cache associated with this screen.
+ */
+ void updateViewCache() {
+ if (false)
+ System.out.println("Screen3D.updateViewCache()");
+ synchronized(this) {
+ screenViewCache.snapshot();
+ }
+ }
+
+ /**
+ * Increment canvas count, initialize renderer if needed
+ */
+ synchronized void incCanvasCount() {
+ canvasCount++;
+ }
+
+ /**
+ * Decrement canvas count, kill renderer if needed
+ */
+ synchronized void decCanvasCount() {
+ canvasCount--;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ScreenViewCache.java b/src/classes/share/javax/media/j3d/ScreenViewCache.java
new file mode 100644
index 0000000..a87dd3e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ScreenViewCache.java
@@ -0,0 +1,114 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.Dimension;
+import javax.vecmath.*;
+
+/**
+ * The ScreenViewCache class is used to cache all API data
+ * from the Screen3D object.
+ */
+class ScreenViewCache extends Object {
+ // The screen associated with this screen view cache
+ Screen3D screen;
+
+ //
+ // API/INPUT DATA
+ //
+
+ // The width and height of the screen in meters.
+ double physicalScreenWidth;
+ double physicalScreenHeight;
+
+ // The width and height of the screen in pixels.
+ int screenWidth;
+ int screenHeight;
+
+ // Mask that indicates Screen3D view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int scrvcDirtyMask = 0;
+
+ //
+ // Tracker-base coordinate system to image-plate coordinate
+ // system transform. If head tracking is enabled, this transform
+ // is a calibration constant. If head tracking is not enabled,
+ // this transform is not used.
+ // This is used only in SCREEN_VIEW mode.
+ //
+ Transform3D trackerBaseToImagePlate = new Transform3D();
+
+ //
+ // Head-tracker coordinate system to left and right image-plate coordinate
+ // system transforms. If head tracking is enabled, these transforms
+ // are calibration constants. If head tracking is not enabled,
+ // these transforms are not used.
+ // These are used only in HMD_VIEW mode.
+ //
+ Transform3D headTrackerToLeftImagePlate = new Transform3D();
+ Transform3D headTrackerToRightImagePlate = new Transform3D();
+
+
+ //
+ // DERIVED DATA
+ //
+
+ // Meters per pixel in the X and Y dimension
+ double metersPerPixelX;
+ double metersPerPixelY;
+
+
+ /**
+ * Take snapshot of all per-screen API parameters.
+ */
+ synchronized void snapshot() {
+
+ // accumulate the dirty bits for offscreen because
+ // the dirty bits will not be processed until renderOffScreen
+ // or triggered by RenderBin at some little time
+ if (screen.offScreen)
+ scrvcDirtyMask |= screen.scrDirtyMask;
+ else
+ scrvcDirtyMask = screen.scrDirtyMask;
+
+ screen.scrDirtyMask = 0;
+ physicalScreenWidth = screen.physicalScreenWidth;
+ physicalScreenHeight = screen.physicalScreenHeight;
+ screenWidth = screen.screenSize.width;
+ screenHeight = screen.screenSize.height;
+
+ screen.trackerBaseToImagePlate.getWithLock(trackerBaseToImagePlate);
+
+ screen.headTrackerToLeftImagePlate.getWithLock
+ (headTrackerToLeftImagePlate);
+ screen.headTrackerToRightImagePlate.getWithLock
+ (headTrackerToRightImagePlate);
+
+ // This isn't really API data, but since we have no other derived
+ // data, and it's a simple calculation, it's easier if we just do
+ // it here.
+ metersPerPixelX = physicalScreenWidth / (double) screenWidth;
+ metersPerPixelY = physicalScreenHeight / (double) screenHeight;
+ }
+
+
+ /**
+ * Constructs and initializes a ScreenViewCache object.
+ */
+ ScreenViewCache(Screen3D screen) {
+ this.screen = screen;
+
+ if (false)
+ System.out.println("Constructed a ScreenViewCache");
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Sensor.java b/src/classes/share/javax/media/j3d/Sensor.java
new file mode 100644
index 0000000..2f02485
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Sensor.java
@@ -0,0 +1,697 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The Sensor Class encapsulates an object that provides real-time
+ * data. Examples include six-degree-of-freedom tracking, a joystick,
+ * or a data file being read back during a program. A sensor must be
+ * used in conjuction with an implementation of the InputDevice
+ * interface.<P>
+ *
+ * The Sensor object provides an abstract concept of a hardware
+ * input device. A Sensor consists of a timestamped sequence of
+ * input values and the state of buttons or switches at the time
+ * that Java 3D sampled the value. A sensor also contains a hotspot
+ * offset specified in the sensor's local coordinate system. If not
+ * specified, the hotspot is (0.0, 0.0, 0.0).<P>
+ *
+ * Since a typical hardware environment may contain multiple sensing
+ * elements, Java 3D maintains an array of sensors. Users can access
+ * a sensor directly from their Java code or they can assign a sensor
+ * to one of Java 3D's predefined 6DOF entities, such as UserHead.<P>
+ *
+ * Using a sensor is as easy as accessing an object. Write your
+ * Java code to extract the associated sensor value from the array of
+ * sensors. You can then directly apply that value to an element in a
+ * scene graph or process the sensor values in whatever way necessary.<P>
+ *
+ * Java 3D includes three special six-degrees-of-freedom (6DOF) entities.
+ * These include UserHead, DominantHand, and NondominantHand. You
+ * can assign or change which sensor drives one
+ * of these predefined entities. Java 3D uses the specified sensor to
+ * drive the 6DOF entity - most visibly the View.<P>
+ *
+ * Java 3D does not provide raw tracker or joystick-generated data in
+ * a sensor. At a minimum, Java 3D normalizes the raw data using the
+ * registration and calibration parameters either provided by or
+ * provided for the end user. It additionally may filter and process
+ * the data to remove noise and improve latency.
+ * The application programmer can suppress this latter effect on a
+ * sensor-by-sensor basis.<P>
+ *
+ * @see SensorRead
+ */
+
+public class Sensor {
+
+ /**
+ * Set predictor type to do no prediction; this is the default.
+ */
+ public static final int PREDICT_NONE = 1;
+
+ /**
+ * Set predictor type to generate the SensorRead to correspond with
+ * the next frame time.
+ */
+ public static final int PREDICT_NEXT_FRAME_TIME = 2;
+
+ /**
+ * Use no prediction policy; this is the default.
+ */
+ public static final int NO_PREDICTOR = 16;
+
+ /**
+ * Set the predictor policy to assume the sensor is predicting head
+ * position/orientation.
+ */
+ public static final int HEAD_PREDICTOR = 32;
+
+ /**
+ * Set the predictor policy to assume the sensor is predicting hand
+ * position/orientation.
+ */
+ public static final int HAND_PREDICTOR = 64;
+
+ /**
+ * Default SensorRead object count (30); the number of SensorRead
+ * objects constructed if no count is specified.
+ */
+ public static final int DEFAULT_SENSOR_READ_COUNT = 30;
+
+ /**
+ * SENSOR_READ_COUNT_BUFFER is the number of extra sensor reading
+ * values to store at the end of the circular list. It helps provide
+ * MT-safeness. This is necessary if someone asks for the last
+ * k sensor values and k is close to sensor read count.
+ * This helps avoid some synchronization statements in getRead
+ * and setNextSensorRead.
+ */
+ static final int SENSOR_READ_COUNT_BUFFER = 15;
+
+ static int num_reads_so_far = 0;
+
+ // specifies whether a DEMAND_DRIVEN device has been added that
+ // manages this sensor
+ boolean demand_driven = false;
+
+ // size of the sensor read buffer
+ int sensorReadCount;
+
+ // Default prediction policy: don't predict
+ int predictionPolicy = NO_PREDICTOR;
+
+ // Default Predictor none
+ int predictorType = PREDICT_NONE;
+
+ // This sensor's associated device
+ InputDevice device;
+
+ SensorRead readings[];
+ int currentIndex;
+ int lastIndex;
+ Point3d hotspot;
+ int MaxSensorReadIndex;
+
+ // The count of the number of buttons associated with this sensor.
+ int sensorButtonCount;
+
+ // These matrices used as a temporary workspace for the local SVD
+ // calculations (thus minimimizing garbage collection).
+ Matrix3d orig_rot = new Matrix3d();
+ Matrix3d orig_rot_transpose = new Matrix3d();
+ Matrix3d temp_rot = new Matrix3d();
+ Matrix3d local_svd = new Matrix3d();
+
+ // Prediction workspace -- these may go away when the readings array
+ // is used.
+ static int MAX_PREDICTION_LENGTH = 20;
+ Transform3D[] previousReads = new Transform3D[MAX_PREDICTION_LENGTH];
+ long[] times = new long[MAX_PREDICTION_LENGTH];
+
+
+ /**
+ * Constructs a Sensor object for the specified input device using
+ * default parameters. The default values are as follows:
+ * <ul>
+ * sensor read count : 30<br>
+ * sensor button count : 0<br>
+ * hot spot : (0,0,0)<br>
+ * predictor : PREDICT_NONE<br>
+ * prediction policy : NO_PREDICTOR<br>
+ * </ul>
+ * @param device the Sensor's associated device.
+ */
+ public Sensor(InputDevice device){
+ this(device, DEFAULT_SENSOR_READ_COUNT, 0, new Point3d(0.0, 0.0, 0.0));
+ }
+
+ /**
+ * Constructs a Sensor object for the specified input device using
+ * the specified number of SensorRead objects.
+ * Default values are used for all other parameters.
+ * @param device the Sensor's associated device
+ * @param sensorReadCount the number of SensorReads to associate with
+ * this sensor
+ */
+ public Sensor(InputDevice device, int sensorReadCount){
+ this(device, sensorReadCount, 0, new Point3d(0.0, 0.0, 0.0));
+ }
+
+ /**
+ * Constructs a Sensor object for the specified input device using
+ * the specified number of SensorRead objects and number of buttons.
+ * Default values are used for all other parameters.
+ * @param device the Sensor's associated device
+ * @param sensorReadCount the number of SensorReads to associate with
+ * this sensor
+ * @param sensorButtonCount the number of buttons associated with each
+ * sensor read
+ */
+ public Sensor(InputDevice device, int sensorReadCount,
+ int sensorButtonCount){
+ this(device, sensorReadCount, sensorButtonCount,
+ new Point3d(0.0,0.0, 0.0));
+ }
+
+ /**
+ * Constructs a Sensor object for the specified input device using
+ * the specified hotspot.
+ * Default values are used for all other parameters.
+ * @param device the Sensor's associated device
+ * @param hotspot the Sensor's hotspot defined in its local coordinate
+ * system
+ */
+ public Sensor(InputDevice device, Point3d hotspot){
+ this(device, DEFAULT_SENSOR_READ_COUNT, 0, hotspot);
+ }
+
+ /**
+ * Constructs a Sensor object for the specified input device using
+ * the specified number of SensorRead objects and hotspot.
+ * Default values are used for all other parameters.
+ * @param device the Sensor's associated device
+ * @param sensorReadCount the number of SensorReads to associate with
+ * this sensor
+ * @param hotspot the Sensor's hotspot defined in its local coordinate
+ * system
+ */
+ public Sensor(InputDevice device, int sensorReadCount, Point3d hotspot){
+ this(device, sensorReadCount, 0, hotspot);
+ }
+
+ /**
+ * Constructs a Sensor object for the specified input device using
+ * the specified number of SensorRead objects, number of buttons, and
+ * hotspot.
+ * Default values are used for all other parameters.
+ * @param device the Sensor's associated device
+ * @param sensorReadCount the number of SensorReads to associate with
+ * this sensor
+ * @param sensorButtonCount the number of buttons associated with each
+ * sensor read
+ * @param hotspot the Sensor's hotspot defined in its local coordinate
+ * system
+ */
+ public Sensor(InputDevice device, int sensorReadCount,
+ int sensorButtonCount, Point3d hotspot){
+ this.device = device;
+ this.sensorReadCount = sensorReadCount;
+ this.MaxSensorReadIndex = sensorReadCount + SENSOR_READ_COUNT_BUFFER - 1;
+ this.sensorButtonCount = sensorButtonCount;
+ readings = new SensorRead[MaxSensorReadIndex + 1];
+ for(int i = 0; i < MaxSensorReadIndex + 1; i++){
+ readings[i] = new SensorRead(sensorButtonCount);
+ }
+ currentIndex = 0;
+ this.hotspot = new Point3d(hotspot);
+
+ // prediction initialization
+ for(int i=0 ; i<MAX_PREDICTION_LENGTH ; i++) {
+ previousReads[i] = new Transform3D();
+ }
+ }
+
+ // argument of 0 is last reading (ie, currentIndex), argument
+ // of 1 means next to last index, etc.
+ int previousIndex(int k){
+ int temp = currentIndex - k;
+ return(temp >= 0 ? temp : MaxSensorReadIndex + temp + 1);
+ }
+
+ /**
+ * This function sets the type of predictor to use with this sensor.
+ * @param predictor predictor type one of PREDICT_NONE or
+ * PREDICT_NEXT_FRAME_TIME
+ * @exception IllegalArgumentException if an invalid predictor type
+ * is specified.
+ */
+ public void setPredictor(int predictor){
+ if (predictor != PREDICT_NONE && predictor != PREDICT_NEXT_FRAME_TIME) {
+ throw new IllegalArgumentException(J3dI18N.getString("Sensor0"));
+ } else {
+ predictorType = predictor;
+ }
+ }
+
+ /**
+ * This function returns the type of predictor used by this sensor.
+ * @return returns the predictor type. One of PREDICT_NONE or
+ * PREDICT_NEXT_FRAME_TIME.
+ */
+ public int getPredictor(){
+ return predictorType;
+ }
+
+ /**
+ * This function sets the prediction policy use by this sensor.
+ * @param policy prediction policy one of NO_PREDICTOR, HEAD_PREDICTOR,
+ * or HAND_PREDICTOR
+ * @exception IllegalArgumentException if an invalid prediction policy
+ * is specified.
+ */
+ public void setPredictionPolicy(int policy){
+ if (policy != NO_PREDICTOR && policy != HEAD_PREDICTOR &&
+ policy != HAND_PREDICTOR)
+ throw new IllegalArgumentException(J3dI18N.getString("Sensor1"));
+ else
+ predictionPolicy = policy;
+ }
+
+ /**
+ * This function returns the prediction policy used by this sensor.
+ * @return returns the prediction policy. one of NO_PREDICTOR,
+ * HEAD_PREDICTOR, or HAND_PREDICTOR.
+ */
+ public int getPredictionPolicy(){
+ return predictionPolicy;
+ }
+
+ /**
+ * Set the sensor's hotspot in this sensor's coordinate system.
+ * @param hotspot the sensor's new hotspot
+ */
+ public void setHotspot(Point3d hotspot){
+ this.hotspot.set(hotspot);
+ }
+
+ /**
+ * Get the sensor's hotspot in this sensor's coordinate system.
+ * @param hotspot the variable to receive the sensor's hotspot
+ */
+ public void getHotspot(Point3d hotspot){
+ hotspot.set(this.hotspot);
+ }
+
+ /**
+ * Set the sensor's associated input device.
+ * @param device the sensor's new device
+ */
+ public void setDevice(InputDevice device){
+ this.device = device;
+ }
+
+ /**
+ * Retrieves the sensor's associated input device.
+ * @return the sensor's device
+ */
+ public InputDevice getDevice(){
+ return device;
+ }
+
+ /**
+ * Computes the sensor reading consistent with the prediction policy
+ * and copies that value into the specified argument; calling this method
+ * with a prediction policy of NO_PREDICTOR will return the last sensor
+ * reading; calling this method with a prediction policy of HAND_PREDICTOR,
+ * or HEAD_PREDICTOR will extrapolate previous sensor readings to the
+ * current time.
+ * @param read The matrix that will receive the predicted sensor reading
+ */
+ public void getRead(Transform3D read){
+ long time;
+
+ if(demand_driven == true)
+ device.pollAndProcessInput();
+
+ time = System.currentTimeMillis();
+
+ // before using prediction, fill in some values
+ if(num_reads_so_far < 40*SENSOR_READ_COUNT_BUFFER) {
+ num_reads_so_far++;
+ read.set(readings[currentIndex].read);
+ return;
+ }
+
+ switch(predictionPolicy) {
+ case NO_PREDICTOR:
+ read.set(readings[currentIndex].read);
+ break;
+ case HAND_PREDICTOR:
+ read.set(readings[currentIndex].read);
+ //getPredictedRead(read, time, 3, 2);
+ break;
+ case HEAD_PREDICTOR:
+ read.set(readings[currentIndex].read);
+ //getPredictedRead(read, time, 3, 2);
+ break;
+ }
+ }
+
+ /**
+ * Computes the sensor reading consistent as of time deltaT in the future
+ * and copies that value into the specified argument; the reading is
+ * computed using the current prediction policy; a prediction policy of
+ * NO_PREDICTOR will yield the most recent sensor reading for any
+ * deltaT argument (i.e., this method is the same as getRead for a prediction
+ * policy of NO_PREDICTOR). The time argument must be >= 0.
+ * @param read the matrix that will receive the predicted sensor reading
+ * @param deltaT the time delta into the future for this read
+ */
+ public void getRead(Transform3D read, long deltaT){
+ long current_time;
+
+ if(deltaT < 0L) {
+ throw new IllegalArgumentException(J3dI18N.getString("Sensor2"));
+ }
+
+ if(demand_driven == true)
+ device.pollAndProcessInput();
+
+ current_time = System.currentTimeMillis();
+
+ switch(predictionPolicy) {
+ case NO_PREDICTOR:
+ read.set(readings[currentIndex].read);
+ break;
+ case HAND_PREDICTOR:
+ read.set(readings[currentIndex].read);
+ //getPredictedRead(read, current_time + deltaT, 3, 2);
+ break;
+ case HEAD_PREDICTOR:
+ read.set(readings[currentIndex].read);
+ //getPredictedRead(read, current_time + deltaT, 3, 2);
+ break;
+ }
+ }
+
+ /**
+ * Extracts the most recent sensor reading and copies that value into
+ * the specified argument.
+ * @param read the matrix that will receive the most recent sensor reading
+ */
+ public void lastRead(Transform3D read){
+ read.set(readings[currentIndex].read);
+ }
+
+ /**
+ * Extracts the kth-most recent sensor reading and copies that value into
+ * the specified argument; where 0 is the most recent sensor reading, 1 is
+ * the next most recent sensor reading, etc.
+ * @param read the matrix that will receive the most recent sensor reading
+ * @param kth the kth previous sensor reading
+ */
+ public void lastRead(Transform3D read, int kth){
+ if(kth >= sensorReadCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("Sensor3"));
+ }
+ read.set(readings[previousIndex(kth)].read);
+ }
+
+ /**
+ * Returns the time associated with the most recent sensor reading.
+ * @return the time associated with the most recent sensor reading.
+ */
+ public long lastTime(){
+ return readings[currentIndex].time;
+ }
+
+ /**
+ * Returns the time associated with the kth-most recent sensor reading;
+ * where 0 is the most recent sensor reading, 1 is the next most recent
+ * sensor reading, etc.
+ * @return the time associated with the kth-most recent sensor reading.
+ */
+ public long lastTime(int k){
+ if(k >= sensorReadCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("Sensor4"));
+ }
+ return readings[previousIndex(k)].time;
+ }
+
+ /**
+ * Places the most recent sensor reading value for each button into
+ * the array parameter; will throw an ArrayIndexOutOfBoundsException
+ * if values.length is less than the number of buttons.
+ * @param values the array into which the button values will be
+ * placed
+ */
+ public void lastButtons(int[] values) {
+ System.arraycopy(readings[currentIndex].buttonValues, 0, values,
+ 0, sensorButtonCount);
+ }
+
+ /**
+ * Places the kth-most recent sensor reading value for each button into
+ * the array parameter; where k=0 is the most recent sensor reading, k=1
+ * is the next most recent sensor reading, etc.; will throw an
+ * ArrayIndexOutOfBoundsException if values.length is less than
+ * the number of buttons.
+ * @param k the time associated with the most recent sensor reading
+ * @param values the array into which the button values will be
+ * placed.
+ */
+ public void lastButtons(int k, int[] values) {
+ if(k >= sensorReadCount) {
+ throw new IllegalArgumentException(J3dI18N.getString("Sensor5"));
+ }
+ System.arraycopy(readings[previousIndex(k)].buttonValues, 0, values,
+ 0, sensorButtonCount);
+ }
+
+ /**
+ * Returns the number of SensorRead objects associated with
+ * this sensor.
+ * @return the number of SensorReadObjects associated with this sensor
+ */
+ public int getSensorReadCount() {
+ return this.sensorReadCount;
+ }
+
+ /**
+ * Set the number of sensor read objects per Sensor. This is a
+ * calibration parameter that should normally be set in this
+ * object's constructor. Calling this method resets all of this
+ * sensor's values that are already in the buffer.
+ * It is illegal to change this value after the device has been
+ * added to the scheduler.
+ * @param count the new sensor read count
+ */
+ public void setSensorReadCount(int count) {
+ sensorReadCount = count;
+ MaxSensorReadIndex = sensorReadCount + SENSOR_READ_COUNT_BUFFER - 1;
+ readings = new SensorRead[MaxSensorReadIndex + 1];
+ for(int i = 0; i < MaxSensorReadIndex + 1; i++){
+ readings[i] = new SensorRead(sensorButtonCount);
+ }
+ currentIndex = 0;
+ }
+
+
+ /**
+ * Returns the number of buttons associated with this sensor.
+ * @return the number of buttons associated with this sensor.
+ */
+ public int getSensorButtonCount() {
+ return sensorButtonCount;
+ }
+
+ /**
+ * Gets the current sensor read.
+ * @return the current sensor read object
+ */
+ public SensorRead getCurrentSensorRead() {
+ // not sure if this should return a reference or a copy
+ SensorRead read = new SensorRead(sensorButtonCount);
+ read.set(readings[currentIndex]);
+ return read;
+ }
+
+ /**
+ * Sets the next sensor read to the specified values; once these
+ * values are set via this method they become the current values
+ * returned by methods such as lastRead(), lastTime(), and
+ * lastButtons(); note that if there are no buttons associated with
+ * this sensor, values can just be an empty array.
+ * @param time the next SensorRead's associated time
+ * @param transform the next SensorRead's transformation
+ * @param values the next SensorRead's buttons' states
+ */
+ public void setNextSensorRead(long time, Transform3D transform,
+ int[] values) {
+
+ int temp = currentIndex + 1;
+ if (temp > MaxSensorReadIndex) temp = 0;
+
+ readings[temp].setTime(time);
+ readings[temp].set(transform);
+ if(sensorButtonCount > 0)
+ readings[temp].setButtons(values);
+ currentIndex = temp;
+ }
+
+ /**
+ * Sets the next sensor read to the specified values; once these
+ * values are set via this method they become the current values
+ * returned by methods such as lastRead(), lastTime(), and
+ * lastButtons().
+ * @param read the next SensorRead's values
+ */
+ public void setNextSensorRead(SensorRead read) {
+ int temp = currentIndex + 1;
+ if (temp > MaxSensorReadIndex) temp = 0;
+ readings[temp].set(read);
+ currentIndex = temp;
+ }
+
+ /**
+ * This routine does an nth order fit of the last num_readings, which
+ * can be plotted on a graph of time vs. sensor reading. There is a
+ * separate fit done for each of the 16 matrix elements, then an SVD
+ * done on the final matrix. The curve that is produced takes into
+ * account non-constant times between each sample (it is fully general).
+ * This curve can then be used to produce a prediction for any
+ * time in the future by simply inserting a time value and using the
+ * solution matrix.
+ */
+ void getPredictedRead(Transform3D transform, long time, int num_readings,
+ int order) {
+
+ int index = currentIndex; // lock in current_index for MT-safety
+ long time_basis = readings[index].time;
+ long tempTime;
+
+ time -= time_basis;
+
+ GMatrix A = new GMatrix(num_readings, order+1);
+
+ for(int i=0 ; i<num_readings ; i++) {
+ A.setElement(i, 0, 1.0);
+ tempTime = lastTimeRelative(num_readings-i-1, index, time_basis);
+ A.setElement(i, 1, (double)tempTime);
+ for(int j=2; j<=order ; j++) {
+ // powerAndDiv(time, n) = times^n/n
+ A.setElement(i, j, powerAndDiv(tempTime, j));
+ }
+ }
+
+ GMatrix A_Transpose = new GMatrix(A);
+ A_Transpose.transpose();
+ GMatrix M = new GMatrix(order+1, order+1);
+ M.mul(A_Transpose, A);
+ try {
+ M.invert();
+ } catch (SingularMatrixException e) {
+ System.out.println("SINGULAR MATRIX EXCEPTION in prediction");
+ System.out.println(M);
+ }
+
+ // TODO: can be class scope
+ double[] transformArray = new double[16];
+ GMatrix solMatrix = new GMatrix(order+1, num_readings);
+ solMatrix.mul(M,A_Transpose);
+
+ GVector P = new GVector(order+1);
+
+ // fill in the time for which we are trying to predict a sensor value
+ GVector predTimeVec = new GVector(order+1);
+ predTimeVec.setElement(0, 1);
+ predTimeVec.setElement(1, time);
+ for(int i=2 ; i<=order ; i++) {
+ predTimeVec.setElement(i, powerAndDiv(time, i));
+ }
+
+ GVector R = new GVector(num_readings);
+
+ for(int transElement=0 ; transElement<16 ; transElement++) {
+
+ for(int i=0 ; i<num_readings ; i++) {
+ R.setElement(i, lastReadRelative(num_readings-i-1, index,
+ transElement));
+ }
+
+ P.mul(solMatrix,R);
+ transformArray[transElement] = P.dot(predTimeVec);
+ }
+
+ //Matrix4d temp = new Matrix4d(transformArray);
+ //localSVD(temp);
+ //transform.set(temp);
+ transform.set(transformArray);
+ transform.normalize();
+ }
+
+ /**
+ * Extracts the kth most recent sensor reading and copies that value into
+ * the specified argument; where 0 is the most recent sensor reading, 1 is
+ * the next most recent sensor reading, etc.
+ * @param read The matrix that will receive the most recent sensor reading
+ * @param k The kth previous sensor reading
+ */
+ double lastReadRelative(int kth, int base_index, int mat_element){
+ // kth should be < sensorReadCount
+ return
+ readings[previousIndexRelative(kth, base_index)].read.mat[mat_element];
+ }
+
+ /**
+ * Returns the time associated with the kth most recent sensor reading;
+ * where 0 is the most recent sensor reading, 1 is the next most recent
+ * sensor reading, etc. However, unlike the public method, returns
+ * the kth reading relative to the index given, instead of the
+ * current_index and returns the time relative to timeBasis.
+ * @return the time associated with the kthmost recent sensor reading.
+ */
+ long lastTimeRelative(int k, int base_index, long timeBasis){
+ // kth should be < sensorReadCount
+ long time;
+ time = timeBasis - readings[previousIndexRelative(k, base_index)].time;
+ return time;
+ }
+
+ // argument of 0 is last reading, argument of 1 means next to last
+ // index, etc. , but all of these are relative to *base_index*
+ int previousIndexRelative(int k, int base_index){
+ int temp = base_index - k;
+ return(temp >= 0 ? temp : MaxSensorReadIndex + temp + 1);
+ }
+
+ // this method returns (value^order)/order
+ double powerAndDiv(double value, int order) {
+
+ if(order == 0)
+ return 1;
+ else if(order == 1)
+ return value;
+
+ double total = 1.0;
+ for(int i=0 ; i< order ; i++)
+ total *= value;
+
+ total = total / (double)order;
+ return total;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SensorRead.java b/src/classes/share/javax/media/j3d/SensorRead.java
new file mode 100644
index 0000000..5e0e48b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SensorRead.java
@@ -0,0 +1,167 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * A SensorRead encapsulates all the information associated with a single
+ * reading of a sensor, including a timestamp, a transform, and,
+ * optionally, button values.
+ */
+
+public class SensorRead {
+
+ /**
+ * The maximum number of sensor-attached buttons tracked on a per
+ * sensor basis.
+ */
+ public static final int MAXIMUM_SENSOR_BUTTON_COUNT = 12;
+
+ /**
+ * This reading's time stamp
+ */
+ long time;
+
+ /**
+ * The six-degree-of-freedom reading
+ */
+ Transform3D read;
+
+ /**
+ * The state of the sensor's buttons
+ */
+ int[] buttonValues;
+
+ /**
+ * The number of buttons associated with this SensorRead
+ */
+ int numButtons;
+
+ /**
+ * Constructs a SensorRead object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * number of buttons : 0<br>
+ * button values : 0 (for all array elements)<br>
+ * transform : identity<br>
+ * time : current time<br>
+ * </ul>
+ */
+ public SensorRead(){
+ this(0);
+ }
+
+ /**
+ * Constructs a SensorRead object with the specified number
+ * of buttons.
+ * @param numButtons the number of buttons for this SensorRead
+ */
+ public SensorRead(int numButtons){
+ this.read = new Transform3D();
+ this.numButtons = numButtons;
+ this.buttonValues = new int[numButtons];
+
+ // Do this last
+ this.time = System.currentTimeMillis();
+ }
+
+ final void set(SensorRead sensorRead) {
+ this.time = sensorRead.time;
+ this.numButtons = sensorRead.numButtons;
+ this.read.set(sensorRead.read);
+ if(numButtons > 0)
+ System.arraycopy(sensorRead.buttonValues, 0, this.buttonValues,
+ 0, sensorRead.numButtons);
+ }
+
+ /**
+ * Set the SensorRead's transform to the value specified
+ * @param t1 this sensor's reading
+ */
+ public void set(Transform3D t1) {
+ read.set(t1);
+ }
+
+ /**
+ * Retrieve the SensorRead's transform and place it in result
+ * @param result the recipient of the this sensor's reading
+ */
+ public void get(Transform3D result) {
+ result.set(read);
+ }
+
+ /**
+ * Sets this SensorRead's time stamp to the specified argument
+ * @param time the time to associate with this reading
+ */
+ public void setTime(long time) {
+ this.time = time;
+ }
+
+ /**
+ * Retrieve this SensorRead's associated time stamp
+ * @return the SensorRead's time as a long
+ */
+ public long getTime() {
+ return this.time;
+ }
+
+ /**
+ * Sets the values of all buttons for this SensorRead object.
+ * @param values array contining the new buttons for this SensorRead
+ * @exception ArrayIndexOutOfBoundsException if this object
+ * has 0 buttons or if values.length is less than the number of
+ * buttons in this object.
+ */
+ public void setButtons(int[] values) {
+ if(numButtons == 0)
+
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("SensorRead1"));
+
+ else if(values.length < numButtons)
+
+ throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("SensorRead0"));
+ System.arraycopy(values, 0, buttonValues, 0, numButtons);
+ }
+
+
+ /**
+ * Copies the array of button values for this SensorRead object into
+ * the specified array.
+ * This method has no effect
+ * if this SensorRead object has 0 buttons. The array must be
+ * large enough to hold all of the buttons.
+ * @param values array that will receive the values of all buttons
+ * for this SensorRead
+ */
+ public void getButtons(int[] values) {
+ if(numButtons > 0)
+ System.arraycopy(buttonValues, 0, values, 0, numButtons);
+ }
+
+
+ /**
+ * Returns the number of buttons associated with this SensorRead
+ * object.
+ *
+ * @return the number of buttons associated with this SensorRead
+ * object
+ *
+ * @since Java 3D 1.2
+ */
+ public int getNumButtons() {
+ return numButtons;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SetLiveState.java b/src/classes/share/javax/media/j3d/SetLiveState.java
new file mode 100644
index 0000000..a9a7df0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SetLiveState.java
@@ -0,0 +1,252 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * SetLiveState is used to encapsulate all state needed when a branch
+ * group is added to the scene graph
+ */
+
+class SetLiveState extends Object {
+ // The VirtualUniverse for this branch group
+ VirtualUniverse universe = null;
+
+ // The Locale for this Branch Graph
+ Locale locale = null;
+
+ // The transforms used to update state
+ Transform3D[][] currentTransforms = new Transform3D[1][];
+ int[][] currentTransformsIndex = new int[1][];
+
+ // The keys used when dealing with SharedGroups
+ HashKey[] keys = null;
+
+ // flags for detecting what we are under
+ boolean inSharedGroup = false;
+ boolean inBackgroundGroup = false;
+ boolean inViewSpecificGroup = false;
+
+ /**
+ * The list of nodes added/removed during setLive/clearLive
+ */
+ ArrayList nodeList = new ArrayList();
+
+ /**
+ * List of nodes that are viewScoped. Note that all nodes
+ * except Shape3D nodes can be in viewScopedNodeList, shape3D
+ * nodes will always be in the nodeList regardless of scoped
+ * or not. Also, only renderbin and renderingEnv structure is
+ * interested in viewScopedNodeList
+ */
+ ArrayList viewScopedNodeList = null;
+
+ /**
+ * Parallel list to viewScopedNodeList containing a list of views
+ * that the viewScopedNode is scoped to
+ */
+ ArrayList scopedNodesViewList = null;
+
+ // Threads to notify after setLive/clearLive
+ int notifyThreads = 0;
+
+ // The current list of leaf nodes for transform targets
+ Targets[] transformTargets = null;
+
+ // List of transform level, one per shared path
+ int transformLevels[] = new int[]{-1};
+
+ // List of scoped lights
+ ArrayList lights = null;
+
+ // List of scoped fogs
+ ArrayList fogs =null;
+
+ // List of scoped modelClips
+ ArrayList modelClips = null;
+
+ // List of scoped alt app
+ ArrayList altAppearances =null;
+
+ // List of viewes scoped to this Group, for all subclasses
+ // of group, except ViewSpecificGroup its a pointer to closest
+ // ViewSpecificGroup parent
+ // viewList for this node, if inSharedGroup is
+ // false then only viewList(0) is valid
+ ArrayList viewLists = null;
+ ArrayList changedViewGroup = null;
+ ArrayList changedViewList = null;
+ int[] keyList = null;
+
+
+ // The current bitmask of types in transformTragets
+ //int transformTargetThreads = 0;
+
+ ArrayList orderedPaths = null;
+
+ ArrayList ogList = new ArrayList(5);
+ ArrayList ogChildIdList = new ArrayList(5);
+ ArrayList ogOrderedIdList = new ArrayList(5);
+ // ogCIOList contains a list of OG with affected child index order.
+ ArrayList ogCIOList = new ArrayList(5);
+ // ogCIOTableList contains a list of affected child index order.
+ ArrayList ogCIOTableList = new ArrayList(5);
+
+ /**
+ * List of BranchGroup from this node to the root of tree
+ * This is used by BranchGroupRetained to construct
+ * BranchGroup lists for picking.
+ *
+ * @see NodeRetained.branchGroupPaths
+ */
+ ArrayList branchGroupPaths = null;
+ ArrayList parentBranchGroupPaths = null;
+
+ /**
+ * List of Pickable flags, one for each share path.
+ * This flag is true when all the NodeRetained.pickable is true
+ * along the path except current node.
+ */
+ boolean pickable[] = new boolean[]{true};
+
+ /**
+ * List of collidable flags, one for each share path.
+ * This flag is true when all the NodeRetained.pickable is true
+ * along the path except current node.
+ */
+ boolean collidable[] = new boolean[]{true};
+
+ // reference count use in set/clear Live to remember how
+ // many references of the original branch that attach()/detach()
+ int refCount = 1;
+
+ // background node whose geometry branch contains this node
+ BackgroundRetained geometryBackground = null;
+
+ // behavior nodes
+ ArrayList behaviorNodes = new ArrayList(1);
+
+ // The current list of child transform group nodes or link nodes
+ // under a transform group
+ ArrayList childTransformLinks = null;
+
+ // closest parent which is a TransformGroupRetained or sharedGroupRetained
+ GroupRetained parentTransformLink = null;
+
+ // switch Level, start from -1, increment by one for each SwitchNode
+ // encounter in a branch, one per key
+ int switchLevels[] = new int[]{-1};
+
+ // closest switch parent, one per key
+ SwitchRetained closestSwitchParents[] = new SwitchRetained[]{null};
+
+ // the child id from the closest switch parent, one per key
+ int closestSwitchIndices[] = new int[]{-1};
+
+ // The current list of leaf nodes for switch targets
+ Targets[] switchTargets = null;
+
+ // The current list of closest child switch nodes or
+ // link nodes under a switch node
+ ArrayList childSwitchLinks = null;
+
+ // closest parent which is a SwitchRetained or sharedGroupRetained
+ GroupRetained parentSwitchLink = null;
+
+ SharedGroupRetained lastSharedGroup = null;
+
+ int traverseFlags = 0;
+
+ // Use for set live.
+ Transform3D[][] localToVworld = null;
+ int[][] localToVworldIndex = null;
+ HashKey[] localToVworldKeys = null;
+
+ // cached hashkey index to eliminate duplicate hash key index search
+ // currently used by Switch, can be extended for other node types
+ int[] hashkeyIndex = null;
+
+ ArrayList switchStates = null;
+
+ SetLiveState(VirtualUniverse u) {
+ universe = u;
+ }
+
+
+ void reset(Locale l) {
+ locale = l;
+ clear();
+ }
+
+ void clear() {
+ inSharedGroup = false;
+ inBackgroundGroup = false;
+ inViewSpecificGroup = false;
+ nodeList.clear();
+ viewScopedNodeList = null;
+ scopedNodesViewList = null;
+
+ notifyThreads = 0;
+ transformTargets = null;
+ lights = null;
+ fogs = null;
+ modelClips = null;
+ altAppearances = null;
+ viewLists = null;
+ changedViewGroup = null;
+ changedViewList = null;
+ keyList = null;
+
+ behaviorNodes.clear();
+ traverseFlags = 0;
+
+ ogList.clear();
+ ogChildIdList.clear();
+ ogOrderedIdList.clear();
+ ogCIOList.clear();
+ ogCIOTableList.clear();
+
+ pickable = new boolean[]{true};
+ collidable = new boolean[]{true};
+ refCount = 1;
+ geometryBackground = null;
+ transformLevels = new int[]{-1};
+ childTransformLinks = null;
+ parentTransformLink = null;
+
+ switchTargets = null;
+ switchLevels = new int[]{-1};
+ switchStates = null;
+ closestSwitchIndices = new int[]{-1};
+ closestSwitchParents = new SwitchRetained[]{null};
+ childSwitchLinks = null;
+ parentSwitchLink = null;
+
+ lastSharedGroup = null;
+
+ keys = null;
+ currentTransforms = new Transform3D[1][];
+ currentTransformsIndex = new int[1][];
+
+ localToVworld = null;
+ localToVworldIndex = null;
+ localToVworldKeys = null;
+
+ // TODO: optimization for targetThreads computation, require
+ // cleanup in GroupRetained.doSetLive()
+ //transformTargetThreads = 0;
+
+ hashkeyIndex = null;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Shape3D.java b/src/classes/share/javax/media/j3d/Shape3D.java
new file mode 100644
index 0000000..91d32e1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Shape3D.java
@@ -0,0 +1,759 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+
+
+/**
+ * The Shape3D leaf node specifies all geometric objects. It contains
+ * a list of one or more Geometry component objects and a single
+ * Appearance component object. The geometry objects define the shape
+ * node's geometric data. The appearance object specifies that
+ * object's appearance attributes, including color, material, texture,
+ * and so on.
+ * <p>
+ * The list of geometry objects must all be of the same equivalence
+ * class, that is, the same basic type of primitive. For subclasses
+ * of GeometryArray, all point objects are equivalent, all line
+ * objects are equivalent, and all polygon objects are equivalent.
+ * For other subclasses of Geometry, only objects of the same
+ * subclass are equivalent. The equivalence classes are as follows:
+ * <ul>
+ * <li>GeometryArray (point): [Indexed]PointArray</li>
+ * <li>GeometryArray (line): [Indexed]{LineArray, LineStripArray}</li>
+ * <li>GeometryArray (polygon): [Indexed]{TriangleArray, TriangleStripArray,
+ * TriangleFanArray, QuadArray}</li>
+ * <li>CompressedGeometry</li>
+ * <li>Raster</li>
+ * <li>Text3D</li>
+ * </ul>
+ * <p>
+ * When Shape3D is used with multiple geometry components, Java 3D may
+ * choose to use individual geometry bounds instead of the shape's
+ * bounds for region of influence operations, such as lighting.
+ * For example, the individual characters of a Text3D shape object
+ * may be rendered with a different light set.
+ */
+
+public class Shape3D extends Leaf {
+
+ /**
+ * Id used in the compile optimization to determine
+ * how to get to the geometry in the case of read
+ * or picking ..
+ */
+ int id;
+
+ /**
+ * Specifies that the node allows read access to its geometry information.
+ */
+ public static final int
+ ALLOW_GEOMETRY_READ = CapabilityBits.SHAPE3D_ALLOW_GEOMETRY_READ;
+
+ /**
+ * Specifies that the node allows write access to its geometry information.
+ */
+ public static final int
+ ALLOW_GEOMETRY_WRITE = CapabilityBits.SHAPE3D_ALLOW_GEOMETRY_WRITE;
+
+ /**
+ * Specifies that the node allows read access to its appearance
+ * information.
+ */
+ public static final int
+ ALLOW_APPEARANCE_READ = CapabilityBits.SHAPE3D_ALLOW_APPEARANCE_READ;
+
+ /**
+ * Specifies that the node allows write access to its appearance
+ * information.
+ */
+ public static final int
+ ALLOW_APPEARANCE_WRITE = CapabilityBits.SHAPE3D_ALLOW_APPEARANCE_WRITE;
+
+ /**
+ * Specifies that the node allows reading its collision Bounds
+ */
+ public static final int
+ ALLOW_COLLISION_BOUNDS_READ = CapabilityBits.SHAPE3D_ALLOW_COLLISION_BOUNDS_READ;
+
+ /**
+ * Specifies the node allows writing its collision Bounds
+ */
+ public static final int
+ ALLOW_COLLISION_BOUNDS_WRITE = CapabilityBits.SHAPE3D_ALLOW_COLLISION_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this node allows reading its appearance override
+ * enable flag.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_APPEARANCE_OVERRIDE_READ =
+ CapabilityBits.SHAPE3D_ALLOW_APPEARANCE_OVERRIDE_READ;
+
+ /**
+ * Specifies that this node allows writing its appearance override
+ * enable flag.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_APPEARANCE_OVERRIDE_WRITE =
+ CapabilityBits.SHAPE3D_ALLOW_APPEARANCE_OVERRIDE_WRITE;
+
+ /**
+ * Constructs a Shape3D node with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * appearance : null<br>
+ * geometry : { null }<br>
+ * collision bounds : null<br>
+ * appearance override enable : false<br>
+ * </ul>
+ * The list of geometry components is initialized with a null
+ * geometry component as the single element with an index of 0.
+ * A null geometry component specifies
+ * that no geometry is drawn. A null appearance component specifies
+ * that default values are used for all appearance attributes.
+ */
+ public Shape3D() {
+ }
+
+ /**
+ * Constructs and initializes a Shape3D node with the specified
+ * geometry component and a null appearance component.
+ * The list of geometry components is initialized with the
+ * specified geometry component as the single element with an
+ * index of 0.
+ * A null appearance component specifies that default values are
+ * used for all appearance attributes.
+ * @param geometry the geometry component with which to initialize
+ * this shape node.
+ */
+ public Shape3D(Geometry geometry) {
+ ((Shape3DRetained)retained).setGeometry(geometry, 0);
+ }
+
+ /**
+ * Constructs and initializes a Shape3D node with the specified
+ * geometry and appearance components.
+ * The list of geometry components is initialized with the
+ * specified geometry component as the single element with an
+ * index of 0.
+ * @param geometry the geometry component with which to initialize
+ * this shape node
+ * @param appearance the appearance component of the shape node
+ */
+ public Shape3D(Geometry geometry, Appearance appearance) {
+ ((Shape3DRetained)retained).setGeometry(geometry, 0);
+ ((Shape3DRetained)retained).setAppearance(appearance);
+ }
+
+ /**
+ * Creates the retained mode Shape3DRetained object that this
+ * Shape3D object will point to.
+ */
+ void createRetained() {
+ retained = new Shape3DRetained();
+ retained.setSource(this);
+ }
+
+ /**
+ * Sets the collision bounds of a node.
+ * @param bounds the collision bounding object for a node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setCollisionBounds(Bounds bounds) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLLISION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D0"));
+
+ ((Shape3DRetained)this.retained).setCollisionBounds(bounds);
+ }
+
+ /**
+ * Returns the collision bounding object of this node.
+ * @return the node's collision bounding object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getCollisionBounds() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLLISION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D1"));
+
+ return ((Shape3DRetained)this.retained).getCollisionBounds(id);
+ }
+
+
+ /**
+ * Replaces the geometry component at index 0 in this Shape3D node's
+ * list of geometry components with the specified geometry component.
+ * If there are existing geometry components in the list (besides
+ * the one being replaced), the new geometry component must be of
+ * the same equivalence class (point, line, polygon, CompressedGeometry,
+ * Raster, Text3D) as the others.
+ * @param geometry the geometry component to be stored at index 0.
+ * @exception IllegalArgumentException if the new geometry
+ * component is not of of the same equivalence class as the
+ * existing geometry components.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setGeometry(Geometry geometry) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D2"));
+
+ ((Shape3DRetained)retained).setGeometry(geometry, 0);
+ }
+
+ /**
+ * Retrieves the geometry component at index 0 from this Shape3D node's
+ * list of geometry components.
+ * @return the geometry component at index 0.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Geometry getGeometry() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D3"));
+
+ return ((Shape3DRetained)retained).getGeometry(0, id);
+ }
+
+
+ /**
+ * Replaces the geometry component at the specified index in this
+ * Shape3D node's list of geometry components with the specified
+ * geometry component.
+ * If there are existing geometry components in the list (besides
+ * the one being replaced), the new geometry component must be of
+ * the same equivalence class (point, line, polygon, CompressedGeometry,
+ * Raster, Text3D) as the others.
+ * @param geometry the geometry component to be stored at the
+ * specified index.
+ * @param index the index of the geometry component to be replaced.
+ * @exception IllegalArgumentException if the new geometry
+ * component is not of of the same equivalence class as the
+ * existing geometry components.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void setGeometry(Geometry geometry, int index) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D2"));
+
+ ((Shape3DRetained)retained).setGeometry(geometry, index);
+ }
+
+
+ /**
+ * Retrieves the geometry component at the specified index from
+ * this Shape3D node's list of geometry components.
+ * @param index the index of the geometry component to be returned.
+ * @return the geometry component at the specified index.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public Geometry getGeometry(int index) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D3"));
+
+ return ((Shape3DRetained)retained).getGeometry(index, id);
+ }
+
+
+ /**
+ * Inserts the specified geometry component into this Shape3D
+ * node's list of geometry components at the specified index.
+ * If there are existing geometry components in the list, the new
+ * geometry component must be of the same equivalence class
+ * (point, line, polygon, CompressedGeometry, Raster, Text3D) as
+ * the others.
+ * @param geometry the geometry component to be inserted at the
+ * specified index.
+ * @param index the index at which the geometry component is inserted.
+ * @exception IllegalArgumentException if the new geometry
+ * component is not of of the same equivalence class as the
+ * existing geometry components.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void insertGeometry(Geometry geometry, int index) {
+
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D2"));
+
+ ((Shape3DRetained)retained).insertGeometry(geometry, index);
+ }
+
+
+ /**
+ * Removes the geometry component at the specified index from
+ * this Shape3D node's list of geometry components.
+ * @param index the index of the geometry component to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void removeGeometry(int index) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D2"));
+
+ ((Shape3DRetained)retained).removeGeometry(index);
+ }
+
+
+ /**
+ * Returns an enumeration of this Shape3D node's list of geometry
+ * components.
+ * @return an Enumeration object containing all geometry components in
+ * this Shape3D node's list of geometry components.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public Enumeration getAllGeometries() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D3"));
+
+ return ((Shape3DRetained)retained).getAllGeometries(id);
+ }
+
+
+ /**
+ * Appends the specified geometry component to this Shape3D
+ * node's list of geometry components.
+ * If there are existing geometry components in the list, the new
+ * geometry component must be of the same equivalence class
+ * (point, line, polygon, CompressedGeometry, Raster, Text3D) as
+ * the others.
+ * @param geometry the geometry component to be appended.
+ * @exception IllegalArgumentException if the new geometry
+ * component is not of of the same equivalence class as the
+ * existing geometry components.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void addGeometry(Geometry geometry) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D2"));
+
+ ((Shape3DRetained)retained).addGeometry(geometry);
+ }
+
+
+ /**
+ * Returns the number of geometry components in this Shape3D node's
+ * list of geometry components.
+ * @return the number of geometry components in this Shape3D node's
+ * list of geometry components.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int numGeometries() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D3"));
+ return ((Shape3DRetained)retained).numGeometries(id);
+ }
+
+
+ /**
+ * Retrieves the index of the specified geometry component in
+ * this Shape3D node's list of geometry components.
+ *
+ * @param geometry the geometry component to be looked up.
+ * @return the index of the specified geometry component;
+ * returns -1 if the object is not in the list.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfGeometry(Geometry geometry) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D3"));
+ return ((Shape3DRetained)retained).indexOfGeometry(geometry);
+ }
+
+
+ /**
+ * Removes the specified geometry component from this
+ * Shape3D node's list of geometry components.
+ * If the specified object is not in the list, the list is not modified.
+ *
+ * @param geometry the geometry component to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeGeometry(Geometry geometry) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D2"));
+ ((Shape3DRetained)retained).removeGeometry(geometry);
+ }
+
+
+ /**
+ * Removes all geometry components from this Shape3D node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllGeometries() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_GEOMETRY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D2"));
+ ((Shape3DRetained)retained).removeAllGeometries();
+ }
+
+
+ /**
+ * Sets the appearance component of this Shape3D node. Setting it to null
+ * specifies that default values are used for all appearance attributes.
+ * @param appearance the new appearance component for this shape node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAppearance(Appearance appearance) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_APPEARANCE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D4"));
+
+ ((Shape3DRetained)this.retained).setAppearance(appearance);
+ }
+
+ /**
+ * Retrieves the appearance component of this shape node.
+ * @return the appearance component of this shape node
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Appearance getAppearance() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_APPEARANCE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D5"));
+
+ return ((Shape3DRetained)this.retained).getAppearance();
+ }
+
+
+ /**
+ * Checks whether the geometry in this shape node intersects with
+ * the specified pickShape.
+ *
+ * @param path the SceneGraphPath to this shape node
+ * @param pickShape the PickShape to be intersected
+ *
+ * @return true if the pick shape intersects this node; false
+ * otherwise.
+ *
+ * @exception IllegalArgumentException if pickShape is a PickPoint.
+ * Java 3D doesn't have spatial information of the surface.
+ * Use PickBounds with BoundingSphere and a small radius, instead.
+ *
+ * @exception CapabilityNotSetException if the Geometry.ALLOW_INTERSECT
+ * capability bit is not set in all of the Geometry objects
+ * referred to by this shape node.
+ */
+ public boolean intersect(SceneGraphPath path, PickShape pickShape) {
+ return intersect(path, pickShape, null);
+ }
+
+
+ /**
+ * Checks whether the geometry in this shape node intersects with
+ * the specified pickRay.
+ *
+ * @param path the SceneGraphPath to this shape node
+ * @param pickRay the PickRay to be intersected
+ * @param dist the closest distance of the intersection
+ *
+ * @return true if the pick shape intersects this node; false
+ * otherwise. If true, dist contains the closest distance of
+ * intersection.
+ *
+ * @exception CapabilityNotSetException if the Geometry.ALLOW_INTERSECT
+ * capability bit is not set in all of the Geometry objects
+ * referred to by this shape node.
+ */
+ public boolean intersect(SceneGraphPath path,
+ PickRay pickRay,
+ double[] dist) {
+
+ if (isLiveOrCompiled()) {
+ if (!((Shape3DRetained)retained).allowIntersect())
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D6"));
+ }
+ return ((Shape3DRetained)this.retained).intersect(path, pickRay, dist);
+
+ }
+
+ /**
+ * Checks whether the geometry in this shape node intersects with
+ * the specified pickShape.
+ *
+ * @param path the SceneGraphPath to this shape node
+ * @param pickShape the PickShape to be intersected
+ * @param dist the closest distance of the intersection
+ *
+ * @return true if the pick shape intersects this node; false
+ * otherwise. If true, dist contains the closest distance of
+ * intersection.
+ *
+ * @exception IllegalArgumentException if pickShape is a PickPoint.
+ * Java 3D doesn't have spatial information of the surface.
+ * Use PickBounds with BoundingSphere and a small radius, instead.
+ *
+ * @exception CapabilityNotSetException if the Geometry.ALLOW_INTERSECT
+ * capability bit is not set in all of the Geometry objects
+ * referred to by this shape node.
+ *
+ * @since Java 3D 1.3
+ */
+ public boolean intersect(SceneGraphPath path,
+ PickShape pickShape,
+ double[] dist) {
+
+ if (isLiveOrCompiled()) {
+ if (!((Shape3DRetained)retained).allowIntersect())
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D6"));
+ }
+
+ if (pickShape instanceof PickPoint) {
+ throw new IllegalArgumentException(J3dI18N.getString("Shape3D7"));
+ }
+
+ return ((Shape3DRetained)this.retained).intersect(path, pickShape, dist);
+ }
+
+
+ /**
+ * Sets a flag that indicates whether this node's appearance can
+ * be overridden. If the flag is true, then this node's
+ * appearance may be overridden by an AlternateAppearance leaf
+ * node, regardless of the value of the ALLOW_APPEARANCE_WRITE
+ * capability bit.
+ * The default value is false.
+ *
+ * @param flag the apperance override enable flag.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see AlternateAppearance
+ *
+ * @since Java 3D 1.2
+ */
+ public void setAppearanceOverrideEnable(boolean flag) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_APPEARANCE_OVERRIDE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D8"));
+
+ ((Shape3DRetained)this.retained).setAppearanceOverrideEnable(flag);
+ }
+
+ /**
+ * Retrieves the appearanceOverrideEnable flag for this node.
+ * @return true if the appearance can be overridden; false
+ * otherwise.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getAppearanceOverrideEnable() {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_APPEARANCE_OVERRIDE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Shape3D9"));
+
+ return ((Shape3DRetained)this.retained).getAppearanceOverrideEnable();
+ }
+
+ /**
+ * 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) {
+ Shape3D s = new Shape3D();
+ 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>Shape3D</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+
+
+
+ /**
+ * Copies all Shape3D 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ Shape3DRetained attr = (Shape3DRetained) originalNode.retained;
+ Shape3DRetained rt = (Shape3DRetained) retained;
+
+ rt.setAppearance((Appearance) getNodeComponent(
+ attr.getAppearance(),
+ forceDuplicate,
+ originalNode.nodeHashtable));
+ int num = attr.numGeometries(id);
+ if (num > 0) {
+ rt.setGeometry((Geometry) getNodeComponent(
+ attr.getGeometry(0, id),
+ forceDuplicate,
+ originalNode.nodeHashtable), 0);
+ for(int i=1; i< num; i++) {
+ rt.addGeometry((Geometry) getNodeComponent(
+ attr.getGeometry(i, id),
+ forceDuplicate,
+ originalNode.nodeHashtable));
+ }
+ }
+
+ rt.setCollisionBounds(attr.getCollisionBounds(id));
+ }
+
+ /**
+ * See parent class for the documentation on getBounds().
+ */
+ public Bounds getBounds() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_BOUNDS_READ)) {
+ throw new CapabilityNotSetException(J3dI18N.getString("Node2"));
+ }
+ } else {
+ // this will throw a SceneGraphCycleException if there is
+ // a cycle
+ checkForCycle();
+ }
+
+ return ((Shape3DRetained)this.retained).getBounds();
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/Shape3DCompileRetained.java b/src/classes/share/javax/media/j3d/Shape3DCompileRetained.java
new file mode 100644
index 0000000..7acd916
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Shape3DCompileRetained.java
@@ -0,0 +1,504 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+/**
+ * A leaf node that holds a merged shapes in compile mode
+ */
+class Shape3DCompileRetained extends Shape3DRetained {
+
+
+ int numShapes = 0;
+
+ // Each element in the arraylist is an array of geometries for a
+ // particular merged shape
+ ArrayList geometryInfo = null;
+
+
+ Object[] srcList = null;
+
+ Shape3DCompileRetained(Shape3DRetained[] shapes, int nShapes, int compileFlags) {
+ int i, j;
+ Shape3DRetained shape;
+ GeometryArrayRetained geo;
+ Vector list;
+ // Merged list, only merged if geometry is mergeable
+ Object[] mergedList = new Object[GeometryRetained.GEO_TYPE_GEOMETRYARRAY+1];
+ // Sorted list of separate geometry by geoType
+ Object[] separateList = new Object[GeometryRetained.GEO_TYPE_GEOMETRYARRAY+1];
+
+ // Assign the num of shapes
+ numShapes = nShapes;
+
+ Bounds shapeBounds;
+
+ srcList = new Object[nShapes];
+
+ if (nShapes > 0) {
+ boundsAutoCompute = shapes[0].boundsAutoCompute;
+ source = shapes[0].source;
+ }
+
+ // Remove the null that was added by Shape3DRetained constructor
+ geometryList.remove(0);
+ int geoIndex = 0;
+
+
+
+ // Assign the fields for this compile shape
+ boundsAutoCompute = shapes[0].boundsAutoCompute;
+ isPickable = shapes[0].isPickable;
+ isCollidable = shapes[0].isCollidable;
+ appearanceOverrideEnable = shapes[0].appearanceOverrideEnable;
+ appearance = shapes[0].appearance;
+ collisionBound = shapes[0].collisionBound;
+ localBounds = shapes[0].localBounds;
+
+
+ if ((compileFlags & CompileState.GEOMETRY_READ) != 0)
+ geometryInfo = new ArrayList();
+
+ for (i = 0; i < nShapes; i++) {
+ shape = shapes[i];
+ ((Shape3D)shape.source).id = i;
+ shape.source.retained = this;
+ srcList[i] = shape.source;
+ // If the transform has been pushd down
+ // to the shape, don't merge its geometry with other shapes
+ // geometry
+ // Put it in a separate list sorted by geo_type
+ // Have to handle shape.isPickable
+
+ for (j = 0; j < shape.geometryList.size(); j++) {
+ geo = (GeometryArrayRetained)shape.geometryList.get(j);
+ if (geo != null) {
+ if (shape.willRemainOpaque(geo.geoType) && geo.isMergeable()) {
+ if (mergedList[geo.geoType] == null) {
+ mergedList[geo.geoType] = new ArrayList();
+ }
+ ((ArrayList)mergedList[geo.geoType]).add(geo);
+ }
+ else {
+ // Keep a sorted list based on geoType;
+ if (separateList[geo.geoType] == null) {
+ separateList[geo.geoType] = new ArrayList();
+ }
+ // add it to the geometryList separately
+ ((ArrayList)separateList[geo.geoType]).add(geo);
+ }
+ }
+
+ }
+
+ // Point to the geometryList's source, so the
+ // retained side will be garbage collected
+ if ((compileFlags & CompileState.GEOMETRY_READ) != 0) {
+ ArrayList sList = new ArrayList();
+ for (j = 0; j < shape.geometryList.size(); j++) {
+ GeometryRetained g = (GeometryRetained)shape.geometryList.get(j);
+ if (g != null)
+ sList.add(g.source);
+ else
+ sList.add(null);
+ }
+ geometryInfo.add(sList);
+ }
+
+ }
+ // Now, merged the mergelist and separate list based on geoType,
+ // this enables dlist optmization
+ for (i = 1; i <= GeometryRetained.GEO_TYPE_GEOMETRYARRAY; i++) {
+ GeometryArrayRetained cgeo = null;
+ ArrayList curList;
+ switch (i) {
+ case GeometryArrayRetained.GEO_TYPE_QUAD_SET:
+ if (mergedList[i] != null) {
+ cgeo = new QuadArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_TRI_SET:
+ if (mergedList[i] != null) {
+ cgeo = new TriangleArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_POINT_SET:
+ if (mergedList[i] != null) {
+ cgeo = new PointArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_LINE_SET:
+ if (mergedList[i] != null) {
+ cgeo = new LineArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_TRI_STRIP_SET:
+ if (mergedList[i] != null) {
+ cgeo = new TriangleStripArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_TRI_FAN_SET:
+ if (mergedList[i] != null) {
+ cgeo = new TriangleFanArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_LINE_STRIP_SET:
+ if (mergedList[i] != null) {
+ cgeo = new LineStripArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_INDEXED_QUAD_SET:
+ if (mergedList[i] != null) {
+ cgeo = new IndexedQuadArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_INDEXED_TRI_SET:
+ if (mergedList[i] != null) {
+ cgeo = new IndexedTriangleArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_INDEXED_POINT_SET:
+ if (mergedList[i] != null) {
+ cgeo = new IndexedPointArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_INDEXED_LINE_SET:
+ if (mergedList[i] != null) {
+ cgeo = new IndexedLineArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET:
+ if (mergedList[i] != null) {
+ cgeo = new IndexedTriangleStripArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_INDEXED_TRI_FAN_SET:
+ if (mergedList[i] != null) {
+ cgeo = new IndexedTriangleFanArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ case GeometryArrayRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET:
+ if (mergedList[i] != null) {
+ cgeo = new IndexedLineStripArrayRetained();
+ curList = (ArrayList)mergedList[i];
+ cgeo.setCompiled(curList);
+ geometryList.add(cgeo);
+ cgeo.setSource(((SceneGraphObjectRetained)curList.get(0)).source);
+ }
+ if (separateList[i] != null) {
+ ArrayList glist = (ArrayList)separateList[i];
+ for (int k = 0; k < glist.size(); k++) {
+ geometryList.add(glist.get(k));
+ }
+
+ }
+ break;
+ }
+ }
+
+
+ }
+
+
+ Bounds getCollisionBounds(int childIndex) {
+ return collisionBound;
+ }
+
+
+ int numGeometries(int childIndex) {
+ ArrayList geo = (ArrayList) geometryInfo.get(childIndex);
+ return geo.size();
+ }
+
+
+ Geometry getGeometry(int i, int childIndex) {
+ ArrayList geoInfo = (ArrayList) geometryInfo.get(childIndex);
+ return (Geometry)geoInfo.get(i);
+
+
+ }
+
+ Enumeration getAllGeometries(int childIndex) {
+ ArrayList geoInfo = (ArrayList) geometryInfo.get(childIndex);
+ Vector geomList = new Vector();
+
+ for(int i=0; i<geoInfo.size(); i++) {
+ geomList.add(geoInfo.get(i));
+ }
+
+ return geomList.elements();
+ }
+
+ Bounds getBounds(int childIndex) {
+ if(boundsAutoCompute) {
+ ArrayList glist = (ArrayList) geometryInfo.get(childIndex);
+
+ if(glist != null) {
+ BoundingBox bbox = new BoundingBox((Bounds) null);
+ for(int i=0; i<glist.size(); i++) {
+ Geometry g = (Geometry) glist.get(i);
+ if (g != null) {
+ GeometryRetained geometry = (GeometryRetained)g.retained;
+ if (geometry.geoType != GeometryRetained.GEO_TYPE_NONE) {
+ geometry.computeBoundingBox();
+ synchronized(geometry.geoBounds) {
+ bbox.combine(geometry.geoBounds);
+ }
+ }
+ }
+ }
+
+ return (Bounds) bbox;
+
+ } else {
+ return null;
+ }
+
+ } else {
+ return super.getBounds();
+ }
+ }
+
+
+ /**
+ * Check if the geometry component of this shape node under path
+ * intersects with the pickRay.
+ * @return true if intersected else false. If return is true, dist
+ * contains the closest
+ * distance of intersection.
+ * @exception IllegalArgumentException if <code>path</code> is
+ * invalid.
+ */
+
+ boolean intersect(SceneGraphPath path,
+ PickShape pickShape, double[] dist) {
+
+ // This method will not do bound intersect check, as it assume
+ // caller has already done that. ( For performance and code
+ // simplification reasons. )
+
+ Transform3D localToVworld = path.getTransform();
+ int i;
+
+ if (localToVworld == null) {
+ throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3"));
+ }
+
+ Transform3D t3d = VirtualUniverse.mc.getTransform3D(null);
+ t3d.invert(localToVworld);
+ PickShape newPS = pickShape.transform(t3d);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3d);
+
+ Shape3D shape = (Shape3D) path.getObject();
+ // Get the geometries for this shape only, since the compiled
+ // geomtryList contains several shapes
+ ArrayList glist = (ArrayList) geometryInfo.get(shape.id);
+
+ int geomListSize = glist.size();
+
+ Point3d iPnt = Shape3DRetained.getPoint3d();
+ GeometryRetained geometry;
+
+ if (dist == null) {
+ for (i=0; i < geomListSize; i++) {
+ Geometry g = (Geometry) glist.get(i);
+ if (g != null && g.retained != null) {
+ geometry = (GeometryRetained)g.retained;
+ if (geometry.mirrorGeometry != null) {
+ geometry = geometry.mirrorGeometry;
+ }
+
+ if (geometry.intersect(newPS, null, iPnt)) {
+ Shape3DRetained.freePoint3d(iPnt);
+ return true;
+ }
+ }
+ }
+ } else {
+ double minDist = Double.POSITIVE_INFINITY;
+ // TODO : BugId 4351579 -- Need to return the index of nearest
+ // intersected geometry too.
+
+ for (i=0; i < geomListSize; i++) {
+ Geometry g = (Geometry) glist.get(i);
+ if (g != null && g.retained != null) {
+ geometry = (GeometryRetained)g.retained;
+ if (geometry.mirrorGeometry != null) {
+ geometry = geometry.mirrorGeometry;
+ }
+ if (geometry.intersect(newPS, dist, iPnt)) {
+ localToVworld.transform(iPnt);
+ dist[0] = pickShape.distance(iPnt);
+ if (minDist > dist[0]) {
+ minDist = dist[0];
+ }
+ }
+ }
+ }
+
+ if (minDist < Double.POSITIVE_INFINITY) {
+ dist[0] = minDist;
+ Shape3DRetained.freePoint3d(iPnt);
+ return true;
+ }
+ }
+
+ Shape3DRetained.freePoint3d(iPnt);
+ return false;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Shape3DRetained.java b/src/classes/share/javax/media/j3d/Shape3DRetained.java
new file mode 100644
index 0000000..f115c99
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Shape3DRetained.java
@@ -0,0 +1,2770 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * A shape leaf node consisting of geometry and appearance properties.
+ */
+
+class Shape3DRetained extends LeafRetained {
+
+ static final int GEOMETRY_CHANGED = 0x00001;
+ static final int APPEARANCE_CHANGED = 0x00002;
+ static final int COLLISION_CHANGED = 0x00004;
+ static final int BOUNDS_CHANGED = 0x00008;
+ static final int APPEARANCEOVERRIDE_CHANGED = 0x00010;
+ static final int LAST_DEFINED_BIT = 0x00010;
+
+
+ // Target threads to be notified when light changes
+ static final int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER;
+
+ /**
+ * The appearance component of the shape node.
+ */
+ AppearanceRetained appearance = null;
+
+ /**
+ * The arraylist of geometry component of the shape node.
+ */
+ ArrayList geometryList = null;
+
+ /**
+ * A 2D storage of all geometry atoms associated with this shape node.
+ * There may be more than one geometry for a Shape3D node.
+ * Do not change the following private variables to public, its access need to synchronize
+ * via mirrorShape3DLock.
+ */
+
+ // geomAtomArr should always be a 1 element array, unless S3D contains multiple Text3Ds.
+ private GeometryAtom geomAtom = null;
+
+ /**
+ * To sychronize access of the mirrorShape3D's geomAtomArray*.
+ * A multiple read single write Lock to sychronize access into mirrorShape3D.
+ * To prevent deadlock a call to read/write lock must end with a read/write unlock
+ * respectively.
+ */
+ private MRSWLock mirrorShape3DLock = null;
+
+ /**
+ * The mirror Shape3DRetained nodes for this object. There is one
+ * mirror for each instance of this Shape3D node. If it is not in
+ * a SharedGroup, only index 0 is valid.
+ * Do not change the following private variables to public, its access need to synchronize
+ * via mirrorShape3DLock.
+ */
+ ArrayList mirrorShape3D = new ArrayList(1);
+
+ /**
+ * This field is used for mirror Shape3D nodes accessing their
+ * original nodes. It is a NodeRetained because the original
+ * node may be a Shape3DRetained or a MorphRetained node.
+ */
+ NodeRetained sourceNode = null;
+
+ /**
+ * The hashkey for this Shape3DRetained mirror object
+ */
+ HashKey key = null;
+
+ // This is true when this geometry is referenced in an IMM mode context
+ boolean inImmCtx = false;
+
+ // A bitmask to indicate when something has changed
+ int isDirty = 0xffff;
+
+ // The list of lights that are scoped to this node
+ LightRetained[] lights =null;
+
+ // The number of lights in the above array, may be less than lights.length
+ int numlights = 0;
+
+ // The list of fogs that are scoped to this node
+ FogRetained[] fogs = null;
+
+ // The number of fogs in the above array, may be less than fogs.length
+ int numfogs = 0;
+
+ // The list of modelClips that are scoped to this node
+ ModelClipRetained[] modelClips = null;
+
+ // The number of modelClips in the above array, may be less than modelClips.length
+ int numModelClips = 0;
+
+ // The list of alt app that are scoped to this node
+ AlternateAppearanceRetained[] altApps = null;
+
+ //The number of alt app in the above array, may be less than alt app.length
+ int numAltApps = 0;
+
+ /**
+ * Reference to the BranchGroup path of this mirror shape
+ * This is used for picking and GeometryStructure only.
+ */
+ BranchGroupRetained branchGroupPath[];
+
+ // cache value for picking in mirror shape.
+ // True if all the node of the path from this to root are all pickable
+ boolean isPickable = true;
+
+ // cache value for collidable in mirror shape.
+ // True if all the node of the path from this to root are all collidable
+ boolean isCollidable = true;
+
+ // closest switch parent
+ SwitchRetained closestSwitchParent = null;
+
+ // the child index from the closest switch parent
+ int closestSwitchIndex = -1;
+
+ // Is this S3D visible ? The default is true.
+ boolean visible = true;
+
+ // Whether the normal appearance is overrided by the alternate app
+ boolean appearanceOverrideEnable = false;
+
+ // AlternateAppearance retained that is applicable to this
+ // mirror shape when the override flag is true
+ AppearanceRetained otherAppearance = null;
+
+ // geometry Bounds in local coordinate
+ Bounds bounds = null;
+
+ // geometry Bounds in virtual world coordinate
+ BoundingBox vwcBounds = null;
+
+ // collision Bounds in local coordinate
+ Bounds collisionBound = null;
+
+ // collision Bounds in virtual world coordinate
+ Bounds collisionVwcBound = null;
+
+ // a path of OrderedGroup, childrenId pairs which leads to this node
+ OrderedPath orderedPath = null;
+
+ // List of views that a mirror object is scoped to
+ ArrayList viewList = null;
+
+ int changedFrequent = 0;
+
+ Shape3DRetained() {
+ super();
+ this.nodeType = NodeRetained.SHAPE;
+ numlights = 0;
+ numfogs = 0;
+ numModelClips = 0;
+ numAltApps = 0;
+ localBounds = new BoundingBox((BoundingBox) null);
+
+ mirrorShape3DLock = new MRSWLock();
+ geometryList = new ArrayList(1);
+ geometryList.add(null);
+ }
+
+ /**
+ * Sets the collision bounds of a node.
+ * @param bounds the bounding object for the node
+ */
+ void setCollisionBounds(Bounds bounds) {
+ if (bounds == null) {
+ this.collisionBound = null;
+ } else {
+ this.collisionBound = (Bounds)bounds.clone();
+ }
+
+ if (source.isLive()) {
+ // Notify Geometry Structure to check for collision
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.COLLISION_BOUND_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM;
+ message.universe = universe;
+ message.args[0] = getGeomAtomsArray(mirrorShape3D);
+ // no need to clone collisionBound
+ message.args[1] = collisionBound;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ Bounds getLocalBounds(Bounds bounds) {
+ if(localBounds != null) {
+ localBounds.set(bounds);
+ }
+ else {
+ localBounds = new BoundingBox(bounds);
+ }
+ return localBounds;
+ }
+
+
+ /**
+ * Sets the geometric bounds of a node.
+ * @param bounds the bounding object for the node
+ */
+ void setBounds(Bounds bounds) {
+ super.setBounds(bounds);
+
+ if (source.isLive() && !boundsAutoCompute) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.REGION_BOUND_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_RENDER;
+
+ message.universe = universe;
+ message.args[0] = getGeomAtomsArray(mirrorShape3D);
+ // no need to clone localBounds
+ message.args[1] = localBounds;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ /**
+ * Gets the collision bounds of a node.
+ * @return the node's bounding object
+ */
+ Bounds getCollisionBounds(int id) {
+ return (collisionBound == null ?
+ null: (Bounds)collisionBound.clone());
+ }
+
+ /**
+ * Appends the specified geometry component to this Shape3D
+ * node's list of geometry components.
+ * If there are existing geometry components in the list, the new
+ * geometry component must be of the same equivalence class
+ * (point, line, polygon, CompressedGeometry, Raster, Text3D) as
+ * the others.
+ * @param geometry the geometry component to be appended.
+ * @exception IllegalArgumentException if the new geometry
+ * component is not of of the same equivalence class as the
+ * existing geometry components.
+ *
+ * @since Java 3D 1.2
+ */
+ void addGeometry(Geometry geometry) {
+ int i;
+ Shape3DRetained s;
+ GeometryRetained newGeom = null;
+
+ checkEquivalenceClass(geometry, -1);
+
+ if(((Shape3D)this.source).isLive()) {
+ if (geometry != null) {
+
+ newGeom = ((GeometryRetained)geometry.retained);
+ newGeom.setLive(inBackgroundGroup, refCount);
+
+ geometryList.add(newGeom);
+
+ } else {
+ geometryList.add(null);
+ newGeom = null;
+ }
+ sendDataChangedMessage(newGeom);
+
+ } else {
+ if (geometry != null) {
+ geometryList.add((GeometryRetained) geometry.retained);
+ } else {
+ geometryList.add(null);
+ }
+ }
+
+ }
+
+ /**
+ * Replaces the geometry component at the specified index in this
+ * Shape3D node's list of geometry components with the specified
+ * geometry component.
+ * If there are existing geometry components in the list (besides
+ * the one being replaced), the new geometry component must be of
+ * the same equivalence class (point, line, polygon, CompressedGeometry,
+ * Raster, Text3D) as the others.
+ * @param geometry the geometry component to be stored at the
+ * specified index.
+ * @param index the index of the geometry component to be replaced.
+ * @exception IllegalArgumentException if the new geometry
+ * component is not of of the same equivalence class as the
+ * existing geometry components.
+ *
+ * @since Java 3D 1.2
+ */
+ void setGeometry(Geometry geometry, int index) {
+ int i;
+ Shape3DRetained mShape;
+ GeometryRetained newGeom = null;
+ GeometryRetained oldGeom = null;
+
+ checkEquivalenceClass(geometry, index);
+
+ if (((Shape3D)this.source).isLive()) {
+
+ oldGeom = (GeometryRetained) (geometryList.get(index));
+ if (oldGeom != null) {
+ oldGeom.clearLive(refCount);
+ for (i=0; i<mirrorShape3D.size(); i++) {
+ mShape = (Shape3DRetained)mirrorShape3D.get(i);
+ oldGeom.removeUser(mShape);
+ }
+ oldGeom.decRefCnt();
+ }
+
+ if (geometry != null) {
+ newGeom = (GeometryRetained) geometry.retained;
+ newGeom.incRefCnt();
+ newGeom.setLive(inBackgroundGroup, refCount);
+ geometryList.set(index, newGeom);
+ sendDataChangedMessage(newGeom);
+ } else {
+ geometryList.set(index, null);
+ sendDataChangedMessage(null);
+ }
+
+ } else {
+
+ oldGeom = (GeometryRetained) (geometryList.get(index));
+ if (oldGeom != null) {
+ oldGeom.decRefCnt();
+ }
+ if (geometry != null) {
+ geometryList.set(index,(GeometryRetained) geometry.retained);
+ ((GeometryRetained)geometry.retained).incRefCnt();
+ } else {
+ geometryList.set(index,null);
+ }
+ }
+ }
+
+ /**
+ * Inserts the specified geometry component into this Shape3D
+ * node's list of geometry components at the specified index.
+ * If there are existing geometry components in the list, the new
+ * geometry component must be of the same equivalence class
+ * (point, line, polygon, CompressedGeometry, Raster, Text3D) as
+ * the others.
+ * @param geometry the geometry component to be inserted at the
+ * specified index.
+ * @param index the index at which the geometry component is inserted.
+ *
+ * @since Java 3D 1.2
+ */
+ void insertGeometry(Geometry geometry, int index) {
+ int i;
+ Shape3DRetained mShape;
+ GeometryRetained newGeom = null;
+ GeometryRetained oldGeom = null;
+
+ checkEquivalenceClass(geometry, -1);
+
+ if (((Shape3D)this.source).isLive()) {
+
+ if (geometry != null) {
+ // Note : The order of the statements in important. Want ArrayList class to do index bounds
+ // check before creating internal object.
+ newGeom = (GeometryRetained) geometry.retained;
+ newGeom.incRefCnt();
+ geometryList.add(index, newGeom);
+ newGeom.setLive(inBackgroundGroup, refCount);
+ sendDataChangedMessage(newGeom);
+ } else {
+ geometryList.add(index, null);
+ sendDataChangedMessage(null);
+ }
+
+ } else {
+
+ if (geometry != null) {
+ geometryList.add(index,(GeometryRetained) geometry.retained);
+ ((GeometryRetained)geometry.retained).incRefCnt();
+ } else {
+ geometryList.add(index,null);
+ }
+ }
+ }
+
+ /**
+ * Removes the geometry component at the specified index from
+ * this Shape3D node's list of geometry components.
+ * @param index the index of the geometry component to be removed.
+ *
+ * @since Java 3D 1.2
+ */
+ void removeGeometry(int index) {
+ int i;
+ Shape3DRetained mShape;
+ GeometryRetained oldGeom = null;
+
+ if (((Shape3D)this.source).isLive()) {
+
+ oldGeom = (GeometryRetained) (geometryList.get(index));
+ if (oldGeom != null) {
+ oldGeom.clearLive(refCount);
+ oldGeom.decRefCnt();
+ for (i=0; i<mirrorShape3D.size(); i++) {
+ mShape = (Shape3DRetained)mirrorShape3D.get(i);
+ oldGeom.removeUser(mShape);
+
+ }
+ }
+
+ geometryList.remove(index);
+ sendDataChangedMessage(null);
+
+ } else {
+ oldGeom = (GeometryRetained) (geometryList.get(index));
+ if (oldGeom != null) {
+ oldGeom.decRefCnt();
+ }
+ geometryList.remove(index);
+ }
+
+
+
+ }
+
+ /**
+ * Retrieves the geometry component of this Shape3D node.
+ * @return the geometry component of this shape node
+ *
+ * @since Java 3D 1.2
+ */
+ Geometry getGeometry(int index, int id) {
+ GeometryRetained ga = (GeometryRetained) geometryList.get(index);
+ return (ga == null ? null : (Geometry)ga.source);
+ }
+
+
+ /**
+ * Returns an enumeration of this Shape3D node's list of geometry
+ * components.
+ * @return an Enumeration object containing all geometry components in
+ * this Shape3D node's list of geometry components.
+ *
+ * @since Java 3D 1.2
+ */
+ Enumeration getAllGeometries(int id) {
+ GeometryRetained ga = null;
+ Vector geomList = new Vector(geometryList.size());
+
+ for(int i=0; i<geometryList.size(); i++) {
+ ga = (GeometryRetained) geometryList.get(i);
+ if(ga != null)
+ geomList.add((Geometry)ga.source);
+ else
+ geomList.add(null);
+ }
+
+ return geomList.elements();
+ }
+
+ /**
+ * Returns the number of geometry components in this Shape3D node's
+ * list of geometry components.
+ * @return the number of geometry components in this Shape3D node's
+ * list of geometry components.
+ *
+ * @since Java 3D 1.2
+ */
+ int numGeometries(int id) {
+
+ return geometryList.size();
+ }
+
+ /**
+ * Sets the appearance component of this Shape3D node.
+ * @param appearance the new apearance component for this shape node
+ */
+ void setAppearance(Appearance newAppearance) {
+
+ Shape3DRetained s;
+ boolean visibleIsDirty = false;
+
+ if (((Shape3D)this.source).isLive()) {
+ if (appearance != null) {
+ appearance.clearLive(refCount);
+ for (int i=0; i<mirrorShape3D.size(); i++) {
+ s = (Shape3DRetained)mirrorShape3D.get(i);
+ appearance.removeAMirrorUser(s);
+ }
+ }
+
+ if (newAppearance != null) {
+ ((AppearanceRetained)newAppearance.retained).setLive(inBackgroundGroup, refCount);
+ appearance = ((AppearanceRetained)newAppearance.retained);
+ for (int i=0; i<mirrorShape3D.size(); i++) {
+ s = (Shape3DRetained)mirrorShape3D.get(i);
+ appearance.addAMirrorUser(s);
+ }
+ if((appearance.renderingAttributes != null) &&
+ (visible != appearance.renderingAttributes.visible)) {
+ visible = appearance.renderingAttributes.visible;
+ visibleIsDirty = true;
+ }
+ }
+ else {
+ if(visible == false) {
+ visible = true;
+ visibleIsDirty = true;
+ }
+ }
+ int size = 0;
+ if (visibleIsDirty)
+ size = 2;
+ else
+ size = 1;
+ J3dMessage[] createMessage = new J3dMessage[size];
+ // Send a message
+ createMessage[0] = VirtualUniverse.mc.getMessage();
+ createMessage[0].threads = targetThreads;
+ createMessage[0].type = J3dMessage.SHAPE3D_CHANGED;
+ createMessage[0].universe = universe;
+ createMessage[0].args[0] = this;
+ createMessage[0].args[1]= new Integer(APPEARANCE_CHANGED);
+ Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()];
+ mirrorShape3D.toArray(s3dArr);
+ createMessage[0].args[2] = s3dArr;
+ Object[] obj = new Object[2];
+ if (newAppearance == null) {
+ obj[0] = null;
+ }
+ else {
+ obj[0] = appearance.mirror;
+ }
+ obj[1] = new Integer(changedFrequent);
+ createMessage[0].args[3] = obj;
+ createMessage[0].args[4] = getGeomAtomsArray(mirrorShape3D);
+ if(visibleIsDirty) {
+ createMessage[1] = VirtualUniverse.mc.getMessage();
+ createMessage[1].threads = J3dThread.UPDATE_GEOMETRY;
+ createMessage[1].type = J3dMessage.SHAPE3D_CHANGED;
+ createMessage[1].universe = universe;
+ createMessage[1].args[0] = this;
+ createMessage[1].args[1]= new Integer(APPEARANCE_CHANGED);
+ createMessage[1].args[2]= visible?Boolean.TRUE:Boolean.FALSE;
+ createMessage[1].args[3]= createMessage[0].args[4];
+ }
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ }
+ else { // not live.
+ if (newAppearance == null) {
+ appearance = null;
+ } else {
+ appearance = (AppearanceRetained) newAppearance.retained;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the shape node's appearance component.
+ * @return the shape node's appearance
+ */
+ Appearance getAppearance() {
+ return (appearance == null ? null: (Appearance) appearance.source);
+ }
+
+
+ /**
+ * Check if the geometry component of this shape node under path
+ * intersects with the pickShape.
+ * This is an expensive method. It should only be called if and only
+ * if the path's bound intersects pickShape.
+ * @exception IllegalArgumentException if <code>path</code> is
+ * invalid.
+ */
+
+ boolean intersect(SceneGraphPath path,
+ PickShape pickShape, double[] dist) {
+
+ // This method will not do bound intersect check, as it assume
+ // caller has already done that. ( For performance and code
+ // simplification reasons. )
+
+ Transform3D localToVworld = path.getTransform();
+ int i;
+
+ if (localToVworld == null) {
+ throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3"));
+ }
+
+ // Support OrientedShape3D here.
+ // TODO - BugId : 4363899 - APIs issue : OrientedShape3D's intersect needs view
+ // info. temp. fix use the primary view.
+ if (this instanceof OrientedShape3DRetained) {
+ Transform3D orientedTransform = ((OrientedShape3DRetained)this).
+ getOrientedTransform(getPrimaryViewIdx());
+ localToVworld.mul(orientedTransform);
+ }
+
+ Transform3D t3d = VirtualUniverse.mc.getTransform3D(null);
+ t3d.invert(localToVworld);
+ PickShape newPS = pickShape.transform(t3d);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3d);
+
+ // TODO: For optimization - Should do a geobounds check of
+ // each geometry first. But this doesn't work for
+ // OrientedShape3D case...
+ int geomListSize = geometryList.size();
+ Point3d iPnt = getPoint3d();
+ GeometryRetained geometry;
+
+ if (dist == null) {
+ for (i=0; i < geomListSize; i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if (geometry != null) {
+ if (geometry.mirrorGeometry != null) {
+ geometry = geometry.mirrorGeometry;
+ }
+ if (geometry.intersect(newPS, null, iPnt)) {
+ freePoint3d(iPnt);
+ return true;
+ }
+ }
+ }
+ } else {
+ double minDist = Double.POSITIVE_INFINITY;
+ // TODO : BugId 4351579 -- Need to return the index of nearest
+ // intersected geometry too.
+
+ for (i=0; i < geomListSize; i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if (geometry != null) {
+ if (geometry.mirrorGeometry != null) {
+ geometry = geometry.mirrorGeometry;
+ }
+ if (geometry.intersect(newPS, dist, iPnt)) {
+ localToVworld.transform(iPnt);
+ dist[0] = pickShape.distance(iPnt);
+ if (minDist > dist[0]) {
+ minDist = dist[0];
+ }
+ }
+ }
+ }
+
+ if (minDist < Double.POSITIVE_INFINITY) {
+ dist[0] = minDist;
+ freePoint3d(iPnt);
+ return true;
+ }
+ }
+
+ freePoint3d(iPnt);
+
+ return false;
+ }
+
+ void setAppearanceOverrideEnable(boolean flag) {
+ if (((Shape3D)this.source).isLive()) {
+
+ // Send a message
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.SHAPE3D_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(APPEARANCEOVERRIDE_CHANGED);
+ Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()];
+ mirrorShape3D.toArray(s3dArr);
+ createMessage.args[2] = s3dArr;
+ Object[] obj = new Object[2];
+ if (flag) {
+ obj[0] = Boolean.TRUE;
+ }
+ else {
+ obj[0] = Boolean.FALSE;
+ }
+ obj[1] = new Integer(changedFrequent);
+ createMessage.args[3] = obj;
+ createMessage.args[4] = getGeomAtomsArray(mirrorShape3D);
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ appearanceOverrideEnable = flag;
+ }
+
+ boolean getAppearanceOverrideEnable() {
+ return appearanceOverrideEnable;
+ }
+
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+ /**
+ * This updates the mirror shape to reflect the state of the
+ * real shape3d.
+ */
+ private void initMirrorShape3D(SetLiveState s, Shape3DRetained ms, int index) {
+
+ // New 1.2.1 code
+
+ ms.inBackgroundGroup = inBackgroundGroup;
+ ms.geometryBackground = geometryBackground;
+ ms.source = source;
+ ms.universe = universe;
+ // Has to be false. We have a instance of mirror for every link to the shape3d.
+ ms.inSharedGroup = false;
+ ms.locale = locale;
+ ms.parent = parent;
+
+ OrderedPath op = (OrderedPath)s.orderedPaths.get(index);
+ if (op.pathElements.size() == 0) {
+ ms.orderedPath = null;
+ } else {
+ ms.orderedPath = op;
+/*
+ System.out.println("initMirrorShape3D ms.orderedPath ");
+ ms.orderedPath.printPath();
+*/
+ }
+
+ // all mirror shapes point to the same transformGroupRetained
+ // for the static transform
+ ms.staticTransform = staticTransform;
+
+
+ ms.appearanceOverrideEnable = appearanceOverrideEnable;
+
+ ms.geometryList = geometryList;
+
+ // Assign the parent of this mirror shape node
+ ms.sourceNode = this;
+
+ if (this instanceof OrientedShape3DRetained) {
+ OrientedShape3DRetained os = (OrientedShape3DRetained)this;
+ OrientedShape3DRetained oms = (OrientedShape3DRetained)ms;
+ oms.initAlignmentMode(os.mode);
+ oms.initAlignmentAxis(os.axis);
+ oms.initRotationPoint(os.rotationPoint);
+ oms.initConstantScaleEnable(os.constantScale);
+ oms.initScale(os.scaleFactor);
+ }
+
+ }
+
+ void updateImmediateMirrorObject(Object[] objs) {
+ int component = ((Integer)objs[1]).intValue();
+ GeometryArrayRetained ga;
+
+ Shape3DRetained[] msArr = (Shape3DRetained[]) objs[2];
+ int i, j;
+ if ((component & APPEARANCE_CHANGED) != 0) {
+ Object[] arg = (Object[])objs[3];
+ int val = ((Integer)arg[1]).intValue();
+ for ( i = msArr.length-1; i >=0; i--) {
+ msArr[i].appearance = (AppearanceRetained)arg[0];
+ msArr[i].changedFrequent = val;
+ }
+ }
+ if ((component & APPEARANCEOVERRIDE_CHANGED) != 0) {
+ Object[] arg = (Object[])objs[3];
+ int val = ((Integer)arg[1]).intValue();
+ for ( i = msArr.length-1; i >=0; i--) {
+ msArr[i].appearanceOverrideEnable = ((Boolean)arg[0]).booleanValue();
+ msArr[i].changedFrequent = val;
+ }
+ }
+ }
+
+ /**
+ * Gets the bounding object of a node.
+ * @return the node's bounding object
+ */
+
+ Bounds getBounds() {
+
+ if(boundsAutoCompute) {
+ // System.out.println("getBounds ---- localBounds is " + localBounds);
+
+
+ if(geometryList != null) {
+ BoundingBox bbox = new BoundingBox((Bounds) null);
+ GeometryRetained geometry;
+ for(int i=0; i<geometryList.size(); i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if ((geometry != null) &&
+ (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) {
+ geometry.computeBoundingBox();
+ synchronized(geometry.geoBounds) {
+ bbox.combine(geometry.geoBounds);
+ }
+ }
+ }
+ return (Bounds) bbox;
+
+ } else {
+ return null;
+ }
+
+ } else {
+ return super.getBounds();
+ }
+ }
+
+ Bounds getEffectiveBounds() {
+ if(boundsAutoCompute) {
+ return getBounds();
+ }
+ else {
+ return super.getEffectiveBounds();
+ }
+ }
+
+
+ /**
+ * ONLY needed for SHAPE, MORPH, and LINK node type.
+ * Compute the combine bounds of bounds and its localBounds.
+ */
+ void computeCombineBounds(Bounds bounds) {
+
+ if(boundsAutoCompute) {
+
+ if(geometryList != null) {
+ GeometryRetained geometry;
+ BoundingBox bbox = null;
+
+ if (staticTransform != null) {
+ bbox = new BoundingBox((BoundingBox) null);
+ }
+
+ for(int i=0; i<geometryList.size(); i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if ((geometry != null) &&
+ (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) {
+ geometry.computeBoundingBox();
+ // Should this be lock too ? ( MT safe ? )
+ synchronized(geometry.geoBounds) {
+ if (staticTransform != null) {
+ bbox.set(geometry.geoBounds);
+ bbox.transform(staticTransform.transform);
+ bounds.combine((Bounds)bbox);
+ } else {
+ bounds.combine((Bounds)geometry.geoBounds);
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+
+ // Should this be lock too ? ( MT safe ? )
+ synchronized(localBounds) {
+ bounds.combine((Bounds) localBounds);
+ }
+ }
+ }
+
+ /**
+ * assign a name to this node when it is made live.
+ */
+
+ void setLive(SetLiveState s) {
+ doSetLive(s);
+ markAsLive();
+ }
+
+ void doSetLive(SetLiveState s) {
+ // System.out.println("S3DRetained : setLive " + s);
+ Shape3DRetained shape;
+ GeometryRetained geometry;
+ int i, j, k, gaCnt;
+ ArrayList msList = new ArrayList();
+
+ super.doSetLive(s);
+
+ nodeId = universe.getNodeId();
+
+
+ if (inSharedGroup) {
+ for (i=0; i<s.keys.length; i++) {
+ if (this instanceof OrientedShape3DRetained) {
+ shape = new OrientedShape3DRetained();
+ } else {
+ shape = new Shape3DRetained();
+ }
+ shape.key = s.keys[i];
+ shape.localToVworld = new Transform3D[1][];
+ shape.localToVworldIndex = new int[1][];
+
+ j = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ /*
+ System.out.print("s.keys[i] = "+s.keys[i]+" j = "+j);
+ if(j < 0) {
+ System.out.println("Shape3dRetained : Can't find hashKey");
+ }
+ */
+ shape.localToVworld[0] = localToVworld[j];
+ shape.localToVworldIndex[0] = localToVworldIndex[j];
+ shape.branchGroupPath = (BranchGroupRetained []) branchGroupPaths.get(j);
+ shape.isPickable = s.pickable[i];
+ shape.isCollidable = s.collidable[i];
+
+ initMirrorShape3D(s, shape, j);
+
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(shape, Targets.GEO_TARGETS);
+ shape.closestSwitchParent = s.closestSwitchParents[i];
+ shape.closestSwitchIndex = s.closestSwitchIndices[i];
+ }
+ shape.switchState = (SwitchState)s.switchStates.get(j);
+
+
+ // Add any scoped lights to the mirror shape
+ if (s.lights != null) {
+ ArrayList l = (ArrayList)s.lights.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addLight((LightRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped fog
+ if (s.fogs != null) {
+ ArrayList l = (ArrayList)s.fogs.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addFog((FogRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped modelClip
+ if (s.modelClips != null) {
+ ArrayList l = (ArrayList)s.modelClips.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addModelClip((ModelClipRetained)l.get(m));
+ }
+ }
+ }
+
+ // Add any scoped alt app
+ if (s.altAppearances != null) {
+ ArrayList l = (ArrayList)s.altAppearances.get(j);
+ if (l != null) {
+ for (int m = 0; m < l.size(); m++) {
+ shape.addAltApp((AlternateAppearanceRetained)l.get(m));
+ }
+ }
+ }
+ synchronized(mirrorShape3D) {
+ mirrorShape3D.add(j,shape);
+ }
+
+ msList.add(shape);
+ if (s.viewLists != null) {
+ shape.viewList = (ArrayList)s.viewLists.get(j);
+ } else {
+ shape.viewList = null;
+ }
+ }
+ } else {
+ if (this instanceof OrientedShape3DRetained) {
+ shape = new OrientedShape3DRetained();
+ } else {
+ shape = new Shape3DRetained();
+ }
+
+ shape.localToVworld = new Transform3D[1][];
+ shape.localToVworldIndex = new int[1][];
+ shape.localToVworld[0] = localToVworld[0];
+ shape.localToVworldIndex[0] = localToVworldIndex[0];
+ shape.branchGroupPath = (BranchGroupRetained []) branchGroupPaths.get(0);
+ shape.isPickable = s.pickable[0];
+ shape.isCollidable = s.collidable[0];
+ initMirrorShape3D(s, shape, 0);
+
+ // Add any scoped lights to the mirror shape
+ if (s.lights != null) {
+ ArrayList l = (ArrayList)s.lights.get(0);
+ for (i = 0; i < l.size(); i++) {
+ shape.addLight((LightRetained)l.get(i));
+ }
+ }
+
+ // Add any scoped fog
+ if (s.fogs != null) {
+ ArrayList l = (ArrayList)s.fogs.get(0);
+ for (i = 0; i < l.size(); i++) {
+ shape.addFog((FogRetained)l.get(i));
+ }
+ }
+
+ // Add any scoped modelClip
+ if (s.modelClips != null) {
+ ArrayList l = (ArrayList)s.modelClips.get(0);
+ for (i = 0; i < l.size(); i++) {
+ shape.addModelClip((ModelClipRetained)l.get(i));
+ }
+
+ }
+
+ // Add any scoped alt app
+ if (s.altAppearances != null) {
+ ArrayList l = (ArrayList)s.altAppearances.get(0);
+ for (i = 0; i < l.size(); i++) {
+ shape.addAltApp((AlternateAppearanceRetained)l.get(i));
+ }
+ }
+ synchronized(mirrorShape3D) {
+ mirrorShape3D.add(shape);
+ }
+
+ msList.add(shape);
+ if (s.viewLists != null)
+ shape.viewList = (ArrayList)s.viewLists.get(0);
+ else
+ shape.viewList = null;
+
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS);
+ shape.closestSwitchParent = s.closestSwitchParents[0];
+ shape.closestSwitchIndex = s.closestSwitchIndices[0];
+ }
+ shape.switchState = (SwitchState)s.switchStates.get(0);
+ }
+
+ for (k = 0; k < msList.size(); k++) {
+ Shape3DRetained sh = (Shape3DRetained)msList.get(k);
+
+ if (appearance != null) {
+ synchronized(appearance.liveStateLock) {
+ if (k == 0) { // Do only first time
+ appearance.setLive(inBackgroundGroup, s.refCount);
+ appearance.initMirrorObject();
+ if (appearance.renderingAttributes != null)
+ visible = appearance.renderingAttributes.visible;
+ }
+ sh.appearance = (AppearanceRetained)appearance.mirror;
+ appearance.addAMirrorUser(sh);
+
+ }
+ }
+ else {
+ sh.appearance = null;
+ }
+
+ if (geometryList != null) {
+ for(gaCnt=0; gaCnt<geometryList.size(); gaCnt++) {
+ geometry = (GeometryRetained) geometryList.get(gaCnt);
+ if(geometry != null) {
+ synchronized(geometry.liveStateLock) {
+ if (k == 0) { // Do only first time
+ geometry.setLive(inBackgroundGroup, s.refCount);
+ }
+ geometry.addUser(sh);
+ }
+ }
+ }
+
+ }
+
+ // after the geometry has been setLived and bounds computed
+ if (k== 0 && boundsAutoCompute) { // Do only once
+ // user may call setBounds with a bounds other than boundingBox
+ if (! (localBounds instanceof BoundingBox)) {
+ localBounds = new BoundingBox((BoundingBox) null);
+ }
+ getCombineBounds((BoundingBox)localBounds);
+
+ }
+ // Assign GAtom and set the bounds if we are not using switch
+ initializeGAtom(sh);
+
+ GeometryAtom ga = getGeomAtom(sh);
+
+ // Add the geometry atom for this shape to the nodeList
+ s.nodeList.add(ga);
+
+ if (s.transformTargets != null &&
+ s.transformTargets[k] != null) {
+ // Add the geometry atom for this shape to the transformTargets
+
+ s.transformTargets[k].addNode(ga, Targets.GEO_TARGETS);
+ }
+ }
+
+ s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT);
+
+ }
+
+ /**
+ * This clears all references in a mirror shape
+ */
+ // This is call in RenderingEnvironmentStructure.removeNode() because that is the
+ // last point that will reference this ms.
+ // called on the mirror shape ..
+ void clearMirrorShape() {
+ int i;
+
+ source = null;
+ sourceNode = null;
+ parent = null;
+
+ if (otherAppearance != null) {
+ otherAppearance.sgApp.removeAMirrorUser(this);
+ otherAppearance = null;
+ }
+
+ appearance = null;
+
+ branchGroupPath = null;
+ isPickable = true;
+ isCollidable = true;
+ branchGroupPath = null;
+ // No locking needed. Owner, s3dR, has already been destory.
+ // DO NOT clear geometryList, ie. geometryList.clear().
+ // It is referred by the source s3DRetained.
+ geometryList = null;
+
+ // Clear the mirror scoping info
+ // Remove all the fogs
+ for (i = 0; i < numfogs; i++)
+ fogs[i] = null;
+ numfogs = 0;
+
+ // Remove all the modelClips
+ for (i = 0; i < numModelClips; i++)
+ modelClips[i] = null;
+ numModelClips = 0;
+
+ // Remove all the lights
+ for (i = 0; i < numlights; i++)
+ lights[i] = null;
+ numlights = 0;
+
+ // Remove all the al app
+ for (i = 0; i < numAltApps; i++)
+ altApps[i] = null;
+ numAltApps = 0;
+
+ viewList = null;
+
+ }
+
+ /**
+ * assign a name to this node when it is made live.
+ */
+ void clearLive(SetLiveState s) {
+
+ //System.out.println("S3DRetained : clearLive " + s);
+
+ int i, j, gaCnt;
+ Shape3DRetained shape;
+ GeometryRetained geometry;
+ Object[] shapes;
+ ArrayList msList = new ArrayList();
+
+ super.clearLive(s);
+
+
+
+ if (inSharedGroup) {
+ synchronized(mirrorShape3D) {
+ shapes = mirrorShape3D.toArray();
+ for (i=0; i<s.keys.length; i++) {
+ for (j=0; j<shapes.length; j++) {
+ shape = (Shape3DRetained)shapes[j];
+ if (shape.key.equals(s.keys[i])) {
+ mirrorShape3D.remove(mirrorShape3D.indexOf(shape));
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(
+ shape, Targets.GEO_TARGETS);
+ }
+ msList.add(shape);
+ GeometryAtom ga = getGeomAtom(shape);
+
+ // Add the geometry atom for this shape to the nodeList
+ s.nodeList.add(ga);
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ga, Targets.GEO_TARGETS);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // Only entry 0 is valid
+ shape = (Shape3DRetained)mirrorShape3D.get(0);
+ synchronized(mirrorShape3D) {
+ mirrorShape3D.remove(0);
+ }
+
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS);
+ }
+
+
+ msList.add(shape);
+
+ GeometryAtom ga = getGeomAtom(shape);
+
+ // Add the geometry atom for this shape to the nodeList
+ s.nodeList.add(ga);
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ga, Targets.GEO_TARGETS);
+ }
+ }
+
+
+ for (int k = 0; k < msList.size(); k++) {
+ Shape3DRetained sh = (Shape3DRetained)msList.get(k);
+ if (appearance != null) {
+ synchronized(appearance.liveStateLock) {
+ if (k == 0) {
+ appearance.clearLive(s.refCount);
+ }
+ appearance.removeAMirrorUser(sh);
+ }
+ }
+ if (geometryList != null) {
+ for(gaCnt=0; gaCnt<geometryList.size(); gaCnt++) {
+ geometry = (GeometryRetained) geometryList.get(gaCnt);
+ if(geometry != null) {
+ synchronized(geometry.liveStateLock) {
+ if (k == 0) {
+ geometry.clearLive(s.refCount);
+ }
+ geometry.removeUser(sh);
+ }
+ }
+ }
+ }
+ }
+
+ s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_TRANSFORM |
+ // This is used to clear the scope info
+ // of all the mirror shapes
+ J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_RENDER);
+
+ if (!source.isLive()) {
+ // Clear the mirror scoping info
+ // Remove all the fogs
+ for (i = 0; i < numfogs; i++)
+ fogs[i] = null;
+ numfogs = 0;
+
+ // Remove all the modelClips
+ for (i = 0; i < numModelClips; i++)
+ modelClips[i] = null;
+ numModelClips = 0;
+
+ // Remove all the lights
+ for (i = 0; i < numlights; i++)
+ lights[i] = null;
+ numlights = 0;
+
+ // Remove all the al app
+ for (i = 0; i < numAltApps; i++)
+ altApps[i] = null;
+ numAltApps = 0;
+ }
+ }
+
+ boolean isStatic() {
+ if (source.getCapability(Shape3D.ALLOW_APPEARANCE_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_GEOMETRY_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ boolean staticXformCanBeApplied() {
+
+ // static xform can be applied if
+ // . shape is not pickable or collidable
+ // . geometry is not being shared by more than one shape nodes
+ // . geometry will be put in display list
+ // . geometry is not readable
+
+ // no static xform if shape is pickable or collidable because
+ // otherwise the static xform will have to be applied to the
+ // currentLocalToVworld in the intersect test, it will then
+ // be more costly and really beat the purpose of eliminating
+ // the static transform group
+ if (isPickable || isCollidable ||
+ source.getCapability(Shape3D.ALLOW_PICKABLE_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_COLLIDABLE_WRITE)) {
+ return false;
+ }
+
+ if (appearance != null &&
+ (appearance.transparencyAttributes != null && appearance.transparencyAttributes.transparencyMode != TransparencyAttributes.NONE))
+ return false;
+
+ GeometryRetained geo;
+ boolean alphaEditable;
+
+ for (int i=0; i<geometryList.size(); i++) {
+ geo = (GeometryRetained) geometryList.get(i);
+ if (geo != null) {
+ if (geo.refCnt > 1) {
+ return false;
+ }
+ alphaEditable = isAlphaEditable(geo);
+ if (geo instanceof GeometryArrayRetained) {
+ geo.isEditable = !((GeometryArrayRetained)geo).isWriteStatic();
+
+ // TODO: for now if vertex data can be returned, then
+ // don't apply static transform
+ if (geo.source.getCapability(
+ GeometryArray.ALLOW_COORDINATE_READ) ||
+ geo.source.getCapability(
+ GeometryArray.ALLOW_NORMAL_READ))
+ return false;
+
+ }
+
+ if (!geo.canBeInDisplayList(alphaEditable)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+
+ void compile(CompileState compState) {
+ AppearanceRetained newApp;
+
+ super.compile(compState);
+
+ if (isStatic() && staticXformCanBeApplied()) {
+ mergeFlag = SceneGraphObjectRetained.MERGE;
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numShapesWStaticTG++;
+ }
+ } else
+ {
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+ compState.keepTG = true;
+ }
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numShapes++;
+ }
+
+ if (appearance != null) {
+ appearance.compile(compState);
+ // Non-static apperanace can still be compiled, since in compile
+ // state we will be grouping all shapes that have same appearance
+ // so, when the appearance changes, all the shapes will be affected
+ // For non-static appearances, we don't get an equivalent appearance
+ // from the compile state
+ if (appearance.isStatic()) {
+ newApp = compState.getAppearance(appearance);
+ appearance = newApp;
+ }
+ }
+
+ for (int i = 0; i < geometryList.size(); i++) {
+ GeometryRetained geo = (GeometryRetained)geometryList.get(i);
+ if (geo != null)
+ geo.compile(compState);
+ }
+
+ }
+
+ void merge(CompileState compState) {
+
+
+ if (mergeFlag == SceneGraphObjectRetained.DONT_MERGE) {
+
+ // no need to save the staticTransform here
+
+ TransformGroupRetained saveStaticTransform =
+ compState.staticTransform;
+ compState.staticTransform = null;
+ super.merge(compState);
+ compState.staticTransform = saveStaticTransform;
+ } else {
+ super.merge(compState);
+ }
+
+ if (shapeIsMergeable(compState)) {
+ compState.addShape(this);
+ }
+ }
+
+
+ boolean shapeIsMergeable(CompileState compState) {
+ boolean mergeable = true;
+ AppearanceRetained newApp;
+ int i;
+
+ GeometryRetained geometry = null;
+ int index = 0;
+ i = 0;
+ /*
+ if (isPickable)
+ return false;
+ */
+
+ // For now, don't merge if the shape has static transform
+ if (staticTransform != null)
+ return false;
+
+ // If this shape's to be immediate parent is orderedGroup or a switchNode
+ // this shape is not mergerable
+ if (parent instanceof OrderedGroupRetained ||
+ parent instanceof SwitchRetained)
+ return false;
+
+ // Get the first geometry that is non-null
+ while (geometry == null && index < geometryList.size()) {
+ geometry = (GeometryRetained) geometryList.get(index);
+ index++;
+ }
+
+ if (!(geometry instanceof GeometryArrayRetained)) {
+ return false;
+ }
+
+ GeometryArrayRetained firstGeo = (GeometryArrayRetained) geometry;
+
+ for(i=index; (i<geometryList.size() && mergeable); i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if (geometry != null) {
+ GeometryArrayRetained geo = (GeometryArrayRetained)geometry;
+
+ if (! geo.isWriteStatic())
+ mergeable = false;
+
+ if (geo.vertexFormat != firstGeo.vertexFormat)
+ mergeable = false;
+
+
+ }
+ }
+
+ // For now, turn off lots of capability bits
+ if (source.getCapability(Shape3D.ALLOW_COLLISION_BOUNDS_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_APPEARANCE_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_AUTO_COMPUTE_BOUNDS_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_BOUNDS_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_COLLIDABLE_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_PICKABLE_WRITE) ||
+ source.getCapability(Shape3D.ALLOW_GEOMETRY_WRITE)) {
+ mergeable = false;
+ }
+
+ return mergeable;
+
+ }
+
+
+ void getMirrorObjects( ArrayList list, HashKey k) {
+ Shape3DRetained ms;
+ if (inSharedGroup) {
+ if (k.count == 0) {
+ // System.out.println("===> CAN NEVER BE TRUE");
+ return;
+ }
+ else {
+ ms = getMirrorShape(k);
+ }
+ }
+ else {
+ ms = (Shape3DRetained)mirrorShape3D.get(0);
+ }
+
+ list.add(getGeomAtom(ms));
+
+ }
+
+
+ // Called on the mirror Object
+ void addLight(LightRetained light) {
+ LightRetained[] newlights;
+ int i, n;
+ Shape3DRetained ms;
+
+ if (lights == null) {
+ lights = new LightRetained[10];
+ }
+ else if (lights.length == numlights) {
+ newlights = new LightRetained[numlights*2];
+ for (i=0; i<numlights; i++) {
+ newlights[i] = lights[i];
+ }
+ lights = newlights;
+ }
+ lights[numlights] = light;
+ numlights++;
+ }
+
+ // called on the mirror object
+ void removeLight(LightRetained light) {
+ int i;
+
+ for (i=0; i<numlights; i++) {
+ if (lights[i] == light) {
+ lights[i] = null;
+ break;
+ }
+ }
+
+ // Shift everyone down one.
+ for (i++; i<numlights; i++) {
+ lights[i-1] = lights[i];
+ }
+ numlights--;
+ }
+
+ // Called on the mirror object
+ void addFog(FogRetained fog) {
+ FogRetained[] newfogs;
+ int i;
+
+ if (fogs == null) {
+ fogs = new FogRetained[10];
+ }
+ else if (fogs.length == numfogs) {
+ newfogs = new FogRetained[numfogs*2];
+ for (i=0; i<numfogs; i++) {
+ newfogs[i] = fogs[i];
+ }
+ fogs = newfogs;
+ }
+ fogs[numfogs] = fog;
+ numfogs++;
+ }
+
+ // called on the mirror object
+ void removeFog(FogRetained fog) {
+ int i;
+
+ for (i=0; i<numfogs; i++) {
+ if (fogs[i] == fog) {
+ fogs[i] = null;
+ break;
+ }
+ }
+
+ // Shift everyone down one.
+ for (i++; i<numfogs; i++) {
+ fogs[i-1] = fogs[i];
+ }
+ numfogs--;
+
+ }
+
+ // Called on the mirror object
+ void addModelClip(ModelClipRetained modelClip) {
+ ModelClipRetained[] newModelClips;
+ int i;
+
+
+ if (modelClips == null) {
+ modelClips = new ModelClipRetained[10];
+ }
+ else if (modelClips.length == numModelClips) {
+ newModelClips = new ModelClipRetained[numModelClips*2];
+ for (i=0; i<numModelClips; i++) {
+ newModelClips[i] = modelClips[i];
+ }
+ modelClips = newModelClips;
+ }
+ modelClips[numModelClips] = modelClip;
+ numModelClips++;
+ }
+
+ // called on the mirror object
+ void removeModelClip(ModelClipRetained modelClip) {
+ int i;
+
+ for (i=0; i<numModelClips; i++) {
+ if (modelClips[i] == modelClip) {
+ modelClips[i] = null;
+ break;
+ }
+ }
+
+ // Shift everyone down one.
+ for (i++; i<numModelClips; i++) {
+ modelClips[i-1] = modelClips[i];
+ }
+ numModelClips--;
+
+ }
+
+ // Called on the mirror object
+ void addAltApp(AlternateAppearanceRetained aApp) {
+ AlternateAppearanceRetained[] newAltApps;
+ int i;
+ if (altApps == null) {
+ altApps = new AlternateAppearanceRetained[10];
+ }
+ else if (altApps.length == numAltApps) {
+ newAltApps = new AlternateAppearanceRetained[numAltApps*2];
+ for (i=0; i<numAltApps; i++) {
+ newAltApps[i] = altApps[i];
+ }
+ altApps = newAltApps;
+ }
+ altApps[numAltApps] = aApp;
+ numAltApps++;
+ }
+
+ // called on the mirror object
+ void removeAltApp(AlternateAppearanceRetained aApp) {
+ int i;
+
+ for (i=0; i<numAltApps; i++) {
+ if (altApps[i] == aApp) {
+ altApps[i] = null;
+ break;
+ }
+ }
+
+ // Shift everyone down one.
+ for (i++; i<numAltApps; i++) {
+ altApps[i-1] = altApps[i];
+ }
+ numAltApps--;
+
+ }
+
+
+
+ void updatePickable(HashKey keys[], boolean pick[]) {
+ super.updatePickable(keys, pick);
+ Shape3DRetained shape;
+
+ if (!inSharedGroup) {
+ shape = (Shape3DRetained) mirrorShape3D.get(0);
+ shape.isPickable = pick[0];
+ } else {
+ int size = mirrorShape3D.size();
+ for (int j=0; j< keys.length; j++) {
+ for (int i=0; i < size; i++) {
+ shape = (Shape3DRetained) mirrorShape3D.get(i);
+ if (keys[j].equals(shape.key)) {
+ shape.isPickable = pick[j];
+ break;
+ }
+
+ }
+ }
+ }
+ }
+
+
+ void updateCollidable(HashKey keys[], boolean collide[]) {
+ super.updateCollidable(keys, collide);
+ Shape3DRetained shape;
+
+ if (!inSharedGroup) {
+ shape = (Shape3DRetained) mirrorShape3D.get(0);
+ shape.isCollidable = collide[0];
+ } else {
+ int size = mirrorShape3D.size();
+ for (int j=0; j< keys.length; j++) {
+ for (int i=0; i < size; i++) {
+ shape = (Shape3DRetained) mirrorShape3D.get(i);
+ if (keys[j].equals(shape.key)) {
+ shape.isCollidable = collide[j];
+ break;
+ }
+
+ }
+ }
+ }
+ }
+
+
+
+
+ // New 1.2.1 code ....
+
+ // Remove the old geometry atoms and reInsert
+ // the new geometry atoms and update the transform
+ // target list
+
+ private void sendDataChangedMessage( GeometryRetained newGeom ) {
+
+ int i, j, gaCnt;
+ GeometryAtom[] newGAArray = null;
+ GeometryAtom[] oldGAArray = null;
+ GeometryAtom[] newGeometryAtoms = null;
+ int geometryCnt = 0;
+ GeometryRetained geometry = null;
+
+ int s3dMSize = mirrorShape3D.size();
+
+ if(s3dMSize < 1)
+ return;
+
+ Shape3DRetained mS3d = (Shape3DRetained) mirrorShape3D.get(0);
+
+ mS3d.mirrorShape3DLock.writeLock();
+
+ GeometryAtom oldGA = mS3d.geomAtom;
+
+ GeometryAtom newGA = new GeometryAtom();
+
+ if(newGeom != null) {
+ newGeom.addUser(mS3d);
+ }
+
+ int gSize = geometryList.size();
+
+ for(i=0; i<gSize; i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if(geometry != null) {
+ newGA.geoType = geometry.geoType;
+ newGA.alphaEditable = mS3d.isAlphaEditable(geometry);
+ break;
+ }
+ }
+
+ if((geometry != null) &&
+ (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D)) {
+
+ for(i = 0; i<gSize; i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if(geometry != null) {
+ Text3DRetained tempT3d = (Text3DRetained)geometry;
+ geometryCnt += tempT3d.numChars;
+ }
+ else {
+ // This is slightly wasteful, but not quite worth to optimize yet.
+ geometryCnt++;
+ }
+ }
+ newGA.geometryArray = new GeometryRetained[geometryCnt];
+ newGA.lastLocalTransformArray = new Transform3D[geometryCnt];
+ // Reset geometryCnt;
+ geometryCnt = 0;
+
+ }
+ else {
+ newGA.geometryArray = new GeometryRetained[gSize];
+ }
+
+ newGA.locale = mS3d.locale;
+ newGA.visible = visible;
+ newGA.source = mS3d;
+
+
+ for(gaCnt = 0; gaCnt<gSize; gaCnt++) {
+ geometry = (GeometryRetained) geometryList.get(gaCnt);
+ if(geometry == null) {
+ newGA.geometryArray[geometryCnt++] = null;
+ }
+ else {
+ if (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D) {
+ Text3DRetained t = (Text3DRetained)geometry;
+ GeometryRetained geo;
+ for (i=0; i<t.numChars; i++, geometryCnt++) {
+ geo = t.geometryList[i];
+ if (geo!= null) {
+ newGA.geometryArray[geometryCnt] = geo;
+ newGA.lastLocalTransformArray[geometryCnt] =
+ t.charTransforms[i];
+
+ } else {
+ newGA.geometryArray[geometryCnt] = null;
+ newGA.lastLocalTransformArray[geometryCnt] = null;
+ }
+
+ }
+
+ } else {
+ newGA.geometryArray[geometryCnt++] = geometry;
+ }
+ }
+ }
+
+ oldGAArray = new GeometryAtom[s3dMSize];
+ newGAArray = new GeometryAtom[s3dMSize];
+ oldGAArray[0] = oldGA;
+ newGAArray[0] = newGA;
+
+ mS3d.geomAtom = newGA;
+ mS3d.mirrorShape3DLock.writeUnlock();
+
+ // ..... clone the rest of mirrorS3D's GA with the above newGA, but modify
+ // its source.
+
+ for (i = 1; i < s3dMSize; i++) {
+ mS3d = (Shape3DRetained) mirrorShape3D.get(i);
+ mS3d.mirrorShape3DLock.writeLock();
+ oldGA = mS3d.geomAtom;
+ newGA = new GeometryAtom();
+
+ if(newGeom != null) {
+ newGeom.addUser(mS3d);
+ }
+
+ newGA.geoType = newGAArray[0].geoType;
+ newGA.locale = mS3d.locale;
+ newGA.visible = visible;
+ newGA.source = mS3d;
+ newGA.alphaEditable = newGAArray[0].alphaEditable;
+
+ newGA.geometryArray = new GeometryRetained[newGAArray[0].geometryArray.length];
+ for(j=0; j<newGA.geometryArray.length; j++) {
+ newGA.geometryArray[j] = newGAArray[0].geometryArray[j];
+ }
+
+ oldGAArray[i] = oldGA;
+ newGAArray[i] = newGA;
+
+ mS3d.geomAtom = newGA;
+ mS3d.mirrorShape3DLock.writeUnlock();
+ }
+
+ TargetsInterface ti =
+ ((GroupRetained)parent).getClosestTargetsInterface(
+ TargetsInterface.TRANSFORM_TARGETS);
+ CachedTargets[] newCtArr = null;
+
+ if (ti != null) {
+ CachedTargets ct;
+ newCtArr = new CachedTargets[s3dMSize];
+
+ for (i=0; i<s3dMSize; i++) {
+
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ newCtArr[i] = new CachedTargets();
+ newCtArr[i].copy(ct);
+ newCtArr[i].replace(oldGAArray[i], newGAArray[i],
+ Targets.GEO_TARGETS);
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr, -1);
+ }
+
+
+ J3dMessage changeMessage = VirtualUniverse.mc.getMessage();
+ changeMessage.type = J3dMessage.SHAPE3D_CHANGED;
+ // Who to send this message to ?
+ changeMessage.threads = J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY;
+ changeMessage.universe = universe;
+ changeMessage.args[0] = this;
+ changeMessage.args[1] = new Integer(GEOMETRY_CHANGED);
+ changeMessage.args[2] = oldGAArray;
+ changeMessage.args[3] = newGAArray;
+ if (ti != null) {
+ changeMessage.args[4] = ti;
+ changeMessage.args[5] = newCtArr;
+ }
+ if (boundsAutoCompute) {
+ getCombineBounds((BoundingBox)localBounds);
+ }
+ VirtualUniverse.mc.processMessage(changeMessage);
+
+ }
+
+
+ // ********** End of New 1.2.1 code ....
+
+
+
+
+
+ Shape3DRetained getMirrorShape(SceneGraphPath path) {
+ if (!inSharedGroup) {
+ return (Shape3DRetained) mirrorShape3D.get(0);
+ }
+ HashKey key = new HashKey("");
+ path.getHashKey(key);
+ return getMirrorShape(key);
+ }
+
+ Shape3DRetained getMirrorShape(HashKey key) {
+ if (key == null) {
+ return (Shape3DRetained) mirrorShape3D.get(0);
+ } else {
+ int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+
+ if (i>=0) {
+ return (Shape3DRetained) mirrorShape3D.get(i);
+ }
+ }
+ // Not possible
+ throw new RuntimeException("Shape3DRetained: MirrorShape Not found!");
+ }
+
+ void setBoundsAutoCompute(boolean autoCompute) {
+ GeometryRetained geometry;
+ if (autoCompute != boundsAutoCompute) {
+ if (autoCompute) {
+ // localBounds may not have been set to bbox
+ localBounds = new BoundingBox((BoundingBox) null);
+ if (source.isLive() && geometryList != null) {
+ int size = geometryList.size()*mirrorShape3D.size();
+ for (int i=0; i<size; i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ geometry.incrComputeGeoBounds();
+ }
+ }
+
+ getCombineBounds((BoundingBox)localBounds);
+ }
+ else {
+ if (source.isLive() && geometryList != null) {
+ int size = geometryList.size()*mirrorShape3D.size();
+ for (int i=0; i<size; i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ geometry.decrComputeGeoBounds();
+ }
+
+ }
+ }
+ super.setBoundsAutoCompute(autoCompute);
+ if (source.isLive()) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_RENDER;
+ message.universe = universe;
+ message.args[0] = getGeomAtomsArray(mirrorShape3D);
+ // no need to clone localBounds
+ message.args[1] = localBounds;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+ }
+ // This method is called when coordinates of a geometry in the geometrylist
+ // changed and autoBoundsCompute is true
+
+ void updateBounds() {
+ localBounds = new BoundingBox((BoundingBox) null);
+ getCombineBounds((BoundingBox)localBounds);
+ synchronized(mirrorShape3D) {
+ if (source.isLive()) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED;
+ message.threads = J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_RENDER;
+ message.universe = universe;
+ message.args[0] = getGeomAtomsArray(mirrorShape3D);
+ // no need to clone localBounds
+ message.args[1] = localBounds;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+ }
+
+ boolean allowIntersect() {
+ GeometryRetained ga = null;
+
+ for(int i=0; i<geometryList.size(); i++) {
+ ga = (GeometryRetained) geometryList.get(i);
+ if(ga != null)
+ if (!ga.source.getCapability(Geometry.ALLOW_INTERSECT)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ boolean intersectGeometryList(Shape3DRetained otherShape) {
+ GeometryRetained geom1, geom2;
+ ArrayList gaList = otherShape.geometryList;
+ int gaSize = gaList.size();
+ Transform3D otherLocalToVworld = otherShape.getCurrentLocalToVworld();
+ Transform3D thisLocalToVworld = getCurrentLocalToVworld();
+ View views = null;
+ int primaryViewIdx = -1;
+
+
+ if (this instanceof OrientedShape3DRetained) {
+ primaryViewIdx = getPrimaryViewIdx();
+ thisLocalToVworld.mul(((OrientedShape3DRetained)this).
+ getOrientedTransform(primaryViewIdx));
+ }
+
+ if (otherShape instanceof OrientedShape3DRetained) {
+ if (primaryViewIdx < 0) {
+ primaryViewIdx = getPrimaryViewIdx();
+ }
+ otherLocalToVworld.mul(((OrientedShape3DRetained)otherShape).
+ getOrientedTransform(primaryViewIdx));
+ }
+
+ for (int i=geometryList.size()-1; i >=0; i--) {
+ geom1 = (GeometryRetained) geometryList.get(i);
+ if (geom1 != null) {
+ for (int j=gaSize-1; j >=0; j--) {
+ geom2 = (GeometryRetained) gaList.get(j);
+ if ((geom2 != null) &&
+ geom1.intersect(thisLocalToVworld,
+ otherLocalToVworld, geom2)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ boolean intersectGeometryList(Transform3D thisLocalToVworld, Bounds targetBound) {
+
+ GeometryRetained geometry;
+
+ if (this instanceof OrientedShape3DRetained) {
+ Transform3D orientedTransform =
+ ((OrientedShape3DRetained)this).
+ getOrientedTransform(getPrimaryViewIdx());
+ thisLocalToVworld.mul(orientedTransform);
+ }
+
+ for (int i=geometryList.size() - 1; i >=0; i--) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if ((geometry != null) &&
+ geometry.intersect(thisLocalToVworld, targetBound)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+
+ /**
+ * This initialize the mirror shape to reflect the state of the
+ * real Morph.
+ */
+ void initMirrorShape3D(SetLiveState s, MorphRetained morph, int index) {
+
+ GeometryRetained geometry;
+
+ GeometryAtom[] newGeometryAtoms = null;
+
+ universe = morph.universe;
+ inSharedGroup = morph.inSharedGroup;
+ inBackgroundGroup = morph.inBackgroundGroup;
+ geometryBackground = morph.geometryBackground;
+ parent = morph.parent;
+ locale = morph.locale;
+
+ OrderedPath op = (OrderedPath)s.orderedPaths.get(index);
+ if (op.pathElements.size() == 0) {
+ orderedPath = null;
+ } else {
+ orderedPath = op;
+ }
+
+ staticTransform = morph.staticTransform;
+ if (morph.boundsAutoCompute) {
+ localBounds.set(morph.localBounds);
+ }
+ bounds = localBounds;
+ vwcBounds = new BoundingBox((BoundingBox) null);
+ vwcBounds.transform(bounds, getCurrentLocalToVworld(0));
+
+ if (morph.collisionBound == null) {
+ collisionBound = null;
+ collisionVwcBound = vwcBounds;
+ } else {
+ collisionBound = morph.collisionBound;
+ collisionVwcBound = (Bounds)collisionBound.clone();
+ collisionVwcBound.transform(getCurrentLocalToVworld(0));
+ }
+
+ appearanceOverrideEnable = morph.appearanceOverrideEnable;
+
+ // mga is the final geometry we're interested.
+ geometryList = new ArrayList(1);
+ geometryList.add((GeometryArrayRetained)morph.morphedGeometryArray.retained);
+
+ GeometryAtom gAtom = new GeometryAtom();
+ gAtom.geometryArray = new GeometryRetained[1];
+
+ gAtom.locale = locale;
+ gAtom.visible = morph.visible;
+ gAtom.source = this;
+
+ geometry = (GeometryRetained) geometryList.get(0);
+
+ if(geometry ==null) {
+ gAtom.geometryArray[0] = null;
+ } else {
+ gAtom.geometryArray[0] = (GeometryArrayRetained)morph.
+ morphedGeometryArray.retained;
+ gAtom.geoType = gAtom.geometryArray[0].geoType;
+ }
+ geomAtom = gAtom;
+
+ // Assign the parent of this mirror shape node
+ sourceNode = morph;
+ }
+
+ // geometries in morph object is modified, update the geometry
+ // list in the mirror shapes and the geometry array in the geometry atom
+
+ void setMorphGeometry(Geometry geometry, ArrayList mirrorShapes) {
+ GeometryAtom oldGA, newGA;
+ Shape3DRetained ms;
+ TransformGroupRetained tg;
+ int nMirrorShapes = mirrorShapes.size();
+ int i;
+
+ GeometryAtom oldGAArray[] = new GeometryAtom[nMirrorShapes];
+ GeometryAtom newGAArray[] = new GeometryAtom[nMirrorShapes];
+
+
+ for (i = 0; i < nMirrorShapes; i++) {
+ ms = (Shape3DRetained) mirrorShapes.get(i);
+
+ oldGA = Shape3DRetained.getGeomAtom(ms);
+
+ ms.geometryList = new ArrayList(1);
+ ms.geometryList.add((GeometryArrayRetained)geometry.retained);
+
+ newGA = new GeometryAtom();
+ newGA.geometryArray = new GeometryRetained[1];
+
+ if (geometry ==null) {
+ newGA.geometryArray[0] = null;
+ } else {
+ newGA.geometryArray[0] =
+ (GeometryArrayRetained)geometry.retained;
+ newGA.geoType = newGA.geometryArray[0].geoType;
+ }
+
+ newGA.locale = locale;
+ newGA.visible = oldGA.visible;
+ newGA.source = this;
+
+ oldGAArray[i] = oldGA;
+ newGAArray[i] = newGA;
+
+ Shape3DRetained.setGeomAtom(ms, newGA);
+ }
+
+ TargetsInterface ti =
+ ((GroupRetained)parent).getClosestTargetsInterface(
+ TargetsInterface.TRANSFORM_TARGETS);
+ CachedTargets[] newCtArr = null;
+
+ if (ti != null) {
+ CachedTargets ct;
+ newCtArr = new CachedTargets[nMirrorShapes];
+
+ for (i=0; i<nMirrorShapes; i++) {
+
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ newCtArr[i] = new CachedTargets();
+ newCtArr[i].copy(ct);
+ newCtArr[i].replace(oldGAArray[i], newGAArray[i],
+ Targets.GEO_TARGETS);
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+
+ // send a Shape GEOMETRY_CHANGED message for all geometry atoms
+
+ J3dMessage changeMessage = VirtualUniverse.mc.getMessage();
+ changeMessage.type = J3dMessage.SHAPE3D_CHANGED;
+ changeMessage.threads = J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY;
+ changeMessage.universe = universe;
+ changeMessage.args[0] = this;
+ changeMessage.args[1] = new Integer(GEOMETRY_CHANGED);
+ changeMessage.args[2] = oldGAArray;
+ changeMessage.args[3] = newGAArray;
+ if (ti != null) {
+ changeMessage.args[4] = ti;
+ changeMessage.args[5] = newCtArr;
+ }
+ VirtualUniverse.mc.processMessage(changeMessage);
+ }
+
+
+ /**
+ * Return an array of geometry atoms belongs to userList.
+ * The input is an arraylist of Shape3DRetained type.
+ * This is used to send a message of the snapshot of the
+ * geometry atoms that are affected by this change.
+ */
+ final static GeometryAtom[] getGeomAtomsArray(ArrayList userList) {
+ Shape3DRetained ms = null;
+ GeometryAtom[] gaArr = null;
+ int size, nullCnt=0, i, j;
+
+ synchronized(userList) {
+ size = userList.size();
+ gaArr = new GeometryAtom[size];
+ for (i = 0; i < size; i++) {
+ ms = (Shape3DRetained) userList.get(i);
+ ms.mirrorShape3DLock.readLock();
+ if(ms.geomAtom == null) {
+ nullCnt++;
+ }
+ gaArr[i] = ms.geomAtom;
+ ms.mirrorShape3DLock.readUnlock();
+ }
+ }
+ if(nullCnt == 0) {
+ return gaArr;
+ }
+ else if(nullCnt == size) {
+ return null;
+ }
+ else {
+ GeometryAtom[] newGaArr = new GeometryAtom[size - nullCnt];
+
+ for (i=0, j=0; i < size; i++) {
+ if(gaArr[i] != null) {
+ newGaArr[j++] = gaArr[i];
+ }
+ }
+ return newGaArr;
+ }
+ }
+
+ /**
+ * Return a list of geometry atoms belongs to userList and places a list of
+ * universe found in userList in univList.
+ * The input is an array of Shape3DRetained type.
+ * univList is assume to be empty.
+ * This is used to send a message of the snapshot of the
+ * geometry atoms that are affected by this change.
+ */
+ final static ArrayList getGeomAtomsList(ArrayList userList, ArrayList univList) {
+ ArrayList listPerUniverse = new ArrayList();
+ int index;
+ ArrayList gaList = null;
+ Shape3DRetained ms = null;
+ boolean moreThanOneUniv = false;
+ VirtualUniverse firstFndUniv = null;
+
+ synchronized(userList) {
+ for (int i = userList.size()-1; i >=0; i--) {
+ ms = (Shape3DRetained) userList.get(i);
+
+ if(moreThanOneUniv == false) {
+ if(firstFndUniv == null) {
+ firstFndUniv = ms.universe;
+ univList.add(ms.universe);
+
+ gaList = new ArrayList();
+ listPerUniverse.add(gaList);
+ }
+ else if(firstFndUniv != ms.universe) {
+ moreThanOneUniv = true;
+ univList.add(ms.universe);
+ gaList = new ArrayList();
+ listPerUniverse.add(gaList);
+ }
+ }
+ else {
+ index = univList.indexOf(ms.universe);
+ if (index < 0) {
+ univList.add(ms.universe);
+ gaList = new ArrayList();
+ listPerUniverse.add(gaList);
+ }
+ else {
+ gaList = (ArrayList) listPerUniverse.get(index);
+ }
+ }
+
+
+ ms.mirrorShape3DLock.readLock();
+
+ if(ms.geomAtom != null) {
+ gaList.add(ms.geomAtom);
+ }
+ ms.mirrorShape3DLock.readUnlock();
+
+ }
+ }
+ return listPerUniverse;
+ }
+
+ final static GeometryAtom getGeomAtom(Shape3DRetained shape) {
+ GeometryAtom ga;
+
+ shape.mirrorShape3DLock.readLock();
+ ga = shape.geomAtom;
+ shape.mirrorShape3DLock.readUnlock();
+
+ return ga;
+ }
+
+ final static void setGeomAtom(Shape3DRetained shape, GeometryAtom ga) {
+ shape.mirrorShape3DLock.writeLock();
+ shape.geomAtom = ga;
+ shape.mirrorShape3DLock.writeUnlock();
+ }
+
+
+ // Alpha is editable due to the appearance
+ boolean isAlphaEditable(GeometryRetained geo) {
+
+ boolean alphaEditable = false;
+
+ if (appearanceOverrideEnable) {
+ alphaEditable = true;
+ } else if (geo != null &&
+ appearance != null) {
+
+ AppearanceRetained app = appearance;
+
+ if (source.getCapability(
+ Shape3D.ALLOW_APPEARANCE_WRITE) ||
+ source.getCapability(
+ Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) ||
+
+ app.source.getCapability(
+ Appearance.ALLOW_RENDERING_ATTRIBUTES_WRITE) ||
+
+ app.source.getCapability(
+ Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE) ||
+
+ (app.renderingAttributes != null &&
+ (app.renderingAttributes.source.getCapability(
+ RenderingAttributes.ALLOW_ALPHA_TEST_FUNCTION_WRITE) ||
+ app.renderingAttributes.source.getCapability(
+ RenderingAttributes.ALLOW_IGNORE_VERTEX_COLORS_WRITE))) ||
+
+ (app.transparencyAttributes != null &&
+ (app.transparencyAttributes.source.getCapability(
+ TransparencyAttributes.ALLOW_MODE_WRITE) ||
+ app.transparencyAttributes.source.getCapability(
+ TransparencyAttributes.ALLOW_VALUE_WRITE)))) {
+
+ alphaEditable = true;
+
+ } else if (geo instanceof GeometryArrayRetained &&
+ (app.source.getCapability(
+ Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE) ||
+
+ (app.textureAttributes != null &&
+ app.textureAttributes.source.getCapability(
+ TextureAttributes.ALLOW_MODE_WRITE)))) {
+
+ alphaEditable = true;
+
+ } else if (geo instanceof RasterRetained) {
+ if ((((RasterRetained)geo).type & Raster.RASTER_COLOR) !=
+0
+ && ((RasterRetained)geo).source.getCapability(
+ Raster.ALLOW_IMAGE_WRITE)) {
+
+ alphaEditable = true;
+ }
+ }
+ }
+ return alphaEditable;
+ }
+
+ // getCombineBounds is faster than computeCombineBounds since it
+ // does not recompute the geometry.geoBounds
+ void getCombineBounds(BoundingBox bounds) {
+
+ if(geometryList != null) {
+ BoundingBox bbox = null;
+ GeometryRetained geometry;
+
+ if (staticTransform != null) {
+ bbox = new BoundingBox((BoundingBox) null);
+ }
+
+ synchronized(bounds) {
+ bounds.setLower( 1.0, 1.0, 1.0);
+ bounds.setUpper(-1.0,-1.0,-1.0);
+ for(int i=0; i<geometryList.size(); i++) {
+ geometry = (GeometryRetained) geometryList.get(i);
+ if ((geometry != null) &&
+ (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) {
+ synchronized(geometry.geoBounds) {
+ if (staticTransform != null) {
+ bbox.set(geometry.geoBounds);
+ bbox.transform(staticTransform.transform);
+ bounds.combine((Bounds)bbox);
+ } else {
+ bounds.combine((Bounds)geometry.geoBounds);
+ }
+ }
+ }
+ }
+ }
+
+ // System.out.println("Shape3DRetained - getCombineBounds");
+ // Enlarge boundingBox to the "minmium bounds" that encompasses all possible
+ // orientation.
+ if (this instanceof OrientedShape3DRetained) {
+ double maxVal = Math.abs(bounds.lower.x);
+ double tempVal = Math.abs(bounds.upper.x);
+ if(tempVal > maxVal)
+ maxVal = tempVal;
+ tempVal = Math.abs(bounds.lower.y);
+ if(tempVal > maxVal)
+ maxVal = tempVal;
+ tempVal = Math.abs(bounds.upper.y);
+ if(tempVal > maxVal)
+ maxVal = tempVal;
+ tempVal = Math.abs(bounds.lower.z);
+ if(tempVal > maxVal)
+ maxVal = tempVal;
+ tempVal = Math.abs(bounds.upper.z);
+ if(tempVal > maxVal)
+ maxVal = tempVal;
+
+ // System.out.println("Shape3DRetained - bounds (Before) " + bounds);
+ bounds.setLower(-maxVal, -maxVal, -maxVal);
+ bounds.setUpper(maxVal, maxVal, maxVal);
+ // System.out.println("Shape3DRetained - bounds (After) " + bounds);
+ }
+
+ }
+ }
+
+
+ boolean isEquivalent(Shape3DRetained shape) {
+ if (this.appearance != shape.appearance ||
+ // Scoping info should be same since they are under same group
+ this.appearanceOverrideEnable != shape.appearanceOverrideEnable ||
+ this.isPickable != shape.isPickable ||
+ this.isCollidable != shape.isCollidable) {
+
+ return false;
+ }
+ if (this.boundsAutoCompute) {
+ if (!shape.boundsAutoCompute)
+ return false;
+ }
+ else {
+ // If bounds autoCompute is false
+ // Then check if both bounds are equal
+ if (this.localBounds != null) {
+ if (shape.localBounds != null) {
+ return this.localBounds.equals(shape.localBounds);
+ }
+ }
+ else if (shape.localBounds != null) {
+ return false;
+ }
+ }
+ if (collisionBound != null) {
+ if (shape.collisionBound == null)
+ return false;
+ else
+ return collisionBound.equals(shape.collisionBound);
+ }
+ else if (shape.collisionBound != null)
+ return false;
+
+ return true;
+ }
+
+ // Bounds can only be set after the geometry is setLived, so has to be done
+ // here, if we are not using switchVwcBounds
+ void initializeGAtom(Shape3DRetained ms) {
+ int i, gaCnt;
+ int geometryCnt = 0;
+ int gSize = geometryList.size();
+ GeometryRetained geometry = null;
+
+ ms.bounds = localBounds;
+ ms.vwcBounds = new BoundingBox((BoundingBox) null);
+ ms.vwcBounds.transform(ms.bounds, ms.getCurrentLocalToVworld(0));
+
+ if (collisionBound == null) {
+ ms.collisionBound = null;
+ ms.collisionVwcBound = ms.vwcBounds;
+ } else {
+ ms.collisionBound = collisionBound;
+ ms.collisionVwcBound = (Bounds)ms.collisionBound.clone();
+ ms.collisionVwcBound.transform(ms.getCurrentLocalToVworld(0));
+ }
+ GeometryAtom gAtom = new GeometryAtom();
+ for(gaCnt=0; gaCnt<gSize; gaCnt++) {
+ geometry = (GeometryRetained) geometryList.get(gaCnt);
+ if(geometry != null) {
+ gAtom.geoType = geometry.geoType;
+ gAtom.alphaEditable = ms.isAlphaEditable(geometry);
+ break;
+ }
+ }
+ if((geometry != null) &&
+ (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D)) {
+
+ for(gaCnt = 0; gaCnt<gSize; gaCnt++) {
+ geometry = (GeometryRetained) geometryList.get(gaCnt);
+ if(geometry != null) {
+ Text3DRetained tempT3d = (Text3DRetained)geometry;
+ geometryCnt += tempT3d.numChars;
+ }
+ else {
+ // This is slightly wasteful, but not quite worth to optimize yet.
+ geometryCnt++;
+ }
+ }
+ gAtom.geometryArray = new GeometryRetained[geometryCnt];
+ gAtom.lastLocalTransformArray = new Transform3D[geometryCnt];
+ // Reset geometryCnt;
+ geometryCnt = 0;
+
+ }
+ else {
+ gAtom.geometryArray = new GeometryRetained[gSize];
+ }
+
+
+ for(gaCnt = 0; gaCnt<geometryList.size(); gaCnt++) {
+ geometry = (GeometryRetained) geometryList.get(gaCnt);
+ if(geometry == null) {
+ gAtom.geometryArray[gaCnt] = null;
+ }
+ else {
+ if (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D) {
+ Text3DRetained t = (Text3DRetained)geometry;
+ GeometryRetained geo;
+ for (i=0; i<t.numChars; i++, geometryCnt++) {
+ geo = t.geometryList[i];
+ if (geo != null) {
+ gAtom.geometryArray[geometryCnt] = geo;
+ gAtom.lastLocalTransformArray[geometryCnt] =
+ t.charTransforms[i];
+ } else {
+ gAtom.geometryArray[geometryCnt] = null;
+ gAtom.lastLocalTransformArray[geometryCnt] = null;
+ }
+
+ }
+
+ } else {
+ gAtom.geometryArray[gaCnt] = geometry;
+ }
+ }
+ }
+ gAtom.locale = ms.locale;
+ gAtom.visible = visible;
+ gAtom.source = ms;
+ ms.geomAtom = gAtom;
+ }
+
+ // Check if geomRetained's class is equivalence with the geometry class.
+ void checkEquivalenceClass(Geometry geometry, int index) {
+
+ if (geometry != null) {
+ for (int i=geometryList.size()-1; i >= 0; i--) {
+ GeometryRetained geomRetained = (GeometryRetained) geometryList.get(i);
+ if ((geomRetained != null) &&
+ (index != i)) { // this geometry will replace
+ // current one so there is no need to check
+ if (!geomRetained.isEquivalenceClass((GeometryRetained)geometry.retained)) {
+ throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained5"));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ int indexOfGeometry(Geometry geometry) {
+ if(geometry != null)
+ return geometryList.indexOf(geometry.retained);
+ else
+ return geometryList.indexOf(null);
+ }
+
+
+ // Removes the specified geometry from this Shape3DRetained's list of geometries
+ void removeGeometry(Geometry geometry) {
+ int ind = indexOfGeometry(geometry);
+ if(ind >= 0)
+ removeGeometry(ind);
+ }
+
+ // Removes all the geometries from this node
+ void removeAllGeometries() {
+ int n = geometryList.size();
+
+ int i;
+ Shape3DRetained mShape;
+ GeometryRetained oldGeom = null;
+
+ if (((Shape3D)this.source).isLive()) {
+ for(int index = n-1; index >= 0; index--) {
+ oldGeom = (GeometryRetained) (geometryList.get(index));
+ if (oldGeom != null) {
+ oldGeom.clearLive(refCount);
+ oldGeom.decRefCnt();
+ for (i=0; i<mirrorShape3D.size(); i++) {
+ mShape = (Shape3DRetained)mirrorShape3D.get(i);
+ oldGeom.removeUser(mShape);
+ }
+ }
+ geometryList.remove(index);
+ }
+ sendDataChangedMessage(null);
+ } else {
+ for(int index = n-1; index >= 0; index--) {
+ oldGeom = (GeometryRetained) (geometryList.get(index));
+ if (oldGeom != null) {
+ oldGeom.decRefCnt();
+ }
+ geometryList.remove(index);
+ }
+ }
+ }
+
+ boolean willRemainOpaque(int geoType) {
+ if (appearance == null ||
+ (appearance.isStatic() &&
+ appearance.isOpaque(geoType))) {
+ return true;
+ }
+ else {
+ return false;
+ }
+
+ }
+
+ static Point3d getPoint3d() {
+ return (Point3d)FreeListManager.getObject(FreeListManager.POINT3D);
+ }
+
+ static void freePoint3d(Point3d p) {
+ FreeListManager.freeObject(FreeListManager.POINT3D, p);
+ }
+
+ void handleFrequencyChange(int bit) {
+ int mask = 0;
+ if (bit == Shape3D.ALLOW_GEOMETRY_WRITE) {
+ mask = GEOMETRY_CHANGED;
+ }
+ else if (bit == Shape3D.ALLOW_APPEARANCE_WRITE) {
+ mask = APPEARANCE_CHANGED;
+ }
+ else if (bit == Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) {
+ mask = APPEARANCEOVERRIDE_CHANGED;
+ }
+ if (mask != 0) {
+ if (source.getCapabilityIsFrequent(bit))
+ changedFrequent |= mask;
+ else if (!source.isLive()) {
+ changedFrequent &= ~mask;
+ }
+ }
+ }
+
+
+ // Alpha is editable due to the appearance(Called on the MirrorShape3D)
+ boolean isAlphaFrequentlyEditable(GeometryRetained geo) {
+
+ boolean alphaFrequentlyEditable = false;
+ if (appearanceOverrideEnable) {
+ alphaFrequentlyEditable = true;
+ } else if (geo != null &&
+ appearance != null) {
+ AppearanceRetained app = appearance;
+
+ if (((changedFrequent &(APPEARANCE_CHANGED|APPEARANCEOVERRIDE_CHANGED)) != 0)||
+ ((app.changedFrequent &(AppearanceRetained.RENDERING|AppearanceRetained.TRANSPARENCY)) != 0) ||
+ (app.renderingAttributes != null &&
+ (((app.renderingAttributes.changedFrequent & (RenderingAttributesRetained.IGNORE_VCOLOR |RenderingAttributesRetained.ALPHA_TEST_FUNC)) != 0))) ||
+
+ (app.transparencyAttributes != null &&
+ ((app.transparencyAttributes.changedFrequent != 0)))) {
+
+ alphaFrequentlyEditable = true;
+
+ } else if (geo instanceof GeometryArrayRetained &&
+ ((app.changedFrequent & AppearanceRetained.TEXTURE_ATTR) != 0) ||
+ (app.textureAttributes != null &&
+ ((app.textureAttributes.changedFrequent & TextureAttributes.ALLOW_MODE_WRITE) != 0))) {
+ alphaFrequentlyEditable = true;
+
+ } else if (geo instanceof RasterRetained) {
+ if (((((RasterRetained)geo).type & Raster.RASTER_COLOR) !=
+0)
+ && (((RasterRetained)geo).cachedChangedFrequent != 0)) {
+
+ alphaFrequentlyEditable = true;
+ }
+ }
+ }
+ // System.out.println("changedFrequent="+changedFrequent+" sourceNode = "+sourceNode+" isAlphaFrequentlyEditable, = "+alphaFrequentlyEditable);
+ return alphaFrequentlyEditable;
+ }
+
+
+ int getPrimaryViewIdx() {
+ // To avoid MT-safe issues when using View, just clone it.
+ UnorderList viewList = VirtualUniverse.mc.cloneView();
+ View views[] = (View []) viewList.toArray(false);
+ int size = viewList.arraySize();
+
+ for (int i=0; i < size; i++) {
+ if (views[i].primaryView) {
+ return views[i].viewIndex;
+ }
+ }
+ return 0;
+ }
+
+ void searchGeometryAtoms(UnorderList list) {
+ list.add(getGeomAtom(getMirrorShape(key)));
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/SharedGroup.java b/src/classes/share/javax/media/j3d/SharedGroup.java
new file mode 100644
index 0000000..832cec7
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SharedGroup.java
@@ -0,0 +1,144 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The SharedGroup provides the ability to manipulate an
+ * instanced scene graph.
+ * A SharedGroup node allows multiple Link leaf nodes to share its
+ * subgraph according to the following semantics:
+ * <P><UL>
+ * <LI>A SharedGroup may be referenced by one or more Link leaf
+ * nodes. Any runtime changes to a node or component object in this
+ * shared subgraph affect all graphs that refer to this subgraph.</LI><P>
+ *
+ * <LI>A SharedGroup may be compiled by calling its compile method
+ * prior to being referenced by any Link leaf nodes.</LI><P>
+ *
+ * <LI>Only Link leaf nodes may refer to SharedGroup nodes. A
+ * SharedGroup node cannot have parents or be attached to a Locale.</LI><P>
+ * </UL>
+ *
+ * A shared subgraph may contain any group node, except an embedded
+ * SharedGroup node (SharedGroup nodes cannot have parents). However,
+ * only the following leaf nodes may appear in a shared subgraph:
+ * <P><UL>
+ * <LI>Light</LI>
+ * <LI>Link</LI>
+ * <LI>Morph</LI>
+ * <LI>Shape</LI>
+ * <LI>Sound</LI></UL><P>
+ *
+ * An IllegalSharingException is thrown if any of the following leaf nodes
+ * appear in a shared subgraph:<P>
+ * <UL>
+ * <LI>AlternateAppearance</LI>
+ * <LI>Background</LI>
+ * <LI>Behavior</LI>
+ * <LI>BoundingLeaf</LI>
+ * <LI>Clip</LI>
+ * <LI>Fog</LI>
+ * <LI>ModelClip</LI>
+ * <LI>Soundscape</LI>
+ * <LI>ViewPlatform</LI></UL>
+ * <P>
+ *
+ * @see IllegalSharingException
+ */
+
+public class SharedGroup extends Group {
+
+ /**
+ * Specifies that this SharedGroup node allows reading the
+ * list of links that refer to this node.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_LINK_READ = CapabilityBits.SHARED_GROUP_ALLOW_LINK_READ;
+
+
+ /**
+ * Constructs and initializes a new SharedGroup node object.
+ */
+ public SharedGroup() {
+ }
+
+
+ /**
+ * Returns the list of Link nodes that refer to this SharedGroup node.
+ * @return An array of Link nodes that refer to this SharedGroup node.
+ *
+ * @since Java 3D 1.3
+ */
+ public Link[] getLinks() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_LINK_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("SharedGroup1"));
+ return ((SharedGroupRetained)retained).getLinks();
+ }
+
+
+ /**
+ * Creates the retained mode SharedGroupRetained object that this
+ * SharedGroup component object will point to.
+ */
+ void createRetained() {
+ this.retained = new SharedGroupRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Compiles the source SharedGroup associated with this object and
+ * creates and caches a compiled scene graph.
+ * @exception SceneGraphCycleException if there is a cycle in the
+ * scene graph
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of a live scene graph.
+ */
+ public void compile() {
+ if (isLive()) {
+ throw new RestrictedAccessException(J3dI18N.getString("SharedGroup0"));
+ }
+
+ if (isCompiled() == false) {
+ // will throw SceneGraphCycleException if there is a cycle
+ // in the scene graph
+ checkForCycle();
+
+ ((SharedGroupRetained)this.retained).compile();
+ }
+ }
+
+
+ /**
+ * 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) {
+ SharedGroup sg = new SharedGroup();
+ sg.duplicateNode(this, forceDuplicate);
+ return sg;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SharedGroupRetained.java b/src/classes/share/javax/media/j3d/SharedGroupRetained.java
new file mode 100644
index 0000000..3651bc0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SharedGroupRetained.java
@@ -0,0 +1,878 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * The SharedGroup node provides the ability to share a scene graph from
+ * multiple other scene graphs through the use of a Link node.
+ */
+
+class SharedGroupRetained extends GroupRetained implements TargetsInterface {
+
+ /*
+ static final int ILLEGAL_LEAF_MASK =
+ 1 << NodeRetained.BACKGROUND |
+ 1 << NodeRetained.BEHAVIOR |
+ 1 << NodeRetained.CLIP |
+ 1 << NodeRetained.LINEARFOG |
+ 1 << NodeRetained.EXPONENTIALFOG |
+ 1 << NodeRetained.SOUNDSCAPE |
+ 1 << NodeRetained.VIEWPLATFORM |
+ 1 << NodeRetained.BOUNDINGLEAF;
+ */
+
+ // The current list of child transform group nodes or link nodes
+ // under a transform group
+ ArrayList childTransformLinks = new ArrayList(1);
+
+ // key which identifies a unique path from a
+ // locale to this transform group
+ HashKey currentKey = new HashKey();
+
+ // key which identifies a unique path from a locale to this switch link
+ HashKey switchKey = new HashKey();
+
+ /**
+ * The Shared Group Node's parent vector.
+ */
+ Vector parents = new Vector(1);
+
+ // J3d copy.
+ CachedTargets[] j3dCTs = null;
+
+ // User copy.
+ CachedTargets[] cachedTargets = null;
+
+ // A bitmask of the types in targets for transform targets
+ int localTargetThreads = 0;
+ // combined localTargetThreads and decendants' localTargetThreads
+ int targetThreads = 0;
+
+ ArrayList switchStates = null;
+
+ SharedGroupRetained() {
+ this.nodeType = NodeRetained.SHAREDGROUP;
+ }
+
+ // SharedGroup specific data at SetLive.
+ void setAuxData(SetLiveState s, int index, int hkIndex) {
+ int i, size;
+
+ // Group's setAuxData()
+ super.setAuxData(s, index, hkIndex);
+
+ branchGroupPaths.add(hkIndex, s.branchGroupPaths.get(index));
+
+ if (orderedPaths == null) {
+ orderedPaths = new ArrayList(1);
+ }
+ orderedPaths.add(hkIndex, s.orderedPaths.get(index));
+
+ if (switchStates == null) {
+ switchStates = new ArrayList(1);
+ }
+ switchStates.add(hkIndex, s.switchStates.get(index));
+
+ if (viewLists == null) {
+ viewLists = new ArrayList(1);
+ }
+ // If there are some ViewSpecificGroups in the path above this SharedGroup
+ // System.out.println("====> hkIndex = "+hkIndex+" s.viewLists = "+s.viewLists);
+ if (s.viewLists != null) {
+ viewLists.add(hkIndex, s.viewLists.get(index));
+ }
+ else {
+ viewLists.add(hkIndex, null);
+ }
+
+ if (lights == null) {
+ lights = new ArrayList(1);
+ }
+ if (s.lights != null) {
+ lights.add(hkIndex, s.lights.get(index));
+ }
+ else {
+ lights.add(hkIndex, null);
+ }
+
+ if (fogs == null) {
+ fogs = new ArrayList(1);
+ }
+ if (s.fogs != null) {
+ fogs.add(hkIndex, s.fogs.get(index));
+ }
+ else {
+ fogs.add(hkIndex, null);
+ }
+
+
+ if (modelClips == null) {
+ modelClips = new ArrayList(1);
+ }
+ if (s.modelClips != null) {
+ modelClips.add(hkIndex, s.modelClips.get(index));
+ }
+ else {
+ modelClips.add(hkIndex, null);
+ }
+
+
+ if (altAppearances == null) {
+ altAppearances = new ArrayList(1);
+ }
+ if (s.altAppearances != null) {
+ altAppearances.add(hkIndex, s.altAppearances.get(index));
+ }
+ else {
+ altAppearances.add(hkIndex, null);
+ }
+ }
+
+
+ void setNodeData(SetLiveState s) {
+
+ // For inSharedGroup case.
+ int i, j, len;
+
+ if (localToVworld == null) {
+ localToVworld = new Transform3D[s.keys.length][];
+ localToVworldIndex = new int[s.keys.length][];
+ localToVworldKeys = new HashKey[s.keys.length];
+ cachedTargets = new CachedTargets[s.keys.length];
+ len=0;
+ }
+ else {
+
+ int newLen = localToVworld.length + s.keys.length;
+
+ Transform3D newTList[][] = new Transform3D[newLen][];
+ HashKey newHList[] = new HashKey[newLen];
+ int newIndexList[][] = new int[newLen][];
+ CachedTargets newTargets[] = new CachedTargets[newLen];
+
+ len = localToVworld.length;
+
+ // Copy the existing data into the newly created data objects.
+ System.arraycopy(localToVworld, 0, newTList, 0, localToVworld.length);
+ System.arraycopy(localToVworldIndex, 0, newIndexList, 0,
+ localToVworldIndex.length);
+ System.arraycopy(localToVworldKeys, 0, newHList, 0,
+ localToVworldKeys.length);
+ System.arraycopy(cachedTargets, 0, newTargets, 0,
+ cachedTargets.length);
+
+ localToVworld = newTList;
+ localToVworldIndex = newIndexList;
+ localToVworldKeys = newHList;
+ cachedTargets = newTargets;
+ }
+
+ int[] hkIndex = new int[1];
+ int hkIndexPlus1, blkSize;
+
+ s.hashkeyIndex = new int[s.keys.length];
+
+ // This should appear before super.setNodeData() if it exists
+ s.parentBranchGroupPaths = branchGroupPaths;
+
+ for(i=len, j=0; i<localToVworld.length; i++, j++) {
+
+ if(s.keys[j].equals(localToVworldKeys, hkIndex, 0, i)) {
+ System.out.println("Found matching hashKey in setNodeData.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ s.hashkeyIndex[j] = hkIndex[0];
+
+
+ if(hkIndex[0] == i) { // Append to last.
+ localToVworldKeys[i] = s.keys[j];
+ localToVworld[i] = s.currentTransforms[j];
+ localToVworldIndex[i] = s.currentTransformsIndex[j];
+ }
+ else { // Insert in between array elements.
+ hkIndexPlus1 = hkIndex[0] + 1;
+ blkSize = i - hkIndex[0];
+
+ // Shift the later portion of array elements by one position.
+ // This is the make room for the new data entry.
+ System.arraycopy(localToVworldKeys, hkIndex[0], localToVworldKeys,
+ hkIndexPlus1, blkSize);
+ System.arraycopy(localToVworld, hkIndex[0], localToVworld,
+ hkIndexPlus1, blkSize);
+ System.arraycopy(localToVworldIndex, hkIndex[0], localToVworldIndex,
+ hkIndexPlus1, blkSize);
+ System.arraycopy(cachedTargets, hkIndex[0], cachedTargets,
+ hkIndexPlus1, blkSize);
+
+ localToVworldKeys[hkIndex[0]] = s.keys[j];
+ localToVworld[hkIndex[0]] = s.currentTransforms[j];
+ localToVworldIndex[hkIndex[0]] = s.currentTransformsIndex[j];
+ }
+
+ // System.out.println("SG: j = "+j+" hkIndex[0] = "+hkIndex[0]+" s.keys[j] = "+s.keys[j]);
+ // For now (1.2.1beta2) only. We cleanup setLive, and clearLive in
+ // next release.
+ setAuxData(s, j, hkIndex[0]);
+ }
+
+ // The SetLiveState need the reflect the new state of this SharedGroup.
+ // The SetLiveState will get reset back in SetLive, after all children of this
+ // node have been set live.
+ s.localToVworld = localToVworld;
+ s.localToVworldIndex = localToVworldIndex;
+ s.localToVworldKeys = localToVworldKeys;
+ s.orderedPaths = orderedPaths;
+ s.switchStates = switchStates;
+
+ // Note that s.childSwitchLinks is updated in super.setLive
+ s.childTransformLinks = childTransformLinks;
+ s.parentTransformLink = this;
+ s.parentSwitchLink = this;
+ s.viewLists = viewLists;
+ s.lights = lights;
+ s.fogs = fogs;
+ s.altAppearances = altAppearances;
+ s.modelClips = modelClips;
+ }
+
+ void setLive(SetLiveState s) {
+
+ int i,j;
+ Targets[] newTargets = null;
+
+ // save setLiveState
+ Transform3D savedLocalToVworld[][] = s.localToVworld;
+ int savedLocalToVworldIndex[][] = s.localToVworldIndex;
+ HashKey savedLocalToVworldKeys[] = s.localToVworldKeys;
+ ArrayList savedOrderedPaths = s.orderedPaths;
+ ArrayList savedViewList = s.viewLists;
+ ArrayList savedLights = s.lights;
+ ArrayList savedFogs = s.fogs;
+ ArrayList savedMclips = s.modelClips;
+ ArrayList savedAltApps = s.altAppearances;
+
+ SharedGroupRetained savedLastSharedGroup = s.lastSharedGroup;
+ Targets[] savedSwitchTargets = s.switchTargets;
+ ArrayList savedSwitchStates = s.switchStates;
+ ArrayList savedChildSwitchLinks = s.childSwitchLinks;
+ GroupRetained savedParentSwitchLink = s.parentSwitchLink;
+ ArrayList savedChildTransformLinks = s.childTransformLinks;
+ GroupRetained savedParentTransformLink = s.parentTransformLink;
+ int[] savedHashkeyIndex = s.hashkeyIndex;
+
+ // update setLiveState for this node
+ // Note that s.containsNodesList is updated in super.setLive
+ s.lastSharedGroup = this;
+
+ Targets[] savedTransformTargets = s.transformTargets;
+
+ int numPaths = s.keys.length;
+ newTargets = new Targets[numPaths];
+ for(i=0; i<numPaths; i++) {
+ if (s.transformLevels[i] >= 0) {
+ newTargets[i] = new Targets();
+ } else {
+ newTargets[i] = null;
+ }
+ }
+ s.transformTargets = newTargets;
+
+ super.setLive(s);
+
+ int hkIndex;
+ for(i=0; i<numPaths; i++) {
+ if (s.transformTargets[i] != null) {
+ hkIndex = s.hashkeyIndex[i];
+ cachedTargets[hkIndex] = s.transformTargets[i].snapShotInit();
+ }
+ }
+ // Assign data in cachedTargets to j3dCTs.
+ j3dCTs = new CachedTargets[cachedTargets.length];
+ copyCachedTargets(TargetsInterface.TRANSFORM_TARGETS, j3dCTs);
+
+ computeTargetThreads(TargetsInterface.TRANSFORM_TARGETS, cachedTargets);
+
+
+ // restore setLiveState
+ s.localToVworld = savedLocalToVworld;
+ s.localToVworldIndex = savedLocalToVworldIndex;
+ s.localToVworldKeys = savedLocalToVworldKeys;
+ s.orderedPaths = savedOrderedPaths;
+ s.viewLists = savedViewList;
+
+ s.lights = savedLights;
+ s.fogs = savedFogs;
+ s.modelClips = savedMclips;
+ s.altAppearances = savedAltApps;
+
+ s.lastSharedGroup = savedLastSharedGroup;
+ s.switchTargets = savedSwitchTargets;
+ s.switchStates = savedSwitchStates;
+
+ s.childSwitchLinks = savedChildSwitchLinks;
+ s.parentSwitchLink = savedParentSwitchLink;
+ s.childTransformLinks = savedChildTransformLinks;
+ s.parentTransformLink = savedParentTransformLink;
+
+ s.transformTargets = savedTransformTargets;
+ s.hashkeyIndex = savedHashkeyIndex;
+/*
+// TODO : port this
+ for (int i=0; i < children.size(); i++) {
+ if ((childContains[i][0] & ILLEGAL_LEAF_MASK) != 0) {
+ throw new IllegalSharingException(J3dI18N.getString("SharedGroupRetained0")); }
+ }
+*/
+ }
+
+
+ /**
+ * remove the localToVworld transform for a node.
+ */
+ void removeNodeData(SetLiveState s) {
+
+ int numChildren = children.size();
+ ArrayList switchTargets;
+ int i,j;
+
+ if (refCount <= 0) {
+ localToVworld = null;
+ localToVworldIndex = null;
+ localToVworldKeys = null;
+ // restore to default and avoid calling clear()
+ // that may clear parent reference branchGroupPaths
+ // Note that this function did not invoke super.removeNodeData()
+ branchGroupPaths = new ArrayList(1);
+ orderedPaths = null;
+ switchStates = null;
+ cachedTargets = null;
+ targetThreads = 0;
+ lights.clear();
+ fogs.clear();
+ modelClips.clear();
+ altAppearances.clear();
+ }
+ else {
+ int index, len;
+
+ // Remove the localToVworld key
+ int newLen = localToVworld.length - s.keys.length;
+
+ Transform3D[][] newTList = new Transform3D[newLen][];
+ HashKey[] newHList = new HashKey[newLen];
+ Transform3D newChildTList[][] = null;
+ int[][] newIndexList = new int[newLen][];
+ CachedTargets[] newTargets = new CachedTargets[newLen];
+
+ int[] tempIndex = new int[s.keys.length];
+ int curStart =0, newStart =0;
+ boolean found = false;
+
+ for(i=0;i<s.keys.length;i++) {
+ index = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length);
+
+ tempIndex[i] = index;
+
+ if(index >= 0) {
+ found = true;
+ if(index == curStart) {
+ curStart++;
+ }
+ else {
+ len = index - curStart;
+ System.arraycopy(localToVworld, curStart, newTList, newStart, len);
+ System.arraycopy(localToVworldIndex, curStart, newIndexList,
+ newStart, len);
+ System.arraycopy(localToVworldKeys, curStart, newHList, newStart, len);
+ System.arraycopy(cachedTargets, curStart, newTargets,
+ newStart, len);
+
+ curStart = index+1;
+ newStart = newStart + len;
+ }
+ }
+ else {
+ found = false;
+ System.out.println("Can't Find matching hashKey in SG.removeNodeData.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ }
+
+ if((found == true) && (curStart < localToVworld.length)) {
+ len = localToVworld.length - curStart;
+ System.arraycopy(localToVworld, curStart, newTList, newStart, len);
+ System.arraycopy(localToVworldIndex, curStart, newIndexList,
+ newStart, len);
+ System.arraycopy(localToVworldKeys, curStart, newHList, newStart, len);
+ System.arraycopy(cachedTargets, curStart, newTargets,
+ newStart, len);
+ }
+
+ // Must be in reverse, to preserve right indexing.
+ for (i = tempIndex.length-1; i >= 0 ; i--) {
+ if(tempIndex[i] >= 0) {
+ branchGroupPaths.remove(tempIndex[i]);
+ orderedPaths.remove(tempIndex[i]);
+ switchStates.remove(tempIndex[i]);
+ lights.remove(tempIndex[i]);
+ fogs.remove(tempIndex[i]);
+ modelClips.remove(tempIndex[i]);
+ altAppearances.remove(tempIndex[i]);
+ }
+ }
+
+ localToVworld = newTList;
+ localToVworldIndex = newIndexList;
+ localToVworldKeys = newHList;
+ cachedTargets = newTargets;
+ }
+ s.localToVworld = localToVworld;
+ s.localToVworldIndex = localToVworldIndex;
+ s.localToVworldKeys = localToVworldKeys;
+ s.orderedPaths = orderedPaths;
+ s.switchStates = switchStates;
+ s.viewLists = viewLists;
+ s.lights = lights;
+ s.fogs = fogs;
+ s.modelClips = modelClips;
+ s.altAppearances = altAppearances;
+ }
+
+ void clearLive(SetLiveState s) {
+
+ int i,j,k, index;
+
+ Transform3D savedLocalToVworld[][] = s.localToVworld;
+ int savedLocalToVworldIndex[][] = s.localToVworldIndex;
+ HashKey savedLocalToVworldKeys[] = s.localToVworldKeys;
+ ArrayList savedOrderedPaths = s.orderedPaths;
+ ArrayList savedViewLists = s.viewLists;
+
+ ArrayList savedLights = s.lights;
+ ArrayList savedFogs = s.fogs;
+ ArrayList savedMclips = s.modelClips;
+ ArrayList savedAltApps = s.altAppearances;
+
+ Targets[] savedSwitchTargets = s.switchTargets;
+ Targets[] savedTransformTargets = s.transformTargets;
+ // no need to gather targets from sg in clear live
+ s.transformTargets = null;
+ s.switchTargets = null;
+
+
+ // TODO: This is a hack since removeNodeData is called before
+ // children are clearLives
+ int[] tempIndex = null;
+ // Don't keep the indices if everything will be cleared
+ if (s.keys.length != localToVworld.length) {
+ tempIndex = new int[s.keys.length];
+ for (i = s.keys.length-1; i >= 0; i--) {
+ tempIndex[i] = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length);
+ }
+ }
+
+ super.clearLive(s);
+ // Do this after children clearlive since part of the viewLists may get cleared
+ // during removeNodeData
+ if(refCount <= 0) {
+ viewLists.clear();
+ }
+ else {
+ // Must be in reverse, to preserve right indexing.
+ for (i = tempIndex.length-1; i >= 0 ; i--) {
+ if(tempIndex[i] >= 0) {
+ viewLists.remove(tempIndex[i]);
+ }
+ }
+ }
+
+ // restore setLiveState from it's local variables.
+ // removeNodeData has altered these variables.
+ s.localToVworld = savedLocalToVworld;
+ s.localToVworldIndex = savedLocalToVworldIndex;
+ s.localToVworldKeys = savedLocalToVworldKeys;
+ s.orderedPaths = savedOrderedPaths;
+ s.viewLists = savedViewLists;
+ s.lights = savedLights;
+ s.fogs = savedFogs;
+ s.modelClips = savedMclips;
+ s.altAppearances = savedAltApps;
+ s.transformTargets = savedTransformTargets;
+ s.switchTargets = savedSwitchTargets;
+ }
+
+ void updateChildLocalToVworld(HashKey key, int index,
+ ArrayList dirtyTransformGroups,
+ ArrayList keySet,
+ UpdateTargets targets,
+ ArrayList blUsers) {
+
+ LinkRetained ln;
+ TransformGroupRetained tg;
+ int i,j;
+ Object obj;
+
+ CachedTargets ct = j3dCTs[index];
+ if (ct != null) {
+ targets.addCachedTargets(ct);
+ if (ct.targetArr[Targets.BLN_TARGETS] != null) {
+ gatherBlUsers(blUsers, ct.targetArr[Targets.BLN_TARGETS]);
+ }
+ }
+
+ synchronized(childTransformLinks) {
+ for (i=0; i<childTransformLinks.size(); i++) {
+ obj = childTransformLinks.get(i);
+
+ if (obj instanceof TransformGroupRetained) {
+ tg = (TransformGroupRetained)obj;
+ tg.updateChildLocalToVworld(
+ tg.localToVworldKeys[index], index,
+ dirtyTransformGroups, keySet,
+ targets, blUsers);
+
+
+ } else { // LinkRetained
+ ln = (LinkRetained)obj;
+ currentKey.set(key);
+ currentKey.append(LinkRetained.plus).append(ln.nodeId);
+ if (ln.sharedGroup.localToVworldKeys != null) {
+ j = currentKey.equals(ln.sharedGroup.localToVworldKeys,0,
+ ln.sharedGroup.localToVworldKeys.length);
+ if(j < 0) {
+ System.out.println("SharedGroupRetained : Can't find hashKey");
+ }
+
+ if (j < ln.sharedGroup.localToVworldKeys.length) {
+ ln.sharedGroup.updateChildLocalToVworld(
+ ln.sharedGroup.localToVworldKeys[j], j,
+ dirtyTransformGroups, keySet,
+ targets, blUsers);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void traverseSwitchChild(int child, HashKey key,
+ int index, SwitchRetained switchRoot,
+ boolean init, boolean swChanged,
+ boolean switchOn, int switchLevel,
+ ArrayList updateList) {
+
+ SwitchRetained sw;
+ LinkRetained ln;
+ Object obj;
+ ArrayList childSwitchLinks;
+ int i,j,k;
+
+ childSwitchLinks = (ArrayList)childrenSwitchLinks.get(child);
+ for (i=0; i<childSwitchLinks.size(); i++) {
+ obj = childSwitchLinks.get(i);
+
+ if (obj instanceof SwitchRetained) {
+ sw = (SwitchRetained)obj;
+ for(j=0; j<sw.children.size(); j++) {
+ sw.traverseSwitchChild(j, key, index, switchRoot,
+ init, swChanged, switchOn, switchLevel, updateList);
+ }
+ } else { // LinkRetained
+ ln = (LinkRetained)obj;
+ switchKey.set(key);
+ switchKey.append(LinkRetained.plus).append(ln.nodeId);
+
+ if (ln.sharedGroup.localToVworldKeys != null) {
+
+ j = switchKey.equals(ln.sharedGroup.localToVworldKeys,0,
+ ln.sharedGroup.localToVworldKeys.length);
+ if(j < 0) {
+ System.out.println("SharedGroupRetained : Can't find hashKey");
+ }
+
+ if (j < ln.sharedGroup.localToVworldKeys.length) {
+ for(k=0; k<ln.sharedGroup.children.size(); k++) {
+ ln.sharedGroup.traverseSwitchChild(k,
+ ln.sharedGroup.
+ localToVworldKeys[j],
+ j, switchRoot, init,
+ swChanged, switchOn,
+ switchLevel, updateList);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void traverseSwitchParent() {
+ int i;
+ NodeRetained ln;
+
+ for(i=0; i<parents.size(); i++) {
+ ln = (NodeRetained) parents.elementAt(i);
+ if (ln.parentSwitchLink != null) {
+ if (parentSwitchLink instanceof SwitchRetained) {
+ ((SwitchRetained)parentSwitchLink).traverseSwitchParent();
+ } else if (parentSwitchLink instanceof SharedGroupRetained) {
+ ((SharedGroupRetained)parentSwitchLink).traverseSwitchParent();
+ }
+ }
+ }
+ }
+
+ // Top level compile call, same as BranchGroup.compile()
+ void compile() {
+
+ if (source.isCompiled() || VirtualUniverse.mc.disableCompile)
+ return;
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_3,
+ "SharedGroupRetained.compile()....\n");
+ }
+
+ CompileState compState = new CompileState();
+
+ isRoot = true;
+
+ compile(compState);
+ merge(compState);
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ if (J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_3)) {
+ compState.printStats();
+ }
+ if (J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_5)) {
+ this.traverse(false, 1);
+ System.out.println();
+ }
+ }
+
+ }
+
+ /**
+ * Returns the Link nodes that refer to this SharedGroup node
+ * @return An array of Link nodes
+ */
+ Link[] getLinks() {
+ Link[] links;
+ // make sure this method is MT-safe
+ synchronized(parents) {
+ int n = parents.size();
+ // allocate new array
+ links = new Link[n];
+ for(int i = 0; i < n; i++) {
+ // copy Link nodes from this node's list of parents
+ links[i] = (Link)((LinkRetained)parents.elementAt(i)).source;
+ }
+ }
+ return links;
+ }
+
+ void insertChildrenData(int index) {
+ if (childrenSwitchLinks == null) {
+ childrenSwitchLinks = new ArrayList(1);
+ }
+ childrenSwitchLinks.add(index, new ArrayList(1));
+ }
+
+ void appendChildrenData() {
+ if (childrenSwitchLinks == null) {
+ childrenSwitchLinks = new ArrayList(1);
+ }
+ childrenSwitchLinks.add(new ArrayList(1));
+ }
+
+ void removeChildrenData(int index) {
+ ArrayList oldSwitchLinks = (ArrayList)childrenSwitchLinks.get(index);
+ oldSwitchLinks.clear();
+ childrenSwitchLinks.remove(index);
+ }
+
+
+ // ***************************
+ // TargetsInterface methods
+ // ***************************
+
+ public int getTargetThreads(int type) {
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ return targetThreads;
+ } else {
+ System.out.println("getTargetThreads: wrong arguments");
+ return -1;
+ }
+ }
+
+ TargetsInterface getClosestTargetsInterface(int type) {
+ return this;
+ }
+
+ // re-evalute localTargetThreads using newCachedTargets and
+ // re-evaluate targetThreads
+ public void computeTargetThreads(int type,
+ CachedTargets[] newCachedTargets) {
+
+ localTargetThreads = 0;
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ for(int i=0; i<newCachedTargets.length; i++) {
+ if (newCachedTargets[i] != null) {
+ localTargetThreads |= newCachedTargets[i].computeTargetThreads();
+ }
+ }
+ targetThreads = localTargetThreads;
+
+ int numLinks = childTransformLinks.size();
+ TargetsInterface childLink;
+ NodeRetained node;
+
+ for(int i=0; i<numLinks; i++) {
+ node = (NodeRetained)childTransformLinks.get(i);
+ if (node.nodeType == NodeRetained.LINK) {
+ childLink = (TargetsInterface)
+ ((LinkRetained)node).sharedGroup;
+ } else {
+ childLink = (TargetsInterface) node;
+ }
+ if (childLink != null) {
+ targetThreads |=
+ childLink.getTargetThreads(TargetsInterface.TRANSFORM_TARGETS);
+ }
+ }
+
+ } else {
+ System.out.println("computeTargetsThreads: wrong arguments");
+ }
+ }
+
+ // re-compute localTargetThread, targetThreads and
+ // propagate changes to ancestors
+ public void updateTargetThreads(int type, CachedTargets[] newCachedTargets) {
+ // type is ignored here, only need for SharedGroup
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ computeTargetThreads(type, newCachedTargets);
+ if (parentTransformLink != null) {
+ TargetsInterface pti = (TargetsInterface)parentTransformLink;
+ pti.propagateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ targetThreads);
+ }
+ } else {
+ System.out.println("updateTargetThreads: wrong arguments");
+ }
+ }
+
+ // re-evaluate targetThreads using childTargetThreads and
+ // propagate changes to ancestors
+ public void propagateTargetThreads(int type, int childTargetThreads) {
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ LinkRetained ln;
+ // TODO : For now we'll OR more than exact.
+ //targetThreads = localTargetThreads | childTargetThreads;
+ targetThreads = targetThreads | childTargetThreads;
+ for(int i=0; i<parents.size(); i++) {
+ ln = (LinkRetained) parents.elementAt(i);
+ if (ln.parentTransformLink != null) {
+ TargetsInterface pti =
+ (TargetsInterface)ln.parentTransformLink;
+ pti.propagateTargetThreads(type, targetThreads);
+ }
+ }
+ } else {
+ System.out.println("propagateTargetThreads: wrong arguments");
+ }
+ }
+
+ public void updateCachedTargets(int type, CachedTargets[] newCt) {
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ j3dCTs = newCt;
+ } else {
+ System.out.println("updateCachedTargets: wrong arguments");
+ }
+ }
+
+ public void copyCachedTargets(int type, CachedTargets[] newCt) {
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ int size = cachedTargets.length;
+ for (int i=0; i<size; i++) {
+ newCt[i] = cachedTargets[i];
+ }
+ } else {
+ System.out.println("copyCachedTargets: wrong arguments");
+ }
+ }
+
+ public CachedTargets getCachedTargets(int type, int index, int child) {
+ if (type == TargetsInterface.SWITCH_TARGETS) {
+ // child info is not used, SG does not have per child states
+ if (index < switchStates.size()) {
+ SwitchState switchState = (SwitchState)switchStates.get(index);
+ return switchState.cachedTargets;
+ } else {
+ return null;
+ }
+ } else {
+ // type == TargetsInterface.TRANSFORM_TARGETS
+ return cachedTargets[index];
+ }
+ }
+
+ public void resetCachedTargets(int type,
+ CachedTargets[] newCtArr,int child) {
+ if (type == TargetsInterface.SWITCH_TARGETS) {
+ // child info is not used, SG does not have per child states
+ SwitchState switchState;
+ if (newCtArr.length != switchStates.size()) {
+ System.out.println("resetCachedTargets: unmatched length!" +
+ newCtArr.length + " " + switchStates.size());
+ System.out.println(" resetCachedTargets: " + this);
+ }
+ for (int i=0; i<newCtArr.length; i++) {
+ switchState = (SwitchState)switchStates.get(i);
+ switchState.cachedTargets = newCtArr[i];
+ }
+
+ } else {
+ // type == TargetsInterface.TRANSFORM_TARGETS
+ cachedTargets = newCtArr;
+ }
+ }
+
+ public ArrayList getTargetsData(int type, int index) {
+ // index is ignores for SharedGroup
+ if (type == TargetsInterface.SWITCH_TARGETS) {
+ return switchStates;
+ } else {
+ System.out.println("getTargetsData: wrong arguments");
+ return null;
+ }
+ }
+
+
+ void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) {
+
+ int i;
+ s.childSwitchLinks = (ArrayList)childrenSwitchLinks.get(childIndex);
+ s.switchStates = switchStates;
+
+ if(child!=null)
+ child.setLive(s);
+ }
+
+ void childCheckSetLive(NodeRetained child, int childIndex, SetLiveState s) {
+ s.childTransformLinks = childTransformLinks;
+ s.parentTransformLink = this;
+ child.setLive(s);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Sound.java b/src/classes/share/javax/media/j3d/Sound.java
new file mode 100644
index 0000000..eff2786
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Sound.java
@@ -0,0 +1,1151 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * Sound node is an abstract class that defines the properties common to all
+ * sound sources. A scene graph can contain multiple sounds. Associated with each
+ * sound source are: a reference to sound data, an amplitude scale factor, a release
+ * flag denoting that the sound associated with this node is to play to end when
+ * it is disabled, the number of times sound is to be repeated, the sound's state
+ * (on or off), a scheduling region, and a flag denoting if the sound is to
+ * continue playing "silently" even while it is inactive. Whenever the listener
+ * is within a sound node's scheduling bounds this sound is potentially audible.
+ *<P>
+ * Sound Data
+ *
+ * <UL>Associated with each Sound node is a MediaContainer
+ * which includes audio data and information about this data.
+ * This data can be cached (buffered) or non-cached (unbuffered or streaming).
+ * If an AudioDevice has been attached to the PhysicalEnvironment, the sound
+ * data is made ready to begin playing.
+ * Certain functionality can not be applied to true streaming sound data:<p>
+ * 1) querying the sound's duration (Sound.DURATION_UNKNOWN will be returned),<br>
+ * 2) looping over a range of the streaming data; and<br>
+ * 3) restart a previously played portion of the data.<p>
+ * Depending on the implementation of the AudioDevice used, streamed, non-
+ * cached data may not be fully spatialized.</UL>
+ *<P>
+ * Initial Gain
+ *
+ * <UL>This gain is a scale factor applied to the sound data associated
+ * with this sound source to increase or decrease its overall amplitude.</UL>
+ *<P>
+ * Loop
+ *
+ * <UL>Data for non-streaming sound (such as a sound sample) can contain two
+ * loop points marking a section of the data that is to be looped a specific
+ * number of times. Thus sound data can be divided into three segments:
+ * the attack (before the begin loop point), the sustain (between the begin
+ * and end loop points), and the release (after the end loop point). If
+ * there are no loop begin and end points defined as part of the sound data,
+ * the begin loop point is set at the beginning of the sound data,
+ * and the end loop point at the end of the sound data.
+ * If this is the case, looping the sound would mean repeating the whole
+ * sound. However, these allow a portion in the middle of the sound to
+ * be looped.
+ *<P>
+ * A sound can be looped a specified number of times after it is activated
+ * before it is completed. The loop count value explicitly sets the number
+ * of times the sound is looped. Any non-negative number is a valid value.
+ * A value of zero denotes that the looped section is not repeated, but is
+ * played only once. A value of -1 denotes that the loop is repeated
+ * indefinitely.
+ *<P>
+ * Changing loop count of a sound after the sound has been started will not
+ * dynamically affect the loop count currently used by the sound playing.
+ * The new loop count will be used the next time the sound is enabled.</UL>
+ * <P>
+ * Release Flag
+ *
+ * <UL>When a sound is disabled, its playback would normally stop immediately
+ * no matter what part of the sound data was currently being played. By
+ * setting the Release Flag to true for nodes with non-streaming sound data,
+ * the sound is allowed to play from its current position in the sound data
+ * to the end of the data (without repeats), thus playing the release portion
+ * of the sound before stopping.</UL>
+ *<P>
+ * Continuous Flag
+ *
+ * <UL>For some applications, it's useful to turn a sound source "off" but to
+ * continue "silently" playing the sound so that when it is turned back "on"
+ * the sound picks up playing in the same location (over time) as it would
+ * have been if the sound had never been disabled (turned off). Setting the
+ * Continuous flag true causes the sound renderer to keep track of where
+ * (over time) the sound would be playing even when the sound is disabled.</UL>
+ *<P>
+ * Enable Sound
+ *
+ * <UL>When enabled, the sound source is started
+ * playing and thus can potentially be heard, depending on its activation
+ * state, gain control parameters, continuation state, and spatialization
+ * parameters. If the continuous state is true, even if the sound is not
+ * active, enabling the sound starts the sound silently "playing," so that
+ * when the sound is activated, the sound is (potentially) heard from
+ * somewhere in the middle of the sound data. Activation state can change
+ * from active to inactive any number of times without stopping or starting
+ * the sound. To re-start a sound at the beginning of its data, re-enable
+ * the sound by calling setEnable with true.
+ *<P>
+ * Setting the enable flag to true during construction acts as a request
+ * to start the sound playing "as soon as it can" be started.
+ * This could be close to immediately in limited cases, but several conditions,
+ * detailed below, must be met for a sound to be ready to be played.</UL>
+ *<P>
+ * Mute Sound
+ *
+ * <UL>When the mute state is set true, a playing sound is made to play silently.
+ *</UL><P>
+ * Pause Sound
+ *
+ * <UL>When the pause state is set true, a playing sound is paused.
+ *<P>
+ * Setting the enable flag to true during construction acts as a request
+ * to start the sound playing "as soon as it can" be started.
+ * This could be close to immediately in limited cases, but several conditions,
+ * detailed below, must be met for a sound to be ready to be played.</UL>
+ * <P>
+ * Scheduling Bounds
+ *
+ * <UL>
+ * A Sound is scheduled for activation when its scheduling region intersects
+ * the ViewPlatform's activation volume. This is used when the scheduling
+ * bounding leaf is set to null.</UL>
+ *<P>
+ * Scheduling Bounding Leaf
+ *
+ * <UL>When set to a value other than null, the scheduling bounding leaf
+ * region overrides the scheduling bounds
+ * object.</UL>
+ *<P>
+ * Prioritize Sound
+ *
+ * <UL>Sound Priority is used
+ * to rank concurrently playing sounds in order of importance during playback.
+ * When more sounds are started than the AudioDevice
+ * can handle, the sound node with the lowest priority ranking is
+ * deactivated (but continues playing silently). If a sound is deactivated
+ * (due to a sound with a higher
+ * priority being started), it is automatically re-activated when
+ * resources become available (e.g., when a sound with a higher priority
+ * finishes playing), or when the ordering of sound nodes are changed due to
+ * a change in a sound node's priority.
+ * <P>
+ * Sounds with a lower priority than sound that can
+ * not be played due to lack of channels will be played.
+ * For example, assume we have eight channels available for playing sounds.
+ * After ordering four sounds, we begin playing them in order, checking if
+ * the number of channels required to play a given sound are actually available
+ * before the sound is played. Furthermore, say the first sound needs three
+ * channels
+ * to play, the second sound needs four channels, the third sound needs three
+ * channels
+ * and the fourth sound needs only one channel. The first and second sounds
+ * can be started because they require seven of the eight available channels. The
+ * third sound can not be audibly started because it requires three channels and
+ * only one is still available. Consequently, the third sound starts playing
+ * 'silently.' The fourth sound can and will be started since it only requires
+ * one channel. The third sound will be made audible when three channels become
+ * available (i.e., when the first or second sound finishes playing).
+ * <P>
+ * Sounds given the same priority are ordered randomly. If the application
+ * wants a specific ordering, it must assign unique priorities to each sound.
+ * <P>
+ * Methods to determine what audio output resources are required for playing
+ * a Sound node on a particular AudioDevice and to determine the currently
+ * available audio output resources are described in the AudioDevice class.</UL>
+ * <P>
+ * Duration
+ *
+ * <UL>Each sound has a length of time in milliseconds that it
+ * can run (including repeating loop section)
+ * if it plays to completion. If the sound
+ * media type is streaming, or if the sound is looped indefinitely, then a
+ * value of -1 (implying infinite length) is returned.</UL>
+ *<P>
+ * Number of Channels used on Audio Device to Play Sound
+ *
+ * <UL>When a sound is started, it could use more than one channel on the
+ * selected AudioDevice it is to be played on. The number of Audio Device
+ * channels currently used for a sound can be queried using
+ * getNumberOfChannelsUsed().</UL>
+ *<P>
+ * Preparing a Sound to be Played
+ *
+ * <UL>Sound data associated with a Sound node, either during construction
+ * (when the MediaContainer is passed into the constructor as a parameter)
+ * or by calling setSoundData(), it can be prepared to begin playing
+ * only after the following conditions are satisfied:<p>
+ * 1) the Sound node has non-null sound data associated with it<br>
+ * 2) the Sound node is live<br>
+ * 3) there is an active View in the Universe and<br>
+ * 4) there is an initialized AudioDevice associated with the
+ * PhysicalEnvironment.<p>
+ * Depending on the type of MediaContainer the sound data is and on the
+ * implementation of the AudioDevice used, sound data preparation could consist
+ * of opening, attaching, loading, or copying into memory the associated sound data.
+ * The query method, isReady() returns true when the sound is fully preprocessed
+ * so that it is playable (audibly if active, silently if not active).</UL>
+ *<P>
+ * Playing Status
+ *
+ * <UL>A sound source will not be heard unless it is:<p>
+ * 1) enabled/started<br>
+ * 2) activated<br>
+ * 3) not muted<br>
+ * 4) not paused<p>
+ * While these conditions are meet, the sound is potentially audible
+ * and the method isPlaying() will return a status of true.
+ *<P>
+ * isPlaying returns false but isPlayingSilently returns true if a sound:<p>
+ * 1) is enabled before it is activated; it is begun playing silently.<br>
+ * 2) is enabled then deactivated while playing; it continues playing silently<br>
+ * 3) is enabled while it mute state is true
+ *<P>
+ * When the sound finishes playing it's sound data (including all loops), it
+ * is implicitly disabled.</UL>
+ *<P>
+ * @see AudioDevice
+ */
+
+public abstract class Sound extends Leaf {
+ // Constants for Sound object.
+ //
+ // These flags, when enabled using the setCapability method, allow an
+ // application to invoke methods that respectively read and write the
+ // sound fields.
+ // These capability flags are enforced only when the node is part of
+ // a live or compiled scene graph.
+
+ /**
+ * Specifies that this node allows access to its object's sound data
+ * information.
+ */
+ public static final int
+ ALLOW_SOUND_DATA_READ = CapabilityBits.SOUND_ALLOW_SOUND_DATA_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's sound data
+ * information.
+ */
+ public static final int
+ ALLOW_SOUND_DATA_WRITE = CapabilityBits.SOUND_ALLOW_SOUND_DATA_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's initial gain
+ * information.
+ */
+ public static final int
+ ALLOW_INITIAL_GAIN_READ = CapabilityBits.SOUND_ALLOW_INITIAL_GAIN_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's initial gain
+ * information.
+ */
+ public static final int
+ ALLOW_INITIAL_GAIN_WRITE = CapabilityBits.SOUND_ALLOW_INITIAL_GAIN_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's loop
+ * information.
+ */
+ public static final int
+ ALLOW_LOOP_READ = CapabilityBits.SOUND_ALLOW_LOOP_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's loop
+ * information.
+ */
+ public static final int
+ ALLOW_LOOP_WRITE = CapabilityBits.SOUND_ALLOW_LOOP_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's release flag
+ * information.
+ */
+ public static final int
+ ALLOW_RELEASE_READ = CapabilityBits.SOUND_ALLOW_RELEASE_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's release flag
+ * information.
+ */
+ public static final int
+ ALLOW_RELEASE_WRITE = CapabilityBits.SOUND_ALLOW_RELEASE_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's continuous
+ * play information.
+ */
+ public static final int
+ ALLOW_CONT_PLAY_READ = CapabilityBits.SOUND_ALLOW_CONT_PLAY_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's continuous
+ * play information.
+ */
+ public static final int
+ ALLOW_CONT_PLAY_WRITE = CapabilityBits.SOUND_ALLOW_CONT_PLAY_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's sound on
+ * information.
+ */
+ public static final int
+ ALLOW_ENABLE_READ = CapabilityBits.SOUND_ALLOW_ENABLE_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's sound on
+ * information.
+ */
+ public static final int
+ ALLOW_ENABLE_WRITE = CapabilityBits.SOUND_ALLOW_ENABLE_WRITE;
+
+ /**
+ * Specifies that this node allows read access to its scheduling bounds
+ * information.
+ */
+ public static final int
+ ALLOW_SCHEDULING_BOUNDS_READ = CapabilityBits.SOUND_ALLOW_SCHEDULING_BOUNDS_READ;
+
+ /**
+ * Specifies that this node allows write access to its scheduling bounds
+ * information.
+ */
+ public static final int
+ ALLOW_SCHEDULING_BOUNDS_WRITE = CapabilityBits.SOUND_ALLOW_SCHEDULING_BOUNDS_WRITE;
+
+ /**
+ * Specifies that this node allows read access to its priority order
+ * value.
+ */
+ public static final int
+ ALLOW_PRIORITY_READ = CapabilityBits.SOUND_ALLOW_PRIORITY_READ;
+
+ /**
+ * Specifies that this node allows write access to its priority order
+ * value.
+ */
+ public static final int
+ ALLOW_PRIORITY_WRITE = CapabilityBits.SOUND_ALLOW_PRIORITY_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's sound duration
+ * information.
+ */
+ public static final int
+ ALLOW_DURATION_READ = CapabilityBits.SOUND_ALLOW_DURATION_READ;
+
+ /**
+ * Specifies that this node allows access to its object's sound status
+ * denoting if it is ready to be played 'immediately'.
+ */
+ public static final int
+ ALLOW_IS_READY_READ = CapabilityBits.SOUND_ALLOW_IS_READY_READ;
+
+ /**
+ * Specifies that this node allows access to its object's sound audibly
+ * playing or playing silently status.
+ */
+ public static final int
+ ALLOW_IS_PLAYING_READ = CapabilityBits.SOUND_ALLOW_IS_PLAYING_READ;
+
+ /**
+ * Specifies that this node allows access to its number of channels
+ * used by this sound.
+ */
+ public static final int
+ ALLOW_CHANNELS_USED_READ = CapabilityBits.SOUND_ALLOW_CHANNELS_USED_READ;
+
+ /**
+ * Specifies that this node allows access to its object's mute flag
+ * information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_MUTE_READ = CapabilityBits.SOUND_ALLOW_MUTE_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's mute flag
+ * information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_MUTE_WRITE = CapabilityBits.SOUND_ALLOW_MUTE_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's pause flag
+ * information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_PAUSE_READ = CapabilityBits.SOUND_ALLOW_PAUSE_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's pause flag
+ * information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_PAUSE_WRITE = CapabilityBits.SOUND_ALLOW_PAUSE_WRITE;
+
+ /**
+ * Specifies that this node allows access to its object's sample rate scale
+ * factor information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_RATE_SCALE_FACTOR_READ = CapabilityBits.SOUND_ALLOW_RATE_SCALE_FACTOR_READ;
+
+ /**
+ * Specifies that this node allows writing to its object's sample rate scale
+ * factor information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_RATE_SCALE_FACTOR_WRITE = CapabilityBits.SOUND_ALLOW_RATE_SCALE_FACTOR_WRITE;
+
+ /**
+ * Denotes that there is no filter value associated with object's distance
+ * or angular attenuation array.
+ */
+ public static final float NO_FILTER = -1.0f;
+
+ /**
+ * Denotes that the sound's duration could not be calculated.
+ * A fall back for getDuration of a non-cached sound.
+ */
+ public static final int DURATION_UNKNOWN = -1;
+
+ /**
+ * When used as a loop count sound will loop an infinite number of time
+ * until explicitly stopped (setEnabled(false)).
+ */
+ public static final int INFINITE_LOOPS = -1;
+
+
+ /**
+ * Constructs and initializes a new Sound node using default
+ * parameters. The following defaults values are used:
+ * <ul>
+ * sound data: null<br>
+ * initial gain: 1.0<br>
+ * loop: 0<br>
+ * release flag: false<br>
+ * continuous flag: false<br>
+ * enable flag: false<br>
+ * scheduling bounds : null<br>
+ * scheduling bounding leaf : null<br>
+ * priority: 1.0<br>
+ * rate scale factor: 1.0<br>
+ * mute state: false<br>
+ * pause state: false<br>
+ * </ul>
+ */
+ public Sound() {
+ }
+
+ /**
+ * Constructs and initializes a new Sound node object using the provided
+ * data and gain parameter values, and defaults for all other fields. This
+ * constructor implicitly loads the sound data associated with this node if
+ * the implementation uses sound caching.
+ * @param soundData description of JMF source data used by this sound source
+ * @param initialGain overall amplitude scale factor applied to sound source
+ */
+ public Sound(MediaContainer soundData, float initialGain) {
+ ((SoundRetained)this.retained).setSoundData(soundData);
+ ((SoundRetained)this.retained).setInitialGain(initialGain);
+ }
+
+
+ /**
+ * Constructs and initializes a new Sound node using provided parameter
+ * values.
+ * @param soundData description of JMF source data used by this sound source
+ * @param initialGain overall amplitude scale factor applied to sound source
+ * @param loopCount number of times sound is looped when played
+ * @param release flag specifying whether the sound is to be played
+ * to end when stopped
+ * @param continuous flag specifying whether the sound silently plays
+ * when disabled
+ * @param enable flag specifying whether the sound is enabled
+ * @param region scheduling bounds
+ * @param priority defines playback priority if too many sounds started
+ */
+ public Sound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority ) {
+ ((SoundRetained)this.retained).setSoundData(soundData);
+ ((SoundRetained)this.retained).setInitialGain(initialGain);
+ ((SoundRetained)this.retained).setLoop(loopCount);
+ ((SoundRetained)this.retained).setReleaseEnable(release);
+ ((SoundRetained)this.retained).setContinuousEnable(continuous);
+ ((SoundRetained)this.retained).setEnable(enable);
+ ((SoundRetained)this.retained).setSchedulingBounds(region);
+ ((SoundRetained)this.retained).setPriority(priority);
+ }
+
+ /**
+ * Constructs and initializes a new Sound node using provided parameter
+ * values.
+ * @param soundData description of JMF source data used by this sound source
+ * @param initialGain overall amplitude scale factor applied to sound source
+ * @param loopCount number of times sound is looped when played
+ * @param release flag specifying whether the sound is to be played
+ * to end when stopped
+ * @param continuous flag specifying whether the sound silently plays
+ * when disabled
+ * @param enable flag specifying whether the sound is enabled
+ * @param region scheduling bounds
+ * @param priority defines playback priority if too many sounds started
+ * @param rateFactor defines playback sample rate scale factor
+ * @since Java 3D 1.3
+ */
+ public Sound(MediaContainer soundData,
+ float initialGain,
+ int loopCount,
+ boolean release,
+ boolean continuous,
+ boolean enable,
+ Bounds region,
+ float priority,
+ float rateFactor ) {
+ ((SoundRetained)this.retained).setSoundData(soundData);
+ ((SoundRetained)this.retained).setInitialGain(initialGain);
+ ((SoundRetained)this.retained).setLoop(loopCount);
+ ((SoundRetained)this.retained).setReleaseEnable(release);
+ ((SoundRetained)this.retained).setContinuousEnable(continuous);
+ ((SoundRetained)this.retained).setEnable(enable);
+ ((SoundRetained)this.retained).setSchedulingBounds(region);
+ ((SoundRetained)this.retained).setPriority(priority);
+ ((SoundRetained)this.retained).setRateScaleFactor(rateFactor);
+ }
+
+ /**
+ * Sets fields that define the sound source data of this node.
+ * @param soundData description of JMF source data used by this sound source
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setSoundData(MediaContainer soundData) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SOUND_DATA_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound0"));
+
+ if (this instanceof BackgroundSound)
+ ((SoundRetained)this.retained).setSoundData(soundData);
+ else // instanceof PointSound or ConeSound
+ ((PointSoundRetained)this.retained).setSoundData(soundData);
+ }
+
+ /**
+ * Retrieves description/data associated with this sound source.
+ * @return soundData description of JMF source data used by this sound source
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public MediaContainer getSoundData() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SOUND_DATA_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound1"));
+
+ return ((SoundRetained)this.retained).getSoundData();
+ }
+
+ /**
+ * Set the overall gain scale factor applied to data associated with this
+ * source to increase or decrease its overall amplitude.
+ * @param amplitude (gain) scale factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setInitialGain(float amplitude) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INITIAL_GAIN_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound2"));
+
+ ((SoundRetained)this.retained).setInitialGain(amplitude);
+ }
+
+ /**
+ * Get the overall gain applied to the sound data associated with source.
+ * @return overall gain scale factor applied to sound source data.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getInitialGain() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_INITIAL_GAIN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound3"));
+
+ return ((SoundRetained)this.retained).getInitialGain();
+ }
+
+ /**
+ * Sets a sound's loop count.
+ * @param loopCount number of times sound is looped during play
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setLoop(int loopCount) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_LOOP_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound4"));
+
+ ((SoundRetained)this.retained).setLoop(loopCount);
+ }
+
+ /**
+ * Retrieves loop count for this sound
+ * @return loop count
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getLoop() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_LOOP_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound5"));
+
+ return ((SoundRetained)this.retained).getLoop();
+ }
+
+ /**
+ * Enables or disables the release flag for the sound associated with
+ * this sound.
+ * @param state release flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setReleaseEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_RELEASE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound6"));
+
+ ((SoundRetained)this.retained).setReleaseEnable(state);
+ }
+
+ /**
+ * Retrieves the release flag for sound associated with sound.
+ * @return sound's release flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getReleaseEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_RELEASE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound7"));
+
+ return ((SoundRetained)this.retained).getReleaseEnable();
+ }
+
+ /**
+ * Enables or disables continuous play flag.
+ * @param state denotes if deactivated sound silently continues playing
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setContinuousEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CONT_PLAY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound8"));
+
+ ((SoundRetained)this.retained).setContinuousEnable(state);
+ }
+
+ /**
+ * Retrieves sound's continuous play flag.
+ * @return flag denoting if deactivated sound silently continues playing
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getContinuousEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CONT_PLAY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound9"));
+
+ return ((SoundRetained)this.retained).getContinuousEnable();
+ }
+
+ /**
+ * Enable or disable sound.
+ * @param state enable (on/off) flag denotes if active sound is heard
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ENABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound10"));
+
+ if (this instanceof BackgroundSound)
+ ((SoundRetained)this.retained).setEnable(state);
+ else // instanceof PointSound or ConeSound
+ ((PointSoundRetained)this.retained).setEnable(state);
+ }
+
+ /**
+ * Retrieves sound's enabled flag.
+ * @return sound enabled flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ENABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound21"));
+
+ return ((SoundRetained)this.retained).getEnable();
+ }
+
+
+ /**
+ * Set the Sound's scheduling region to the specified bounds.
+ * This is used when the scheduling bounding leaf is set to null.
+ * @param region the bounds that contains the Sound's new scheduling
+ * region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setSchedulingBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCHEDULING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound11"));
+
+ ((SoundRetained)this.retained).setSchedulingBounds(region);
+ }
+
+ /**
+ * Retrieves the Sound node's scheduling bounds.
+ * @return this Sound's scheduling bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getSchedulingBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCHEDULING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound12"));
+
+ return ((SoundRetained)this.retained).getSchedulingBounds();
+ }
+
+
+ /**
+ * Set the Sound's scheduling region to the specified bounding leaf.
+ * When set to a value other than null, this overrides the scheduling
+ * bounds object.
+ * @param region the bounding leaf node used to specify the Sound
+ * node's new scheduling region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setSchedulingBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCHEDULING_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound11"));
+
+ ((SoundRetained)this.retained).setSchedulingBoundingLeaf(region);
+ }
+
+ /**
+ * Retrieves the Sound node's scheduling bounding leaf.
+ * @return this Sound's scheduling bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getSchedulingBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SCHEDULING_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound12"));
+
+ return ((SoundRetained)this.retained).getSchedulingBoundingLeaf();
+ }
+
+
+ /**
+ * Set sound's priority value.
+ * @param priority value used to order sound's importance for playback.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPriority(float priority) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PRIORITY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound15"));
+
+ ((SoundRetained)this.retained).setPriority(priority);
+ }
+
+ /**
+ * Retrieves sound's priority value.
+ * @return sound priority value
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getPriority() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PRIORITY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound16"));
+
+ return ((SoundRetained)this.retained).getPriority();
+ }
+
+
+ /**
+ * Get the Sound's duration
+ * @return this Sound's duration in milliseconds including repeated
+ * loops
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public long getDuration() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_DURATION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound17"));
+
+ return ((SoundRetained)this.retained).getDuration();
+ }
+
+
+ /**
+ * Retrieves sound's 'ready' status. If this sound is fully
+ * prepared to begin playing (audibly or silently) on all
+ * initialized audio devices, this method returns true.
+ * @return flag denoting if sound is immediate playable or not
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean isReady() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_IS_READY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound22"));
+
+ return ((SoundRetained)this.retained).isReady();
+ }
+
+ /**
+ * Retrieves sound's 'ready' status. If this sound is fully
+ * prepared to begin playing (audibly or silently) on the audio
+ * device associated with this view, this method returns true.
+ * @param view the view on which to query the ready status.
+ * @return flag denoting if sound is immediate playable or not
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public boolean isReady(View view) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_IS_READY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound22"));
+
+ return ((SoundRetained)this.retained).isReady(view);
+ }
+
+
+ /**
+ * Retrieves sound's play status. If this sound is audibly playing on any
+ * initialized audio device, this method returns true.
+ * @return flag denoting if sound is playing (potentially audible) or not
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean isPlaying() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_IS_PLAYING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound18"));
+
+ return ((SoundRetained)this.retained).isPlaying();
+ }
+
+ /**
+ * Retrieves sound's play status. If this sound is audibly playing on the
+ * audio device associated with the given view, this method returns
+ * true.
+ * @param view the view on which to query the isPlaying status.
+ * @return flag denoting if sound is playing (potentially audible) or not
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public boolean isPlaying(View view) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_IS_PLAYING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound18"));
+
+ return ((SoundRetained)this.retained).isPlaying(view);
+ }
+
+ /**
+ * Retrieves sound's silent status. If this sound is silently playing on
+ * any initialized audio device, this method returns true.
+ * @return flag denoting if sound is silently playing (enabled but not active)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean isPlayingSilently() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IS_PLAYING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound18"));
+
+ return ((SoundRetained)this.retained).isPlayingSilently();
+ }
+
+ /**
+ * Retrieves sound's silent status. If this sound is silently playing on
+ * the audio device associated with the given view, this method returns
+ * true.
+ * The isPlayingSilently state is affected by enable, mute, and continuous
+ * states as well as active status of sound.
+ * @param view the view on which to query the isPlayingSilently status.
+ * @return flag denoting if sound is silently playing (enabled but not active)
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public boolean isPlayingSilently(View view) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_IS_PLAYING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound18"));
+
+ return ((SoundRetained)this.retained).isPlayingSilently(view);
+ }
+
+
+ /**
+ * Retrieves number of channels that are being used to render this sound
+ * on the audio device associated with the Virtual Universe's primary view.
+ * @return number of channels used by sound; returns 0 if not playing
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getNumberOfChannelsUsed() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHANNELS_USED_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound20"));
+
+ return ((SoundRetained)this.retained).getNumberOfChannelsUsed();
+ }
+
+ /**
+ * Retrieves number of channels that are being used to render this sound
+ * on the audio device associated with given view.
+ * @param view the view on which to query the number of channels used.
+ * @return number of channels used by sound; returns 0 if not playing
+ * @exception CapabilityNotSetException if appropriate capability is
+ * @since Java 3D 1.3
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getNumberOfChannelsUsed(View view) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHANNELS_USED_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound20"));
+
+ return ((SoundRetained)this.retained).getNumberOfChannelsUsed(view);
+ }
+
+ /**
+ * Set mute state flag. If the sound is playing it will be set to
+ * play silently
+ * @param state flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setMute(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_MUTE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound23"));
+
+ ((SoundRetained)this.retained).setMute(state);
+ }
+
+ /**
+ * Retrieves sound Mute state.
+ * A return value of true does not imply that the sound has
+ * been started playing or is still playing silently.
+ * @return mute state flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public boolean getMute() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_MUTE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound24"));
+
+ return ((SoundRetained)this.retained).getMute();
+ }
+
+ /**
+ * Pauses or resumes (paused) playing sound.
+ * @param state pause flag
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setPause(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PAUSE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound25"));
+
+ ((SoundRetained)this.retained).setPause(state);
+ }
+
+ /**
+ * Retrieves the value of the Pause state flag.
+ * A return value of true does not imply that the sound was
+ * started playing and then paused.
+ * @return pause state
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public boolean getPause() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PAUSE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound26"));
+
+ return ((SoundRetained)this.retained).getPause();
+ }
+
+ /**
+ * Sets Sample Rate.
+ * Changes (scales) the playback rate of a sound independent of
+ * Doppler rate changes - applied to ALL sound types.
+ * Affects device sample rate playback and thus affects both pitch and speed
+ * @param scaleFactor %%% describe this.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public void setRateScaleFactor(float scaleFactor) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_RATE_SCALE_FACTOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound27"));
+
+ ((SoundRetained)this.retained).setRateScaleFactor(scaleFactor);
+ }
+
+ /**
+ * Retrieves Sample Rate.
+ * @return sample rate scale factor
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @since Java 3D 1.3
+ */
+ public float getRateScaleFactor() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_RATE_SCALE_FACTOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Sound28"));
+
+ return ((SoundRetained)this.retained).getRateScaleFactor();
+ }
+
+ /**
+ * Copies all Sound 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ SoundRetained orgRetained = (SoundRetained)originalNode.retained;
+
+ SoundRetained thisRetained = (SoundRetained)this.retained;
+
+ thisRetained.setSoundData((MediaContainer) getNodeComponent(
+ orgRetained.getSoundData(),
+ forceDuplicate,
+ originalNode.nodeHashtable));
+ thisRetained.setInitialGain(orgRetained.getInitialGain());
+ thisRetained.setLoop(orgRetained.getLoop());
+ thisRetained.setReleaseEnable(orgRetained.getReleaseEnable());
+ thisRetained.setContinuousEnable(orgRetained.getContinuousEnable());
+ thisRetained.setSchedulingBounds(orgRetained.getSchedulingBounds());
+ thisRetained.setPriority(orgRetained.getPriority());
+ thisRetained.setEnable(orgRetained.getEnable());
+
+ // updateNodeReferences will set the following correctly
+ thisRetained.setSchedulingBoundingLeaf(orgRetained.getSchedulingBoundingLeaf());
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ SoundRetained rt = (SoundRetained) retained;
+ BoundingLeaf bl = rt.getSchedulingBoundingLeaf();
+
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.setSchedulingBoundingLeaf((BoundingLeaf)o);
+ }
+ MediaContainer sd = rt.getSoundData();
+ if (sd != null) {
+ rt.setSoundData(sd);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SoundException.java b/src/classes/share/javax/media/j3d/SoundException.java
new file mode 100644
index 0000000..645de0c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SoundException.java
@@ -0,0 +1,35 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Indicates a problem in loading or playing a sound sample.
+ */
+public class SoundException extends RuntimeException{
+
+/**
+ * Create the exception object with default values.
+ */
+ public SoundException(){
+ }
+
+/**
+ * Create the exception object that outputs message.
+ * @param str the message string to be output.
+ */
+ public SoundException(String str){
+
+ super(str);
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SoundRenderer.java b/src/classes/share/javax/media/j3d/SoundRenderer.java
new file mode 100644
index 0000000..bb47217
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SoundRenderer.java
@@ -0,0 +1,75 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Vector;
+
+class SoundRenderer extends Object {
+
+ SoundRenderer() {
+ }
+
+ void activate(SoundRetained sound, SoundscapeRetained ss) {
+ AuralAttributesRetained aa = ss.attributes.mirrorAa;
+
+ if (sound instanceof BackgroundSoundRetained) {
+ System.out.println("Activating BackgroundSoundRetained");
+ } else if (sound instanceof ConeSoundRetained) {
+ System.out.println("Activating ConeSoundRetained");
+ } else if (sound instanceof PointSoundRetained) {
+ System.out.println("Activating PointSoundRetained");
+ }
+ if (ss != null)
+ System.out.println("Soundscape is " + ss);
+ else
+ System.out.println("Soundscape is null");
+
+ if (aa != null)
+ System.out.println("AuralAttributes is " + aa);
+ else
+ System.out.println("AuralAttributes is null");
+ }
+
+ void update(SoundRetained sound, SoundscapeRetained ss) {
+ AuralAttributesRetained aa = ss.attributes.mirrorAa;
+
+ if (false) {
+ if (sound instanceof BackgroundSoundRetained) {
+ System.out.println("Updating BackgroundSoundRetained");
+ } else if (sound instanceof ConeSoundRetained) {
+ System.out.println("Updating ConeSoundRetained");
+ } else if (sound instanceof PointSoundRetained) {
+ System.out.println("Updating PointSoundRetained");
+ }
+ System.out.println("Soundscape is " + ss);
+ }
+ }
+
+ void deactivate(SoundRetained sound) {
+ if (false) {
+ if (sound instanceof BackgroundSoundRetained) {
+ System.out.println("Deactivating BackgroundSoundRetained");
+ } else if (sound instanceof ConeSoundRetained) {
+ System.out.println("Deactivating ConeSoundRetained");
+ } else if (sound instanceof PointSoundRetained) {
+ System.out.println("Deactivating PointSoundRetained");
+ }
+ }
+ }
+
+ public String toString() {
+ return "";
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SoundRetained.java b/src/classes/share/javax/media/j3d/SoundRetained.java
new file mode 100644
index 0000000..096fcae
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SoundRetained.java
@@ -0,0 +1,1297 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+import java.util.ArrayList;
+
+
+
+/**
+ * SoundRetained is an abstract class that contains instance varables common
+ * to all retained sounds.
+ */
+
+abstract class SoundRetained extends LeafRetained
+{
+
+ /**
+ * Null Sound identifier denotes sound is not created or initialized
+ */
+ static final int NULL_SOUND = -1;
+
+ /**
+ * sound data associated with sound source
+ */
+ MediaContainer soundData = null;
+
+ /**
+ * Overall Scale Factor applied to sound.
+ */
+ float initialGain = 1.0f; // Valid values are >= 0.0.
+
+ /**
+ * Number of times sound is looped/repeated during play
+ */
+ int loopCount = 0; // Range from 0 to POSITIVE_INFINITY(-1)
+
+ /**
+ * Switch for turning sound on or off while the sound is "active"
+ */
+ boolean enable = false;
+
+ /**
+ * Type of release when sound is disabled.
+ * If true, sound plays thru to end of sample before disabled
+ * Otherwise, sound is disabled immediately.
+ */
+ boolean release = false;
+
+ /**
+ * Flag denoting if sound silently continues playing when it's deactivated.
+ */
+ boolean continuous = false;
+
+ /**
+ * Flag denoting if sound is explicitly muted, so that if begins playing
+ * it will be played silently.
+ */
+ boolean mute = false;
+
+ /**
+ * Flag denoting if sound is paused from playing - waiting to be resumed
+ */
+ boolean pause = false;
+
+ /**
+ * Sound priority ranking value.
+ * Valid values are 0.0 to 1.0
+ */
+ float priority = 1.0f;
+
+ /**
+ * Rate Scale Factor applied to sounds playback sample rate in Hertz.
+ * Valid values are 0.0 to 1.0
+ */
+ float rate = 1.0f;
+
+ /**
+ * The Boundary object defining the sound's scheduling region.
+ */
+ Bounds schedulingRegion = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * The transformed bounds from either schedulingRegion or boundingLeaf
+ */
+ Bounds transformedRegion = null;
+
+ // Dirty bit flags used to pass change as part of message, and are
+ // acclummuated/stored in SoundSchedulerAtoms.
+ // These flags are grouped into two catagories:
+ // attribsDirty for sound node fields
+ // stateDirty for changes to sound state not reflected by sound fields.
+
+ // Attributes Dirty bit flags
+ // This bitmask is set when sound node attribute is changed by the user.
+ static final int SOUND_DATA_DIRTY_BIT = 0x0001;
+ static final int INITIAL_GAIN_DIRTY_BIT = 0x0002;
+ static final int LOOP_COUNT_DIRTY_BIT = 0x0004;
+ static final int BOUNDS_DIRTY_BIT = 0x0008;
+ static final int BOUNDING_LEAF_DIRTY_BIT = 0x0010;
+ static final int PRIORITY_DIRTY_BIT = 0x0020;
+ static final int POSITION_DIRTY_BIT = 0x0040;
+ static final int DISTANCE_GAIN_DIRTY_BIT = 0x0080;
+ static final int BACK_DISTANCE_GAIN_DIRTY_BIT = 0x0100;
+ static final int DIRECTION_DIRTY_BIT = 0x0200;
+ static final int ANGULAR_ATTENUATION_DIRTY_BIT = 0x0400;
+ static final int RATE_DIRTY_BIT = 0x0800;
+
+ static final int BOUNDS_CHANGED =
+ BOUNDS_DIRTY_BIT | BOUNDING_LEAF_DIRTY_BIT;
+
+ static final int ATTRIBUTE_DIRTY_BITS =
+ SOUND_DATA_DIRTY_BIT | INITIAL_GAIN_DIRTY_BIT |
+ LOOP_COUNT_DIRTY_BIT | PRIORITY_DIRTY_BIT |
+ RATE_DIRTY_BIT;
+
+ static final int POSITIONAL_DIRTY_BITS =
+ ATTRIBUTE_DIRTY_BITS |
+ POSITION_DIRTY_BIT | DISTANCE_GAIN_DIRTY_BIT;
+
+ static final int DIRECTIONAL_DIRTY_BITS =
+ POSITIONAL_DIRTY_BITS | BACK_DISTANCE_GAIN_DIRTY_BIT |
+ DIRECTION_DIRTY_BIT | ANGULAR_ATTENUATION_DIRTY_BIT;
+
+ // All attribute bits that are specifically set or cleared for any node */
+ static final int ALL_ATTIBS_DIRTY_BITS = 0x0FFF;
+
+ // State Dirty bit flags
+ // This bitmask is set when scene graph state is changed.
+ static final int LIVE_DIRTY_BIT = 0x0001;
+ static final int IMMEDIATE_MODE_DIRTY_BIT = 0x0002;
+ static final int LOAD_SOUND_DIRTY_BIT = 0x0004;
+ static final int RELEASE_DIRTY_BIT = 0x0008;
+ static final int CONTINUOUS_DIRTY_BIT = 0x0010;
+ static final int ENABLE_DIRTY_BIT = 0x0020;
+ static final int MUTE_DIRTY_BIT = 0x0040;
+ static final int PAUSE_DIRTY_BIT = 0x0080;
+ static final int XFORM_DIRTY_BIT = 0x8000;
+
+ // All attribute bits that are specifically set or cleared for any node */
+ static final int ALL_STATE_DIRTY_BITS = 0x80FF;
+
+ // The type of sound node: Background, Point, Cone
+ int soundType = NULL_SOUND;
+
+ // A back reference to the scene graph sound, when this is a mirror sound
+ SoundRetained sgSound = null;
+
+ // A HashKey for sounds in a shared group
+ HashKey key = null;
+
+ // An array of mirror sounds, one for each instance of this sound in a
+ // shared group. Entry 0 is the only one valid if we are not in a shared
+ // group.
+ SoundRetained[] mirrorSounds = new SoundRetained[1];
+
+ // The number of valid sounds in mirrorSounds
+ int numMirrorSounds = 0;
+
+ /**
+ * Array of references to sound scheduler atoms associated with this node.
+ * For each view that a sound node is associated with a sound scheduler
+ * atom is created and maintained
+ */
+ // for a particular view that are playing either audibly or silently.
+ private SoundSchedulerAtom[] loadedAtoms = new SoundSchedulerAtom[1];
+ private int atomCount = 0;
+
+ /**
+ * This is true when this sound is referenced in an immediate mode context
+ */
+ boolean inImmCtx = false;
+
+ /**
+ * Load Sound Data Status
+ */
+ static final int LOAD_COMPLETE = 2;
+ // load requested but could not be performed due because sound not live
+ static final int LOAD_PENDING = 1;
+ static final int LOAD_NULL = 0;
+ static final int LOAD_FAILED = -1;
+ int loadStatus = LOAD_NULL;
+ long duration = Sound.DURATION_UNKNOWN;
+
+ // Static initializer for SoundRetained class
+ static {
+ VirtualUniverse.loadLibraries();
+ }
+
+ // Target threads to be notified when sound changes
+ static final int targetThreads = J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER;
+
+ // Is true, if the mirror light is viewScoped
+ boolean isViewScoped = false;
+
+
+ /**
+ * Dispatch a message about a sound attribute change
+ */
+ void dispatchAttribChange(int dirtyBit, Object argument) {
+ // Send message including a integer argument
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER;
+ createMessage.type = J3dMessage.SOUND_ATTRIB_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(dirtyBit);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorSounds);
+ else
+ createMessage.args[2] = new Integer(1);
+ createMessage.args[3] = mirrorSounds.clone();
+ createMessage.args[4] = argument;
+ if (debugFlag)
+ debugPrint("dispatchAttribChange with " + dirtyBit);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ /**
+ * Dispatch a message about a sound state change
+ */
+ void dispatchStateChange(int dirtyBit, Object argument) {
+ // Send message including a integer argument
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER;
+ createMessage.type = J3dMessage.SOUND_STATE_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(dirtyBit);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorSounds);
+ else
+ createMessage.args[2] = new Integer(1);
+ createMessage.args[3] = mirrorSounds.clone();
+ createMessage.args[4] = argument;
+ if (debugFlag)
+ debugPrint("dispatchStateChange with " + dirtyBit);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ /**
+ * Assign value into sound data field
+ * @param soundData description of sound source data
+ */
+ void setSoundDataState(MediaContainer soundData) {
+ this.soundData = soundData;
+ }
+
+ /**
+ * Associates sound data with this sound source node
+ * Attempt to load sound
+ * @param soundData descrition of sound source data
+ */
+ void setSoundData(MediaContainer soundData) {
+ // if resetting soundData to the same value don't bother doing anything
+ if (this.soundData == soundData) {
+ return;
+ }
+
+ if (this.soundData != null) {
+ // this sound node had older sound data; clear it out
+ ((MediaContainerRetained)this.soundData.retained).removeUser(this);
+ }
+
+ if (source != null && source.isLive()) {
+ if (this.soundData != null) {
+ ((MediaContainerRetained)this.soundData.retained).clearLive(refCount);
+ }
+
+ if (soundData != null) {
+ ((MediaContainerRetained)soundData.retained).setLive(inBackgroundGroup, refCount);
+ ((MediaContainerRetained)soundData.retained).addUser(this);
+ }
+ }
+
+ this.soundData = soundData;
+ dispatchAttribChange(SOUND_DATA_DIRTY_BIT, soundData);
+
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound data associated with this sound source node
+ * @return sound source data container
+ */
+ MediaContainer getSoundData() {
+ return ( this.soundData );
+ }
+
+
+ /**
+ * Set the gain scale factor applied to this sound
+ * @param amplitude gain scale factor
+ */
+ void setInitialGain(float scaleFactor) {
+ if (scaleFactor < 0.0f)
+ this.initialGain = 0.0f;
+ else
+ this.initialGain = scaleFactor;
+
+ dispatchAttribChange(INITIAL_GAIN_DIRTY_BIT, (new Float(scaleFactor)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+ /**
+ * Get the overall gain (applied to the sound data associated with source).
+ * @return overall gain of sound source
+ */
+ float getInitialGain() {
+ return (float) this.initialGain;
+ }
+
+
+ /**
+ * Sets the sound's loop count
+ * @param loopCount number of times sound is looped during play
+ */
+ void setLoop(int loopCount) {
+ if (loopCount < -1)
+ this.loopCount = -1;
+ else
+ this.loopCount = (int) loopCount;
+ if (debugFlag)
+ debugPrint("setLoopCount called with " + this.loopCount);
+
+ dispatchAttribChange(LOOP_COUNT_DIRTY_BIT, (new Integer(loopCount)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves the loop count
+ * @return loop count for data associated with sound
+ */
+ int getLoop() {
+ return (int) this.loopCount;
+ }
+
+ /**
+ * Enable or disable the release flag for this sound source
+ * @param state flag denoting release sound before stopping
+ */
+ void setReleaseEnable(boolean state) {
+ this.release = state;
+ dispatchAttribChange(RELEASE_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves release flag for sound associated with this source node
+ * @return sound's release flag
+ */
+ boolean getReleaseEnable() {
+ return (boolean) this.release;
+ }
+
+ /**
+ * Enable or disable continuous play flag
+ * @param state denotes if sound continues playing silently when deactivated
+ */
+ void setContinuousEnable(boolean state) {
+ this.continuous = state;
+ dispatchAttribChange(CONTINUOUS_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound's continuous play flag
+ * @return flag denoting if deactivated sound silently continues playing
+ */
+ boolean getContinuousEnable() {
+ return (boolean) this.continuous;
+ }
+
+ /**
+ * Sets the flag denotine sound enabled/disabled and sends a message
+ * for the following to be done:
+ * If state is true:
+ * if sound is not playing, sound is started.
+ * if sound is playing, sound is stopped, then re-started.
+ * If state is false:
+ * if sound is playing, sound is stopped
+ * @param state true or false to enable or disable the sound
+ */
+ void setEnable(boolean state) {
+ enable = state;
+ // QUESTION: Is this still valid code?
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ dispatchStateChange(ENABLE_DIRTY_BIT, (new Boolean(enable)));
+ }
+
+ /**
+ * Retrieves sound's enabled flag
+ * @return sound enabled flag
+ */
+ boolean getEnable() {
+ return enable;
+ }
+
+ /**
+ * Set the Sound's scheduling region.
+ * @param region a region that contains the Sound's new scheduling region
+ */
+ void setSchedulingBounds(Bounds region) {
+ if (region != null) {
+ schedulingRegion = (Bounds) region.clone();
+ if (staticTransform != null) {
+ schedulingRegion.transform(staticTransform.transform);
+ }
+ // QUESTION: Clone into transformedRegion IS required. Why?
+ transformedRegion = (Bounds) schedulingRegion.clone();
+ if (debugFlag)
+ debugPrint("setSchedulingBounds for a non-null region");
+ }
+ else {
+ schedulingRegion = null;
+ // QUESTION: Is transformedRegion of node (not mirror node)
+ // even looked at???
+ transformedRegion = null;
+ if (debugFlag)
+ debugPrint("setSchedulingBounds for a NULL region");
+ }
+ // TODO: test that this works - could not new Bounds() since
+ // Bounds is an abstract class and can't be instantiated
+ dispatchAttribChange(BOUNDS_DIRTY_BIT, region);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Get the Sound's scheduling region.
+ * @return this Sound's scheduling region information
+ */
+ Bounds getSchedulingBounds() {
+ Bounds b = null;
+
+ if (this.schedulingRegion != null) {
+ b = (Bounds) schedulingRegion.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Set the Sound's scheduling region to the specified Leaf node.
+ */
+ void setSchedulingBoundingLeaf(BoundingLeaf region) {
+ int i;
+ int numSnds = numMirrorSounds;
+ if (numMirrorSounds == 0)
+ numSnds = 1;
+
+ if ((boundingLeaf != null) &&
+ (source != null && source.isLive())) {
+ // Remove the mirror lights as users of the original bounding leaf
+ for (i = 0; i < numSnds; i++) {
+ boundingLeaf.mirrorBoundingLeaf.removeUser(mirrorSounds[i]);
+ }
+ }
+
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ // Add all mirror sounds as user of this bounding leaf
+ if (source != null && source.isLive()) {
+ for (i = 0; i < numSnds; i++) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorSounds[i]);
+ }
+ }
+ } else {
+ boundingLeaf = null;
+ }
+ // TODO: since BoundingLeaf constructor only takes Bounds
+ // test if region passed into dispatchAttribChange correctly.
+ dispatchAttribChange(BOUNDING_LEAF_DIRTY_BIT, region);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Get the Sound's scheduling region
+ */
+ BoundingLeaf getSchedulingBoundingLeaf() {
+ if (boundingLeaf != null) {
+ return((BoundingLeaf)boundingLeaf.source);
+ } else {
+ return null;
+ }
+ }
+
+ // The update Object function.
+ synchronized void updateMirrorObject(Object[] objs) {
+ Transform3D trans = null;
+ int component = ((Integer)objs[1]).intValue();
+ if (component == -1) { // update everything
+ // object 2 contains the mirror object that needs to be
+ // updated
+ initMirrorObject(((SoundRetained)objs[2]));
+ }
+
+ // call the parent's mirror object update routine
+ super.updateMirrorObject(objs);
+
+ }
+
+ void updateBoundingLeaf(long refTime) {
+ // This is necessary, if for example, the region
+ // changes from sphere to box.
+ if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) {
+ transformedRegion = boundingLeaf.transformedRegion;
+ } else { // evaluate schedulingRegion if not null
+ if (schedulingRegion != null) {
+ transformedRegion = schedulingRegion.copy(transformedRegion);
+ transformedRegion.transform(schedulingRegion,
+ getLastLocalToVworld());
+ } else {
+ transformedRegion = null;
+ }
+ }
+ }
+
+
+ /**
+ * Set sound's proirity value.
+ * @param priority value used to order sound's importance for playback.
+ */
+ void setPriority(float rank) {
+ if (rank == this.priority)
+ // changing priority is expensive in the sound scheduler(s)
+ // so only dispatch a message if 'new' priority value is really
+ // different
+ return;
+
+ this.priority = rank;
+ dispatchAttribChange(PRIORITY_DIRTY_BIT, (new Float(rank)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound's priority value.
+ * @return sound priority value
+ */
+ float getPriority() {
+ return (this.priority);
+ }
+
+
+ /**
+ * Retrieves sound's duration in milliseconds
+ * @return sound's duration, returns DURATION_UNKNOWN if duration could
+ * not be queried from the audio device
+ */
+ long getDuration() {
+ return (duration);
+ }
+
+
+ /**
+ * Set scale factor
+ * @param scaleFactor applied to sound playback rate
+ */
+ void setRateScaleFactor(float scaleFactor) {
+ this.rate = scaleFactor;
+ dispatchAttribChange(RATE_DIRTY_BIT, (new Float(scaleFactor)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound's rate scale factor
+ * @return sound rate scale factor
+ */
+ float getRateScaleFactor() {
+ return (this.rate);
+ }
+
+ void changeAtomList(SoundSchedulerAtom atom, int loadStatus) {
+ if (atom == null)
+ return;
+ if (loadStatus == SoundRetained.LOAD_COMPLETE) {
+ // atom is successfully loaded, so add this atom to array of atoms
+ // associated with this sound, if not already in list
+ for (int i=0; i<atomCount; i++) {
+ if (atom == loadedAtoms[i])
+ return;
+ }
+ // add atom to list
+ atomCount++;
+ int currentArrayLength = loadedAtoms.length;
+ if (atomCount > currentArrayLength) {
+ // expand array - replace with a larger array
+ loadedAtoms = new SoundSchedulerAtom[2*currentArrayLength];
+ }
+ loadedAtoms[atomCount-1] = atom; // store reference to new atom
+ // all atoms sample durations SHOULD be the same so store it in node
+ this.duration = atom.sampleLength; // TODO: refine later? in ms
+ }
+ else { // atom is NOT loaded or has been unloaded; remove from list
+ if (atomCount == 0)
+ return;
+
+ // remove atom from array of playing atoms if it is in list
+ boolean atomFound = false;
+ int i;
+ for (i=0; i<atomCount; i++) {
+ if (atom == loadedAtoms[i]) {
+ atomFound = true;
+ continue;
+ }
+ }
+ if (!atomFound)
+ return;
+
+ // otherwise remove atom from list by close up list
+ for (int j=i; j<atomCount; j++) {
+ loadedAtoms[j] = loadedAtoms[j+1];
+ }
+ atomCount--;
+ if (atomCount == 0)
+ this.duration = Sound.DURATION_UNKNOWN; // clear sound duration
+ }
+ }
+
+ /**
+ * Retrieves sound's ready state for ALL active views.
+ * For this node, the list of sound scheduler atoms associated with
+ * each view is maintained. The 'loaded' (=is ready) state is
+ * true only if the following are true for all views/sound schedulers:
+ *
+ * <ul>
+ * 1) the Sound node has a non-null sound data and this data has
+ * sucessfully been loaded/opened/copied/attached;<br>
+ * 2) the Sound node is live;<br>
+ * 3) there is at least one active View in the Universe; and<br>
+ * 4) an instance of an AudioDevice is attached to the current
+ * PhysicalEnvironment.
+ * </ul>
+ *
+ * @return true if potentially playable (audibly or silently); false otherwise
+ */
+ boolean isReady() {
+ // all the atoms in the atom list must be are ready for this
+ // method to return true
+ // if any non-null atoms are found NOT ready, return false.
+ boolean atomFoundReady = true;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ else
+ if (atom.loadStatus == SoundRetained.LOAD_COMPLETE) {
+ atomFoundReady = true;
+ continue;
+ }
+ else
+ return false;
+ }
+ if (atomFoundReady) // at least on atom found ready
+ return true;
+ else
+ // not even one atom is associated with node so none are loaded
+ return false;
+ }
+
+ /**
+ * Retrieves sound's ready state for a particular view.
+ * For this node, the list of sound scheduler atoms associated with
+ * each view is maintained. The 'loaded' (=is ready) state is
+ * true only if the following are true for the given view:
+ *
+ * <ul>
+ * 1) the Sound node has a non-null sound data and this data has
+ * sucessfully been loaded/opened/copied/attached;<br>
+ * 2) the Sound node is live;<br>
+ * 3) the given View is active in the Universe; and<br>
+ * 4) an instance of an AudioDevice is attached to the current
+ * PhysicalEnvironment.
+ * </ul>
+ *
+ * @param viewRef view to test sound readiness for
+ * @return true if potentially playable (audibly or silently); false otherwise
+ */
+ boolean isReady(View viewRef) {
+ // if an atom in the atom list that is associated with the
+ // given view is found and has been loaded than return true,
+ // otherwise return false.
+ if (viewRef == null)
+ return false;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef)
+ if (atom.loadStatus != SoundRetained.LOAD_COMPLETE)
+ return false;
+ else
+ return true;
+ else // atom is not associated with given referenced view
+ continue;
+ }
+ return false; // sound scheduler atom for given view not found
+
+ }
+
+ // *******************************
+ // Play Status - isPlaying states
+ // *******************************
+
+ /**
+ * Retrieves sound's playing status
+ * true if potentially audible (enabled and active) on ANY audio device
+ * false otherwise
+ * @return sound playing flag
+ */
+ boolean isPlaying() {
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.status == SoundSchedulerAtom.SOUND_AUDIBLE)
+ return true;
+ else
+ continue; // look for at lease one atom that is playing
+ }
+ // not even one atom is associated with this node so none are playing
+ return false;
+ }
+
+ /**
+ * Retrieves sound's playing status for a particular view
+ * true if potentially audible (enabled and active) on audio device
+ * associated with the given view
+ * false otherwise
+ * @param viewRef view to test sound playing state for
+ * @return sound playing flag
+ */
+ boolean isPlaying(View viewRef) {
+ if (viewRef == null)
+ return false;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef) {
+ if (atom.status == SoundSchedulerAtom.SOUND_AUDIBLE)
+ return true;
+ else
+ return false;
+ }
+ else // atom is not associated with given referenced view
+ continue;
+ }
+ return false; // atom associated with this view not found in list
+ }
+
+ /**
+ * Retrieves sound's playing silently status
+ * true if enabled but not active (on any device)
+ * false otherwise
+ * @return sound playing flag
+ */
+ boolean isPlayingSilently() {
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.status == SoundSchedulerAtom.SOUND_SILENT)
+ return true;
+ else
+ return false;
+ }
+ return false; // atom not found in list or not playing audibilly
+ }
+
+ /**
+ * Retrieves sound's playing silently status for a particular view
+ * true if potentially audible (enabled and active) on audio device
+ * associated with the given view
+ * false otherwise
+ * @param viewRef view to test sound playing silently state for
+ * @return sound playing flag
+ */
+ boolean isPlayingSilently(View viewRef) {
+ if (viewRef == null)
+ return false;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef) {
+ if (atom.status == SoundSchedulerAtom.SOUND_SILENT)
+ return true;
+ else
+ return false;
+ }
+ else // atom is not associated with given referenced view
+ continue;
+ }
+ return false; // atom associated with this view not found in list
+ }
+
+ /**
+ * Retrieves number of channels allocated for this sound on the primary
+ * view's audio device.
+ * @return number of channels used by sound across all devices
+ */
+ int getNumberOfChannelsUsed() {
+ // retrieves the number of channels used by the atom that is:
+ // loaded, and
+ // playing either audibily or silently
+ // on the device associated with the primary view.
+ View primaryView = this.universe.getCurrentView();
+ if (primaryView == null)
+ return 0;
+
+ // find atom associated with primary view (VirtualUniverse currentView)
+ // then return the number of channels associated with that atom
+ SoundSchedulerAtom atom;
+ for (int i=0; i<atomCount; i++) {
+ atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == primaryView) {
+ return atom.numberChannels;
+ }
+ }
+ return 0; // atom associated with primary view not found
+ }
+
+ /**
+ * Retrieves number of channels allocated for this sound on the audio
+ * devices associated with a given view.
+ * @param viewRef view to test sound playing silently state for
+ * @return number of channels used by this sound on a particular device
+ */
+ int getNumberOfChannelsUsed(View viewRef) {
+ // retrieves the number of channels used by the atom that is:
+ // loaded, and
+ // playing either audibily or silently
+ // on the device associated with the given view.
+ if (viewRef == null)
+ return 0;
+ SoundSchedulerAtom atom;
+ for (int i=0; i<atomCount; i++) {
+ atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef) {
+ return atom.numberChannels;
+ }
+ }
+ return 0; // atom associated with primary view not found
+ }
+
+ /**
+ * Set mute state flag. If the sound is playing it will be set to
+ * play silently
+ * @param state flag
+ * @since Java 3D 1.3
+ */
+ void setMute(boolean state) {
+ this.mute = state;
+ dispatchAttribChange(MUTE_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound Mute state.
+ * A return value of true does not imply that the sound has
+ * been started playing or is still playing silently.
+ * @return mute state flag
+ * @since Java 3D 1.3
+ */
+ boolean getMute() {
+ return (boolean) this.mute;
+ }
+
+ /**
+ * Set pause state flag. If the sound is playing it will be paused
+ * @param state flag
+ * @since Java 3D 1.3
+ */
+ void setPause(boolean state) {
+ this.pause = state;
+ dispatchAttribChange(PAUSE_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound Pause state.
+ * A return value of true does not imply that the sound has
+ * been started playing auditibly or silently.
+ * @return mute state flag
+ * @since Java 3D 1.3
+ */
+ boolean getPause() {
+ return (boolean) this.pause;
+ }
+
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+ /**
+ * This gets the mirror sound for this sound given the key.
+ */
+ SoundRetained getMirrorSound(HashKey key) {
+ int i;
+ SoundRetained[] newSounds;
+
+ if (inSharedGroup) {
+ for (i=0; i<numMirrorSounds; i++) {
+ if (mirrorSounds[i].key.equals(key)) {
+ return(mirrorSounds[i]);
+ }
+ }
+ if (numMirrorSounds == mirrorSounds.length) {
+ newSounds = new SoundRetained[numMirrorSounds*2];
+ for (i=0; i<numMirrorSounds; i++) {
+ newSounds[i] = mirrorSounds[i];
+ }
+ mirrorSounds = newSounds;
+ }
+ // mirrorSounds[numMirrorSounds] = (SoundRetained) this.clone();
+ mirrorSounds[numMirrorSounds] = (SoundRetained) this.clone();
+ //mirrorSounds[numMirrorSounds].key = new HashKey(key);
+ mirrorSounds[numMirrorSounds].key = key;
+ mirrorSounds[numMirrorSounds].sgSound = this;
+ return(mirrorSounds[numMirrorSounds++]);
+ } else {
+ if (mirrorSounds[0] == null) {
+ // mirrorSounds[0] = (SoundRetained) this.clone(true);
+ mirrorSounds[0] = (SoundRetained) this.clone();
+ mirrorSounds[0].sgSound = this;
+ }
+ return(mirrorSounds[0]);
+ }
+ }
+
+ synchronized void initMirrorObject(SoundRetained ms) {
+ GroupRetained group;
+ Transform3D trans;
+ Bounds region = null;
+
+ ms.setSchedulingBounds(getSchedulingBounds());
+ ms.setSchedulingBoundingLeaf(getSchedulingBoundingLeaf());
+ ms.sgSound = sgSound;
+/*
+// QUESTION: these are not set in LightRetained???
+ ms.key = null;
+ ms.mirrorSounds = new SoundRetained[1];
+ ms.numMirrorSounds = 0;
+*/
+ ms.inImmCtx = inImmCtx;
+ ms.setSoundData(getSoundData());
+
+// TODO: copy ms.atoms array from this.atoms
+
+ ms.parent = parent;
+ ms.inSharedGroup = false;
+ ms.locale = locale;
+ ms.parent = parent;
+ ms.localBounds = (Bounds)localBounds.clone();
+
+ ms.transformedRegion = null;
+ if (boundingLeaf != null) {
+ if (ms.boundingLeaf != null)
+ ms.boundingLeaf.removeUser(ms);
+ ms.boundingLeaf = boundingLeaf.mirrorBoundingLeaf;
+ // Add this mirror object as user
+ ms.boundingLeaf.addUser(ms);
+ ms.transformedRegion = ms.boundingLeaf.transformedRegion;
+ }
+ else {
+ ms.boundingLeaf = null;
+ }
+
+ if (schedulingRegion != null) {
+ ms.schedulingRegion = (Bounds) schedulingRegion.clone();
+ // Assign region only if bounding leaf is null
+ if (ms.transformedRegion == null) {
+ ms.transformedRegion = (Bounds) ms.schedulingRegion.clone();
+ ms.transformedRegion.transform(ms.schedulingRegion,
+ ms.getLastLocalToVworld());
+ }
+
+ }
+ else {
+ ms.schedulingRegion = null;
+ }
+ }
+
+ void setLive(SetLiveState s) {
+ SoundRetained ms;
+ int i, j;
+
+ if (debugFlag)
+ debugPrint("Sound.setLive");
+
+ if (inImmCtx) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("SoundRetained2"));
+ }
+ super.setLive(s);
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("SoundRetained3"));
+ }
+
+ if (this.loadStatus == LOAD_PENDING) {
+ if (debugFlag)
+ debugPrint("Sound.setLive load Sound");
+ dispatchStateChange(LOAD_SOUND_DIRTY_BIT, soundData);
+ }
+
+ if (this.soundData != null) {
+ ((MediaContainerRetained)this.soundData.retained).setLive(inBackgroundGroup, s.refCount);
+ }
+
+ if (s.inSharedGroup) {
+ for (i=0; i<s.keys.length; i++) {
+ ms = this.getMirrorSound(s.keys[i]);
+ ms.localToVworld = new Transform3D[1][];
+ ms.localToVworldIndex = new int[1][];
+
+ j = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(j < 0) {
+ System.out.println("SoundRetained : Can't find hashKey");
+ }
+
+ ms.localToVworld[0] = localToVworld[j];
+ ms.localToVworldIndex[0] = localToVworldIndex[j];
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(i));
+ } else {
+ s.nodeList.add(ms);
+ }
+ // Initialization of the mirror object during the INSERT_NODE
+ // message (in updateMirrorObject)
+ if (s.switchTargets != null && s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(ms, Targets.SND_TARGETS);
+ }
+ ms.switchState = (SwitchState)s.switchStates.get(j);
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ }
+ } else {
+ ms = this.getMirrorSound(null);
+ ms.localToVworld = new Transform3D[1][];
+ ms.localToVworldIndex = new int[1][];
+ ms.localToVworld[0] = this.localToVworld[0];
+ ms.localToVworldIndex[0] = this.localToVworldIndex[0];
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(ms);
+ }
+ // Initialization of the mirror object during the INSERT_NODE
+ // message (in updateMirrorObject)
+ if (s.switchTargets != null && s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(ms, Targets.SND_TARGETS);
+ }
+ ms.switchState = (SwitchState)s.switchStates.get(0);
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ }
+ dispatchStateChange(LIVE_DIRTY_BIT, soundData);
+ s.notifyThreads |= targetThreads;
+ }
+
+ void clearLive(SetLiveState s) {
+ SoundRetained ms;
+
+ super.clearLive(s);
+
+// TODO: if (inSharedGroup)
+
+ if (s.inSharedGroup) {
+ for (int i=0; i<s.keys.length; i++) {
+ ms = this.getMirrorSound(s.keys[i]);
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(ms, Targets.SND_TARGETS);
+ }
+ if (s.transformTargets != null && s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(i));
+ } else {
+ s.nodeList.add(ms);
+ }
+ }
+ } else {
+ ms = this.getMirrorSound(null);
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(ms, Targets.SND_TARGETS);
+ }
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(ms);
+ }
+ }
+ s.notifyThreads |= targetThreads;
+
+ if (this.soundData != null) {
+ ((MediaContainerRetained)this.soundData.retained).clearLive(s.refCount);
+ }
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (schedulingRegion != null) {
+ schedulingRegion.transform(xform.transform);
+ }
+ }
+
+/*
+ // This makes passed in sound look just like this sound
+// QUESTION: DOesn't appread to be called
+// TODO: ...if so, remove...
+ synchronized void update(SoundRetained sound) {
+ if (debugFlag)
+ debugPrint("Sound.update ******** entered ***** this = " + this +
+ ", and sound param = " + sound);
+
+ sound.soundData = soundData;
+ sound.initialGain = initialGain;
+ sound.loopCount = loopCount;
+ sound.release = release;
+ sound.continuous = continuous;
+ sound.enable = enable; // used to be 'on'
+ sound.inImmCtx = inImmCtx;
+
+// QUESTION:
+// This line removed from 1.1.1 version; why ???
+ sound.currentSwitchOn = currentSwitchOn;
+
+// NEW:
+ sound.priority = priority;
+
+// QUESTION: With code below, no sound schedulingRegion found
+// sound.schedulingRegion = schedulingRegion;
+// sound.boundingLeaf = boundingLeaf;
+// TODO: clone of region used in Traverse code, why not here???
+// if (schedulingRegion != null)
+// sound.schedulingRegion = (Bounds)schedulingRegion.clone();
+// TODO: BoundingLeafRetained boundingLeaf ...
+// WHAT ABOUT transformedRegion??
+
+// TODO: Update ALL fields
+// ALL THE BELOW USED TO COMMENTED OUT vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ sound.sampleLength = sampleLength;
+ sound.loopStartOffset = loopStartOffset;
+ sound.loopLength = loopLength;
+ sound.attackLength = attackLength;
+ sound.releaseLength = releaseLength;
+
+ sound.sgSound = sgSound;
+ sound.key = key;
+ sound.numMirrorSounds = numMirrorSounds;
+ for (int index=0; index<numMirrorSounds; index++)
+ sound.mirrorSounds = mirrorSounds;
+ sound.universe = universe;
+ if (universe.sounds.contains(sound) == false) {
+ universe.sounds.addElement(sound);
+ }
+ if (debugFlag)
+ debugPrint("update****************************** exited");
+^^^^^^^^^^^ COMMENTED OUT
+ }
+*/
+
+
+ // Called on mirror object
+// QUESTION: doesn't transformed region need to be saved???
+ void updateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (debugFlag)
+ debugPrint("SoundRetained.updateTransformChange()");
+ if (boundingLeaf == null) {
+ if (schedulingRegion != null) {
+ transformedRegion = schedulingRegion.copy(transformedRegion);
+ transformedRegion.transform(schedulingRegion,
+ getLastLocalToVworld());
+ }
+ }
+ dispatchStateChange(XFORM_DIRTY_BIT, null);
+ }
+
+// QUESTION:
+// Clone method (from 1.1.1 version) removed!?!?!? yet LightRetained has it
+
+
+
+ // Debug print mechanism for Sound nodes
+ static final boolean debugFlag = false;
+ static final boolean internalErrors = false;
+
+ void debugPrint(String message) {
+ if (debugFlag) {
+ System.out.println(message);
+ }
+ }
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ if (key == null) {
+ leafList.add(mirrorSounds[0]);
+ }
+ else {
+ for (int i=0; i<numMirrorSounds; i++) {
+ if (mirrorSounds[i].key.equals(key)) {
+ leafList.add(mirrorSounds[i]);
+ break;
+ }
+ }
+
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SoundScheduler.java b/src/classes/share/javax/media/j3d/SoundScheduler.java
new file mode 100644
index 0000000..ba6cf25
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SoundScheduler.java
@@ -0,0 +1,3241 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+import java.util.Vector;
+import java.util.ArrayList;
+import java.net.URL;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Arrays;
+
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * This structure parallels the RenderBin structure and
+ * is used for sounds
+ */
+class SoundScheduler extends J3dStructure {
+
+ /**
+ * The View that owns this SoundScheduler
+ */
+ View view = null;
+
+ /**
+ * This boolean tells the thread to suspend itself.
+ * This is true ONLY when everythings ready to render using run loop
+ */
+ boolean ready = false;
+
+ /**
+ * The ViewPlatform that is associated with this SoundScheduler
+ */
+ ViewPlatformRetained viewPlatform = null;
+
+ /**
+ * The GraphicContext3D that we are currently unning in.
+ */
+ GraphicsContext3D graphicsCtx = null;
+
+ /**
+ * Maintain what reference to the last AuralAttributes found active
+ * was so that only if there was a change do we need to reset these
+ * parameters in the AudioDevice3D.
+ */
+ AuralAttributesRetained lastAA = null;
+
+ /**
+ * Since AuralAttribute gain scale factor is multipled with sound's
+ * initialGain scale factor, any change in AuralAttrib gain scale
+ * factor should force an update of all active sounds' gains
+ * Also, change in AuralAttributes should force a sound update
+ * even if no other sound field changes occurred.
+ */
+ boolean resetAA = true;
+
+ /**
+ * Audio Device
+ */
+ AudioDevice audioDevice = null;
+ AudioDevice3D audioDevice3D = null;
+ AudioDevice3DL2 audioDevice3DL2 = null;
+ int totalChannels = 0;
+
+ /**
+ * Array of SoundScapeRetained nodes that intersect the viewPlatform
+ * This list is a subset of the soundscapes array, and is used when
+ * selecting the closest Soundscape.
+ * Maintained as an expandable array.
+ */
+ SoundscapeRetained[] intersectedSoundscapes = new SoundscapeRetained[32];
+
+ /**
+ * Array of Bounds nodes for the corresponding intersectedSoundscapes
+ * array. This array is used when selecting the closest Soundscape.
+ * This list is used when selecting the closest Soundscape.
+ * Maintained as an expandable array.
+ */
+ Bounds[] intersectedRegions = new Bounds[32];
+
+ /**
+ * Reference to last processed region within run().
+ * Maintained to avoid re-transforming this bounds.
+ */
+ Bounds region = null;
+
+ /**
+ * An array of prioritized sounds currently playing "live" sounds.
+ * This prioritized sound list is NO longer re-create instead sounds
+ * are insert, shuffled or removed as messages are processed.
+ */
+ // TODO: (Enhancement) should have a seperate list for
+ // background sound and a list for positional sounds
+ ArrayList prioritizedSounds = new ArrayList();
+
+ /**
+ * Current number of scene graph sound nodes in the universe
+ */
+ int nRetainedSounds = -1; // none calculated yet
+
+ /**
+ * Current number of immediate mode sound nodes in the universe
+ */
+ int nImmedSounds = -1; // none calculated yet
+
+ /**
+ * Current active (selected) attribute node in the sceneGraph
+ */
+ AuralAttributesRetained aaRetained = null;
+
+ // variables for processing transform messages
+ boolean transformMsg = false;
+ UpdateTargets targets = null;
+
+
+ /**
+ * Current active (selected) attribute node in the sceneGraph
+ */
+ AuralAttributesRetained aaImmed = null;
+
+ // Dirty flags for fields and parameters that are unique to the
+ // Sound Scheduler or the Audio Device
+ // Any listener (body) and/or view transform changes processed in
+ // CanvasViewCache force one of these flags to be set.
+ static final int EAR_POSITIONS_CHANGED = 0x0001;
+ static final int EYE_POSITIONS_CHANGED = 0x0002;
+ static final int IMAGE_PLATE_TO_VWORLD_CHANGED = 0x0004;
+ static final int HEAD_TO_VWORLD_CHANGED = 0x0008;
+ static final int LISTENER_CHANGED = 0x000F;// all of the above
+ private int listenerUpdated = LISTENER_CHANGED;
+
+ /**
+ * Temporary flag that's denotes that a positional sound was processed
+ * in the current loop of renderChange().
+ */
+ private boolean positionalSoundUpdated = false;
+
+ /**
+ * Temporary flag that's denotes that some field auralAttribute was changed
+ */
+ private boolean auralAttribsChanged = true; // force processing 1st x
+
+ private boolean stallThread = false;
+
+ int lastEventReceived = WindowEvent.WINDOW_CLOSED;
+
+ /**
+ * Constructs a new SoundScheduler
+ */
+ SoundScheduler(VirtualUniverse u, View v) {
+ super(u, J3dThread.SOUND_SCHEDULER);
+ // vworldToVpc.setIdentity();
+ universe = u;
+ view = v;
+ reset(v);
+ }
+
+
+ // NOTE: processMessage only called with updatethread.active true
+ void processMessages(long referenceTime) {
+ J3dMessage[] messages = getMessages(referenceTime);
+ int nMsg = getNumMessage();
+ J3dMessage m;
+ int nSounds;
+
+ if (nMsg > 0) {
+ for (int i=0; i < nMsg; i++) {
+ m = messages[i];
+
+ switch (m.type) {
+ case J3dMessage.INSERT_NODES:
+ insertNodes(m);
+ break;
+ case J3dMessage.REMOVE_NODES:
+ removeNodes(m);
+ break;
+ case J3dMessage.SOUND_ATTRIB_CHANGED:
+ changeNodeAttrib(m);
+ break;
+ case J3dMessage.SOUND_STATE_CHANGED:
+ changeNodeState(m);
+ break;
+ case J3dMessage.BOUNDINGLEAF_CHANGED:
+ processBoundingLeafChanged(m);
+ break;
+ case J3dMessage.SOUNDSCAPE_CHANGED:
+ SoundscapeRetained ss = (SoundscapeRetained)m.args[0];
+ if (universe.soundStructure.isSoundscapeScopedToView(ss, view)) {
+ auralAttribsChanged = true;
+ changeNodeAttrib(m);
+ }
+ break;
+ case J3dMessage.AURALATTRIBUTES_CHANGED:
+ auralAttribsChanged = true;
+ changeNodeAttrib(m);
+ break;
+ case J3dMessage.MEDIA_CONTAINER_CHANGED:
+ changeNodeAttrib(m);
+ break;
+ case J3dMessage.TRANSFORM_CHANGED:
+ transformMsg = true;
+ auralAttribsChanged = true;
+ break;
+ case J3dMessage.RENDER_IMMEDIATE:
+ processImmediateNodes(m.args, referenceTime);
+ break;
+ case J3dMessage.VIEWSPECIFICGROUP_CHANGED:
+ processViewSpecificGroupChanged(m);
+ break;
+ case J3dMessage.UPDATE_VIEW:
+ if (debugFlag)
+ debugPrint(".processMessage() UPDATE_VIEW");
+ // NOTE: can only rely on seeing UPDATE_VIEW when canvas [re]Created
+ // AND when view deactivated...
+ // NOTE:
+ // temp work-around
+ // calling prioritizeSounds() wipes out old atom fields
+ // QUESTION: prioritizedSound is NEVER empty - why if size is 0 can
+ // .isEmpty return anything but TRUE???
+ //
+ if (prioritizedSounds.isEmpty()) {
+ nSounds = prioritizeSounds();
+ }
+ break;
+ case J3dMessage.SWITCH_CHANGED:
+ if (debugFlag)
+ debugPrint(".processMessage() " +
+ "SWITCH_CHANGED ignored");
+ break;
+ } // switch
+ m.decRefcount();
+ } // for
+ if (transformMsg) {
+ targets = universe.transformStructure.getTargetList();
+ updateTransformChange(targets, referenceTime);
+ transformMsg = false;
+ targets = null;
+ }
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ // Call renderChanges within try/catch so errors won't kill
+ // the SoundScheduler.
+ try {
+ renderChanges();
+ }
+ catch (RuntimeException e) {
+ System.err.println("Exception occurred " +
+ "during Sound rendering:");
+ e.printStackTrace();
+ }
+
+ // what if the user/app makes no change to scenegraph?
+ // must still re-render after retest for sound complete
+ // calculate which sound will finished first and set a
+ // wait time to this shortest time so that scheduler is
+ // re-entered to process sound complete.
+
+ long waitTime = shortestTimeToFinish();
+
+ if (waitTime == 0L) {
+ // come right back
+ if (debugFlag)
+ debugPrint(".processMessage calls sendRunMessage " +
+ "for immediate processing");
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.SOUND_SCHEDULER);
+ }
+ else if (waitTime > 0L) {
+ // Use TimerThread to send message with sounds complete.
+ // This uses waitForElapse time to sleep for at least the duration
+ // returned by shortestTimeToFinish method.
+ if (debugFlag)
+ debugPrint(".processMessage calls sendRunMessage " +
+ "with wait time = " + waitTime );
+ // QUESTION (ISSUE): even when this is set to a large time
+ // processMessage is reentered immediately.
+ // Why is timer thread not waiting??
+ VirtualUniverse.mc.sendRunMessage(waitTime, view,
+ J3dThread.SOUND_SCHEDULER);
+ }
+ }
+
+ void insertNodes(J3dMessage m) {
+ Object[] nodes = (Object[])m.args[0];
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ Object node;
+
+ for (int i=0; i<nodes.length; i++) {
+ node = (Object) nodes[i];
+ if (node instanceof SoundRetained) {
+ nRetainedSounds++;
+ // insert sound node into sound scheduler's prioritized list
+ addSound((SoundRetained) node);
+ }
+ else if (node instanceof SoundscapeRetained) {
+ auralAttribsChanged = true;
+ }
+ else if (node instanceof AuralAttributesRetained) {
+ auralAttribsChanged = true;
+ }
+ else if (node instanceof ViewPlatformRetained) {
+ // TODO: don't support multiple viewPlatforms per scheduler
+ /*
+ // useful for resetting VP ??
+ addViewPlatform((ViewPlatformRetained) node);
+ */
+ if (debugFlag) {
+ debugPrint(".insertNodes() viewPlatformRetained not supported yet");
+ }
+ }
+ }
+
+ // Handle ViewScoped Nodes
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+ for (int i = 0; i < size; i++) {
+ node = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ // If the node object is scoped to this view, then ..
+ if (vl.contains(view)) {
+ if (node instanceof SoundRetained) {
+ nRetainedSounds++;
+ // insert sound node into sound scheduler's prioritized list
+ addSound((SoundRetained) node);
+ }
+ else if (node instanceof SoundscapeRetained) {
+ auralAttribsChanged = true;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add sound to sounds list.
+ */
+ void addSound(SoundRetained sound) {
+ if (sound == null)
+ return;
+ if (debugFlag)
+ debugPrint(".addSound()");
+ synchronized (prioritizedSounds) {
+ addPrioritizedSound(sound);
+ }
+ } // end addSound
+
+
+ /**
+ * Node removed from tree
+ */
+ void removeNodes(J3dMessage m) {
+ Object[] nodes = (Object[])m.args[0];
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ Object node;
+
+ for (int i=0; i<nodes.length; i++) {
+ node = (Object) nodes[i];
+ if (node instanceof SoundRetained) {
+ // sound is deactivated but NOT deleted
+ // incase sound is reattached
+
+// QUESTION: what's the difference in messages between really deleting
+// a node and just deactivating it.
+/*
+//
+// can't delete atom in case it's reactivitated
+//
+ nRetainedSounds--;
+ deleteSound((SoundRetained) node);
+//
+// until there's a difference in messages between detaching and deleting
+// a sound node, sound is stopped and atom enable state changed but atom
+// is NOT deleted from list.
+*/
+ SoundSchedulerAtom soundAtom = null;
+ for (int arrIndx=1; ;arrIndx++) {
+ soundAtom = findSoundAtom((SoundRetained)node,
+ arrIndx);
+ if (soundAtom == null)
+ break;
+ stopSound(soundAtom, false);
+ }
+ }
+ else if (node instanceof SoundscapeRetained) {
+ auralAttribsChanged = true;
+ }
+ }
+ // Handle ViewScoped Nodes
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+ for (int i = 0; i < size; i++) {
+ node = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ // If the node object is scoped to this view, then ..
+ if (vl.contains(view)) {
+ if (node instanceof SoundRetained) {
+ SoundSchedulerAtom soundAtom = null;
+ for (int arrIndx=1; ;arrIndx++) {
+ soundAtom = findSoundAtom((SoundRetained)node,
+ arrIndx);
+ if (soundAtom == null)
+ break;
+ stopSound(soundAtom, false);
+ }
+ }
+ else if (node instanceof SoundscapeRetained) {
+ auralAttribsChanged = true;
+ }
+ }
+ }
+ }
+
+ }
+
+
+ // deletes all instances of the sound nodes from the priority list
+ void deleteSound(SoundRetained sound) {
+ if (sound != null)
+ return;
+ if (debugFlag)
+ debugPrint(".deleteSound()");
+ synchronized (prioritizedSounds) {
+ if (!prioritizedSounds.isEmpty()) {
+ // find sound in list and remove it
+ int arrSize = prioritizedSounds.size();
+ for (int index=0; index<arrSize; index++) {
+ SoundSchedulerAtom soundAtom = (SoundSchedulerAtom)
+ prioritizedSounds.get(index);
+ // QUESTION: which???
+ if (soundAtom.sound == sound ||
+ soundAtom.sound.sgSound == sound) {
+ stopSound(soundAtom, false);
+ prioritizedSounds.remove(index);
+ }
+ }
+ }
+ }
+ }
+
+
+ void changeNodeAttrib(J3dMessage m) {
+ Object node = m.args[0];
+ Object value = m.args[1];
+ int attribDirty = ((Integer)value).intValue();
+ if (debugFlag)
+ debugPrint(".changeNodeAttrib:");
+
+ if (node instanceof SoundRetained &&
+ universe.soundStructure.isSoundScopedToView(node, view)) {
+
+ this.setAttribsDirtyFlag((SoundRetained)node, attribDirty);
+ if (debugFlag)
+ debugPrint(" Sound node dirty bit = " + attribDirty);
+ if ((attribDirty & SoundRetained.PRIORITY_DIRTY_BIT) > 0) {
+ shuffleSound((SoundRetained) node);
+ }
+ if ((attribDirty & SoundRetained.SOUND_DATA_DIRTY_BIT) >0) {
+ if (debugFlag)
+ debugPrint(".changeNodeAttrib " +
+ "SOUND_DATA_DIRTY_BIT calls loadSound");
+ loadSound((SoundRetained) node, true);
+ }
+ if ((attribDirty & SoundRetained.MUTE_DIRTY_BIT) > 0) {
+ if (debugFlag)
+ debugPrint(" MuteDirtyBit is on");
+ muteSound((SoundRetained) node);
+ }
+ if ((attribDirty & SoundRetained.PAUSE_DIRTY_BIT) > 0) {
+ if (debugFlag)
+ debugPrint(" PauseDirtyBit is on");
+ pauseSound((SoundRetained) node);
+ }
+ }
+ else if (node instanceof SoundscapeRetained &&
+ universe.soundStructure.isSoundscapeScopedToView(node, view)) {
+ auralAttribsChanged = true;
+ }
+ else if (node instanceof AuralAttributesRetained) {
+ auralAttribsChanged = true;
+ }
+ else if (node instanceof MediaContainerRetained) {
+ int listSize = ((Integer)m.args[2]).intValue();
+ ArrayList userList = (ArrayList)m.args[3];
+ for (int i = 0; i < listSize; i++) {
+ SoundRetained sound = (SoundRetained)userList.get(i);
+ if (sound != null) {
+ loadSound(sound, true);
+ if (debugFlag)
+ debugPrint(".changeNodeAttrib " +
+ "MEDIA_CONTAINER_CHANGE calls loadSound");
+ }
+ }
+ }
+ }
+
+
+ void changeNodeState(J3dMessage m) {
+ Object node = m.args[0];
+ Object value = m.args[1];
+ if (debugFlag)
+ debugPrint(".changeNodeState:");
+ if (node instanceof SoundRetained && universe.soundStructure.isSoundScopedToView(node, view)) {
+ int stateDirty = ((Integer)value).intValue();
+ setStateDirtyFlag((SoundRetained)node, stateDirty);
+ if (debugFlag)
+ debugPrint(" Sound node dirty bit = "+stateDirty);
+ if ((stateDirty & SoundRetained.LIVE_DIRTY_BIT) > 0) {
+ if (debugFlag)
+ debugPrint(".changeNodeState LIVE_DIRTY_BIT " +
+ "calls loadSound");
+ loadSound((SoundRetained) node, false);
+ }
+ if ((stateDirty & SoundRetained.ENABLE_DIRTY_BIT) > 0) {
+ if (debugFlag)
+ debugPrint(" EnableDirtyBit is on");
+ if (((Boolean) m.args[4]).booleanValue()) {
+ enableSound((SoundRetained) node);
+ } else {
+ SoundSchedulerAtom soundAtom;
+ SoundRetained soundRetained = (SoundRetained) node;
+ for (int i=prioritizedSounds.size()-1; i >=0; i--) {
+ soundAtom = ((SoundSchedulerAtom)prioritizedSounds.get(i));
+ if (soundAtom.sound.sgSound == soundRetained) {
+ // ignore soundRetained.release
+ // flag which is not implement
+ turnOff(soundAtom);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void shuffleSound(SoundRetained sound) {
+ // Find sound atom that references this sound node and
+ // reinsert it into prioritized sound list by removing atom for
+ // this sound from priority list, then re-add it.
+ // Assumes priority has really changed since a message is not sent
+ // to the scheduler if the 'new' priority value isn't different.
+ deleteSound(sound); // remove atom for this sound
+ addSound(sound); // then re-insert it back into list in new position
+ }
+
+
+ void loadSound(SoundRetained sound, boolean forceReload) {
+ // find sound atom that references this sound node
+ // QUESTION: "node" probably not mirror node?
+ SoundSchedulerAtom soundAtom = null;
+ for (int i=1; ;i++) {
+ soundAtom = findSoundAtom(sound, i);
+ if (soundAtom == null)
+ break;
+ MediaContainer mediaContainer = sound.getSoundData();
+ if (forceReload ||
+ soundAtom.loadStatus != SoundRetained.LOAD_COMPLETE) {
+ if (debugFlag)
+ debugPrint(": not LOAD_COMPLETE - try attaching");
+ attachSoundData(soundAtom, mediaContainer, forceReload);
+ }
+ }
+ }
+
+
+ void enableSound(SoundRetained sound) {
+ if (debugFlag)
+ debugPrint(".enableSound " + sound );
+ // find sound atom that references this sound node
+ SoundSchedulerAtom soundAtom = null;
+ for (int i=1; ;i++) {
+ soundAtom = findSoundAtom(sound, i);
+ if (soundAtom == null)
+ break;
+ // Set atom enabled field based on current Sound node
+ // enable boolean flag
+ soundAtom.enable(sound.enable);
+ }
+ }
+
+
+ void muteSound(SoundRetained sound) {
+ // make mute pending
+ // mute -> MAKE-SILENT
+ // unmute -> MAKE-AUDIBLE
+ if (debugFlag)
+ debugPrint(".muteSound " + sound );
+ // find sound atom that references this sound node
+ SoundSchedulerAtom soundAtom = null;
+ for (int i=1; ;i++) {
+ soundAtom = findSoundAtom(sound, i);
+ if (soundAtom == null)
+ break;
+ // Set atom mute field based on node current
+ // mute boolean flag
+ soundAtom.mute(sound.mute);
+ }
+ }
+
+ void pauseSound(SoundRetained sound) {
+ // make pause pending
+ // Pause is a separate action
+ // When resumed it has to reset its state
+ // PAUSE_AUDIBLE
+ // PAUSE_SILENT
+ // RESUME_AUDIBLE
+ // RESUME_SILENT
+ // to whatever it was before
+ if (debugFlag)
+ debugPrint(".pauseSound " + sound );
+ // find sound atom that references this sound node
+ SoundSchedulerAtom soundAtom = null;
+ for (int i=1; ;i++) {
+ soundAtom = findSoundAtom(sound, i);
+ if (soundAtom == null)
+ break;
+ // Set atom pause field based on node's current
+ // pause boolean flag
+ soundAtom.pause(sound.pause);
+ }
+ }
+
+ void processImmediateNodes(Object[] args, long referenceTime) {
+ Object command = args[0];
+ Object newNode = args[1];
+ Object oldNode = args[2];
+ Sound oldSound = (Sound)oldNode;
+ Sound newSound = (Sound)newNode;
+ int action = ((Integer)command).intValue();
+ if (debugFlag)
+ debugPrint(".processImmediateNodes() - action = " +
+ action);
+ switch (action) {
+ case GraphicsContext3D.ADD_SOUND :
+ case GraphicsContext3D.INSERT_SOUND :
+ addSound((SoundRetained)newSound.retained);
+ nImmedSounds++;
+ break;
+ case GraphicsContext3D.REMOVE_SOUND :
+ deleteSound((SoundRetained)oldSound.retained);
+ nImmedSounds--;
+ break;
+ case GraphicsContext3D.SET_SOUND :
+ deleteSound((SoundRetained)oldSound.retained);
+ addSound((SoundRetained)newSound.retained);
+ break;
+ }
+ }
+
+
+ void updateTransformChange(UpdateTargets targets, long referenceTime) {
+ // node.updateTransformChange() called immediately rather than
+ // waiting for updateObject to be called and process xformChangeList
+ // which apprears to only happen when sound started...
+
+ UnorderList arrList = targets.targetList[Targets.SND_TARGETS];
+ if (arrList != null) {
+ int j,i;
+ Object nodes[], nodesArr[];
+ int size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (j = 0; j<size; j++) {
+ nodes = (Object[])nodesArr[j];
+
+
+ for (i = 0; i < nodes.length; i++) {
+ if (nodes[i] instanceof ConeSoundRetained && universe.soundStructure.isSoundScopedToView(nodes[i], view)) {
+ ConeSoundRetained cnSndNode =
+ (ConeSoundRetained)nodes[i];
+ synchronized (cnSndNode) {
+ cnSndNode.updateTransformChange();
+ }
+ // set XFORM_DIRTY_BIT in corresponding atom
+ setStateDirtyFlag((SoundRetained)nodes[i],
+ SoundRetained.XFORM_DIRTY_BIT);
+ } else if (nodes[i] instanceof PointSoundRetained && universe.soundStructure.isSoundScopedToView(nodes[i], view)) {
+ PointSoundRetained ptSndNode =
+ (PointSoundRetained)nodes[i];
+ synchronized (ptSndNode) {
+ ptSndNode.updateTransformChange();
+ }
+ // set XFORM_DIRTY_BIT in corresponding atom
+ setStateDirtyFlag((SoundRetained)nodes[i],
+ SoundRetained.XFORM_DIRTY_BIT);
+ } else if (nodes[i] instanceof SoundscapeRetained && universe.soundStructure.isSoundscapeScopedToView(nodes[i], view)) {
+ SoundscapeRetained sndScapeNode =
+ (SoundscapeRetained)nodes[i];
+ synchronized (sndScapeNode) {
+ sndScapeNode.updateTransformChange();
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ void updateTransformedFields(SoundRetained mirSound) {
+ if (mirSound instanceof ConeSoundRetained && universe.soundStructure.isSoundScopedToView(mirSound, view)) {
+ ConeSoundRetained cnSndNode = (ConeSoundRetained)mirSound;
+ synchronized (cnSndNode) {
+ cnSndNode.updateTransformChange();
+ }
+ }
+ else if (mirSound instanceof PointSoundRetained && universe.soundStructure.isSoundScopedToView(mirSound, view)) {
+ PointSoundRetained ptSndNode = (PointSoundRetained)mirSound;
+ synchronized (ptSndNode) {
+ ptSndNode.updateTransformChange();
+ }
+ }
+ }
+
+
+ void activate() {
+ updateThread.active = true;
+ if (debugFlag)
+ debugPrint(".activate(): calls sendRunMessage for immediate processing");
+ VirtualUniverse.mc.sendRunMessage(universe,
+ J3dThread.SOUND_SCHEDULER);
+ // renderChanges() called indirectly thru processMessage now
+ }
+
+
+ // deactivate scheduler only if it state has such that it can not perform
+ // sound processing
+ void deactivate() {
+ if (debugFlag)
+ debugPrint(".deactivate()");
+ //
+
+ // TODO: The following code is clearly erroneous.
+ // The indendation, along with the 2nd test of
+ // "if (debugFlag)" in the else clause, suggests that
+ // the intent was to make the else clause apply to
+ // "if (checkState())". However, the else clause
+ // actually applies to the first "if (debugFlag)".
+ // This is a textbook example of why one should
+ // *ALWAYS* enclose the target of an "if", "while", or
+ // "else" in curly braces -- even when the target
+ // consists of a single statement.
+ //
+ // The upshot of this, is that the else clause is
+ // unconditionally executed, since "debugFlag" is a
+ // static final constant that is set to false for
+ // production builds. We won't fix it now, because
+ // The SoundScheduler may actually be relying on the
+ // fact that all sounds are unconditionally
+ // deactivated when this method is called, and we
+ // don't want to introduce a new bug.
+ //
+ if (checkState())
+ if (debugFlag)
+ debugPrint(" checkState returns true");
+ else {
+ if (debugFlag)
+ debugPrint(" checkState returns false; deactive scheduler");
+ // Call deactivateAllSounds within try/catch so
+ // errors won't kill the SoundScheduler.
+ try {
+ deactivateAllSounds();
+ }
+ catch (RuntimeException e) {
+ System.err.println("Exception occurred " +
+ "during sound deactivation:");
+ e.printStackTrace();
+ }
+ updateThread.active = false;
+ }
+ }
+
+
+ // Check the ready state and return true if ready
+ boolean checkState() {
+ boolean runState = false;
+ if (stallThread) {
+ if (debugFlag)
+ debugPrint(" checkState stallThread true");
+ runState = false;
+ }
+
+ if (ready) {
+ if (debugFlag)
+ debugPrint(" checkState ready to run");
+ runState = true;
+ }
+ else {
+ // previous not ready, force reset call to see if everything
+ // ready now or not
+ reset(view);
+ if (ready) {
+ if (debugFlag)
+ debugPrint(" checkState Now ready to run");
+ runState = true;
+ }
+ else {
+ if (debugFlag) {
+ debugPrint(" checkState NOT ready to run");
+ }
+ runState = false;
+ }
+ }
+ return runState;
+ }
+
+ synchronized void reset(View v) {
+ int fieldsNotSet = 0;
+ if (v == null) {
+ if (debugFlag)
+ debugPrint(".reset() called with null view");
+ view = null;
+ viewPlatform = null;
+ universe = null;
+ // clearly not ready to run
+ // QUESTION: doesn't this leave the possibility that
+ // the SoundScheduler may never be woken up???
+ ready = false;
+ return;
+ }
+ else {
+ if (debugFlag)
+ debugPrint(".reset() called with view = " + v);
+ view = v;
+ // TODO: Does not support multiple canvases per view, thus
+ // multiple GraphicsContext3Ds
+ // QUESTION: what does that mean for sound -
+ // being applied to only ONE graphics context?
+ // GET FIRST Canvas
+ Canvas3D canvas = view.getFirstCanvas();
+ if (canvas != null) {
+ graphicsCtx = canvas.getGraphicsContext3D();
+ }
+ }
+
+ // Set AudioDevice if possible
+ if (v.physicalEnvironment == null) {
+ if (debugFlag)
+ debugPrint(".reset: physicalEnvironment null");
+ audioDevice = null;
+ fieldsNotSet++;
+ }
+ else if (v.physicalEnvironment.audioDevice == null) {
+ if (audioDevice == null) {
+ if (debugFlag)
+ debugPrint(".reset: audioDevice null");
+ fieldsNotSet++;
+ }
+ // otherwise audioDevice set before so leave it unchanged
+ }
+ else {
+ audioDevice = v.physicalEnvironment.audioDevice;
+ }
+
+ // Get viewPlatform and Universe
+ // If any of these are null at anytime, we can't render
+ ViewPlatform vp = v.getViewPlatform();
+
+ if (vp == null || vp.retained == null) {
+ if (debugFlag)
+ debugPrint(".reset: viewPlatform null");
+ viewPlatform = null;
+ fieldsNotSet++;
+ }
+ else {
+ viewPlatform = (ViewPlatformRetained)vp.retained;
+ if (viewPlatform.universe == null) {
+ if (debugFlag)
+ debugPrint(".reset: vP.retained.univ null");
+ universe = null;
+ fieldsNotSet++;
+ }
+ else {
+ // If we've reached this block then viewPlatform and univ
+ // as set below will NOT be null.
+ universe = viewPlatform.universe;
+ }
+ }
+ if (fieldsNotSet > 0 || audioDevice == null) {
+ // still not ready to run
+ ready = false;
+ return;
+ }
+ // now the render loop can be run successfully
+ audioDevice3DL2 = null;
+ audioDevice3D = null;
+ if (audioDevice instanceof AudioDevice3DL2) {
+ audioDevice3DL2 = (AudioDevice3DL2)audioDevice;
+ }
+ if (audioDevice instanceof AudioDevice3D) {
+ audioDevice3D = (AudioDevice3D)audioDevice;
+ if (debugFlag)
+ debugPrint(".reset: audioDevice3D.setView");
+ audioDevice3D.setView(view);
+ totalChannels = audioDevice.getTotalChannels();
+ if (debugFlag)
+ debugPrint(" audioDevice3D.getTotalChannels returned " +
+ totalChannels);
+ }
+ else {
+ if (internalErrors)
+ debugPrint(": AudioDevice implementation not supported");
+ totalChannels = 0;
+ }
+
+ if (totalChannels == 0) {
+ ready = false;
+ return;
+ }
+
+ ready = true;
+ // since audio device is ready; set enable flag for continuous
+ // calculating userHead-to-VirtualWorld transform
+ view.setUserHeadToVworldEnable(true);
+ return;
+ }
+
+
+ void receiveAWTEvent(AWTEvent evt) {
+ int eventId = evt.getID();
+ if (debugFlag)
+ debugPrint(".receiveAWTEvent " + eventId);
+ if (ready && eventId == WindowEvent.WINDOW_ICONIFIED) {
+ lastEventReceived = eventId;
+ }
+ else if (ready &&
+ (lastEventReceived == WindowEvent.WINDOW_ICONIFIED &&
+ eventId == WindowEvent.WINDOW_DEICONIFIED) ) {
+ lastEventReceived = eventId;
+ // used to notify
+ }
+ }
+
+
+ /**
+ * The main loop for the Sound Scheduler.
+ */
+ void renderChanges() {
+ int nSounds = 0;
+ int totalChannelsUsed = 0;
+ int nPrioritizedSound = 0;
+ int numActiveSounds = 0;
+ if (debugFlag)
+ debugPrint(" renderChanges begun");
+ // TODO: BUG?? should wait if audioDevice is NULL or nSounds = 0
+ // when a new sound is added or deleted from list, or
+ // when the audioDevice is set into PhysicalEnvironment
+
+ if (!checkState()) {
+ if (debugFlag)
+ debugPrint(".workToDo() checkState failed");
+ return;
+ }
+
+ /*
+ synchronized (prioritizedSounds) {
+ */
+ nPrioritizedSound = prioritizedSounds.size();
+ if (debugFlag)
+ debugPrint(" nPrioritizedSound = " + nPrioritizedSound);
+ if (nPrioritizedSound == 0)
+ return;
+
+ if (auralAttribsChanged) {
+ // Find closest active aural attributes in scene graph
+ int nIntersected = findActiveSoundscapes();
+ if (nIntersected > 0) {
+ if (debugFlag)
+ debugPrint(" "+ nIntersected +
+ " active SoundScapes found");
+ // TODO: (Performance) calling clone everytime, even
+ // though closest AA has NOT changed, is expensive
+ aaRetained = (AuralAttributesRetained)
+ (findClosestAAttribs(nIntersected)).clone();
+ }
+ else {
+ if (debugFlag) debugPrint(" NO active SoundScapes found");
+ }
+ }
+ if (nPrioritizedSound > 0) {
+ calcSchedulingAction();
+ muteSilentSounds();
+
+ // short term flag set within performActions->update()
+ positionalSoundUpdated = false;
+
+ // if listener parameters changed re-set View parameters
+ if (testListenerFlag()) {
+ if (debugFlag)
+ debugPrint(" audioDevice3D.setView");
+ audioDevice3D.setView(view);
+ }
+
+ numActiveSounds = performActions();
+
+ if (positionalSoundUpdated) {
+ // if performActions updated at least one positional sound
+ // was processed so the listener/view changes were processed,
+ // thus we can clear the SoundScheduler dirtyFlag, otherwise
+ // leave the flag dirty until a positional sound is updated
+ clearListenerFlag(); // clears listenerUpdated flag
+ }
+ }
+ /*
+ }
+ */
+ }
+
+
+ /**
+ * Prioritize all sounds associated with SoundScheduler (view)
+ * This only need be done once when scheduler is initialized since
+ * the priority list is updated when:
+ * a) PRIORITY_DIRTY_BIT in soundDirty field set; or
+ * b) sound added or removed from live array list
+ */
+ int prioritizeSounds() {
+ int size;
+ synchronized (prioritizedSounds) {
+ if (!prioritizedSounds.isEmpty()) {
+ prioritizedSounds.clear();
+ }
+ // TODO: sync soundStructure sound list
+ UnorderList retainedSounds = universe.soundStructure.getSoundList(view);
+ // QUESTION: what is in this sound list??
+ // mirror node or actual node???
+ nRetainedSounds = 0;
+ nImmedSounds = 0;
+ if (debugFlag)
+ debugPrint(" prioritizeSound , num retained sounds" +
+ retainedSounds.size());
+ for (int i=0; i<retainedSounds.size(); i++) {
+ addPrioritizedSound((SoundRetained)retainedSounds.get(i));
+ nRetainedSounds++;
+ }
+ // TODO: sync canvases
+ Enumeration canvases = view.getAllCanvas3Ds();
+ while (canvases.hasMoreElements()) {
+ Canvas3D canvas = (Canvas3D)canvases.nextElement();
+ GraphicsContext3D graphicsContext = canvas.getGraphicsContext3D();
+ Enumeration nonretainedSounds = graphicsContext.getAllSounds();
+ while (nonretainedSounds.hasMoreElements()) {
+ if (debugFlag)
+ debugPrint(" prioritizeSound , get non-retained sound");
+ Sound sound = (Sound)nonretainedSounds.nextElement();
+ if (sound == null) {
+ if (debugFlag)
+ debugPrint(" prioritizeSound , sound element is null");
+ // QUESTION: why should I have to do this?
+ continue;
+ }
+ addPrioritizedSound((SoundRetained)sound.retained);
+ nImmedSounds++;
+ }
+ }
+ if (debugFlag)
+ debugPrint(" prioritizeSound , num of processed retained sounds" +
+ nRetainedSounds);
+ debugPrint(" prioritizeSound , num of processed non-retained sounds" +
+ nImmedSounds);
+ size = prioritizedSounds.size();
+ } // sync
+ return size;
+ }
+
+
+ // methods that call this should synchronize prioritizedSounds
+ void addPrioritizedSound(SoundRetained mirSound) {
+ SoundRetained sound = mirSound.sgSound;
+ if (sound == null) { // this mirSound is a nonretained sound
+ // pad the "child" sg sound pointer with itself
+ mirSound.sgSound = mirSound;
+ sound = mirSound;
+ if (debugFlag)
+ debugPrint(":addPritorizedSound() sound NULL");
+ }
+ boolean addAtom = false;
+ // see if this atom is in the list already
+ // covers the case where the node was detached or unswitched but NOT
+ // deleted (so sample already loaded
+ // QUESTION: is above logic correct???
+ SoundSchedulerAtom atom = null;
+ atom = findSoundAtom(mirSound, 1); // look thru list for 1st instance
+ if (atom == null) {
+ atom = new SoundSchedulerAtom();
+ atom.soundScheduler = this; // save scheduler atom is associated with
+ addAtom = true;
+ }
+
+ // update fields in atom based on sound nodes state
+ atom.sound = mirSound; // new mirror sound
+ updateTransformedFields(mirSound);
+
+ if ( !addAtom ) {
+ return;
+ }
+
+ // if this atom being added then set the enable state
+ atom.enable(sound.enable);
+
+ if (prioritizedSounds.isEmpty()) {
+ // List is currently empty, so just add it
+ // insert into empty list of prioritizedSounds
+ prioritizedSounds.add(atom);
+ if (debugFlag)
+ debugPrint(":addPritorizedSound() inset sound " +
+ mirSound + " into empty priority list");
+ }
+ else {
+ // something's in the proirity list already
+ // Since list is not empty insert sound into list.
+ //
+ // Order is highest to lowest priority values, and
+ // for sounds with equal priority values, sound
+ // inserted first get in list given higher priority.
+ SoundRetained jSound;
+ SoundSchedulerAtom jAtom;
+ int j;
+ int jsounds = (prioritizedSounds.size() - 1);
+ float soundPriority = sound.priority;
+ for (j=jsounds; j>=0; j--) {
+ jAtom = (SoundSchedulerAtom)prioritizedSounds.get(j);
+ jSound = jAtom.sound;
+ if (debugFlag)
+ debugPrint(": priority of sound " + jSound.sgSound +
+ " element " + (j+1) + " of prioritized list");
+ if (soundPriority <= jSound.sgSound.priority) {
+ if (j==jsounds) {
+ // last element's priority is larger than
+ // current sound's priority, so add this
+ // sound to the end of the list
+ prioritizedSounds.add(atom);
+ if (debugFlag)
+ debugPrint(": insert sound at list bottom");
+ break;
+ }
+ else {
+ if (debugFlag)
+ debugPrint(
+ ": insert sound as list element " +
+ (j+1));
+ prioritizedSounds.add(j+1, atom);
+ break;
+ }
+ }
+ } // for loop
+ if (j < 0) { // insert at the top of the list
+ if (debugFlag)
+ debugPrint(": insert sound at top of priority list");
+ prioritizedSounds.add(0, atom);
+ }
+ } // else list not empty
+ }
+
+
+ /**
+ * Process active Soundscapes (if there are any) and intersect these
+ * soundscapes with the viewPlatform.
+ *
+ * Returns the number of soundscapes that intesect with
+ * view volume.
+ */
+ int findActiveSoundscapes() {
+ int nSscapes = 0;
+ int nSelectedSScapes = 0;
+ SoundscapeRetained ss = null;
+ SoundscapeRetained lss = null;
+ boolean intersected = false;
+ int nUnivSscapes = 0;
+ UnorderList soundScapes = null;
+
+ // Make a copy of references to the soundscapes in the universe
+ // that are both switch on and have non-null (transformed) regions,
+ // don't bother testing for intersection with view.
+ if (universe == null) {
+ if (debugFlag)
+ debugPrint(".findActiveSoundscapes() univ=null");
+ return 0;
+ }
+ soundScapes = universe.soundStructure.getSoundscapeList(view);
+ if (soundScapes == null) {
+ if (debugFlag)
+ debugPrint(".findActiveSoundscapes() soundScapes null");
+ return 0;
+ }
+
+ synchronized (soundScapes) {
+ nUnivSscapes = soundScapes.size;
+ if (nUnivSscapes == 0) {
+ if (debugFlag)
+ debugPrint(
+ ".findActiveSoundscapes() soundScapes size=0");
+ return 0;
+ }
+
+ // increase arrays lengths by increments of 32 elements
+ if (intersectedRegions.length < nSscapes) {
+ intersectedRegions = new Bounds[nSscapes + 32];
+ }
+ if (intersectedSoundscapes.length < nSscapes) {
+ intersectedSoundscapes = new SoundscapeRetained[nSscapes + 32];
+ }
+
+ // nSscapes is incremented for every Soundscape found
+ if (debugFlag)
+ debugPrint(".findActiveSoundscapes() nUnivSscapes="+
+ nUnivSscapes);
+ nSelectedSScapes = 0;
+ for (int k=0; k<nUnivSscapes; k++) {
+ lss = (SoundscapeRetained)soundScapes.get(k);
+
+ // Find soundscapes that intersect the view platform region
+ if (lss.transformedRegion == null ) {
+ continue;
+ }
+ if ((region instanceof BoundingSphere &&
+ lss.transformedRegion instanceof BoundingSphere) ||
+ (region instanceof BoundingBox &&
+ lss.transformedRegion instanceof BoundingBox) ||
+ (region instanceof BoundingPolytope &&
+ lss.transformedRegion instanceof BoundingPolytope) ) {
+ lss.transformedRegion.getWithLock(region);
+ if (debugFlag)
+ debugPrint(" get tranformed region " + region );
+ } else {
+ region = (Bounds)lss.transformedRegion.clone();
+ if (debugFlag)
+ debugPrint(" clone tranformed region " + region );
+ }
+ if (region!=null && viewPlatform.schedSphere.intersect(region)){
+ intersectedRegions[nSelectedSScapes] = (Bounds)region.clone();
+ // test View Platform intersection(lss))
+ intersectedSoundscapes[nSelectedSScapes] = lss;
+ if (debugFlag)
+ debugPrint(" store sscape " + lss +
+ " and region " + region + " into array");
+ nSelectedSScapes++;
+ if (debugFlag)
+ debugPrint(": region of intersection for "+
+ "soundscape "+k+" found at "+System.currentTimeMillis());
+ } else {
+ if (debugFlag)
+ debugPrint(
+ ": region of intersection for soundscape "+
+ k + " not found at "+ System.currentTimeMillis());
+ }
+ }
+ }
+ return(nSelectedSScapes);
+ }
+
+
+ AuralAttributesRetained findClosestAAttribs(int nSelectedSScapes) {
+ AuralAttributes aa = null;
+ SoundscapeRetained ss = null;
+ boolean intersected = false;
+
+ // Get auralAttributes from closest (non-null) soundscape
+ ss = null;
+ if (nSelectedSScapes == 1)
+ ss = intersectedSoundscapes[0];
+ else if (nSelectedSScapes > 1) {
+ Bounds closestRegions;
+ closestRegions = viewPlatform.schedSphere.closestIntersection(
+ intersectedRegions);
+ for (int j=0; j < intersectedRegions.length; j++) {
+ if (debugFlag)
+ debugPrint(" element " + j +
+ " in intersectedSoundsscapes is " + intersectedRegions[j]);
+ if (intersectedRegions[j] == closestRegions) {
+ ss = intersectedSoundscapes[j];
+ if (debugFlag)
+ debugPrint(" element " + j + " is closest");
+ break;
+ }
+ }
+ }
+
+ if (ss != null) {
+ if (debugFlag)
+ debugPrint(" closest SoundScape found is " + ss);
+ aa = ss.getAuralAttributes();
+ if (aa != null) {
+ if (debugFlag)
+ debugPrint(": AuralAttribute for " +
+ "soundscape is NOT null");
+ } else {
+ if (debugFlag)
+ debugPrint(": AuralAttribute for " +
+ "soundscape " + ss + " is NULL");
+ }
+ }
+ else {
+ if (debugFlag)
+ debugPrint(": AuralAttribute is null " +
+ "since soundscape is NULL");
+ }
+
+ if (debugFlag)
+ debugPrint(
+ " auralAttrib for closest SoundScape found is " + aa);
+ return ((AuralAttributesRetained)aa.retained);
+ }
+
+ /**
+ * Send current aural attributes to audio device
+ *
+ * Note that a AA's dirtyFlag is clear only after parameters are sent to
+ * audio device.
+ */
+ void updateAuralAttribs(AuralAttributesRetained attribs) {
+ if (auralAttribsChanged) {
+ if (attribs != null) {
+ synchronized (attribs) {
+/*
+ // TODO: remove use of aaDirty from AuralAttrib node
+ if ((attribs != lastAA) || attribs.aaDirty)
+*/
+ if (debugFlag) {
+ debugPrint(" set real updateAuralAttribs because");
+ }
+
+ // Send current aural attributes to audio device
+ // Assumes that aural attribute parameter is NOT null.
+ audioDevice3D.setRolloff(attribs.rolloff);
+ if (debugFlag)
+ debugPrint(" rolloff " + attribs.rolloff);
+
+ // Distance filter parameters
+ int arraySize = attribs.getDistanceFilterLength();
+ if ((attribs.filterType ==
+ AuralAttributesRetained.NO_FILTERING) ||
+ arraySize == 0 ) {
+ audioDevice3D.setDistanceFilter(
+ attribs.NO_FILTERING, null, null);
+ if (debugFlag)
+ debugPrint(" no filtering");
+ }
+ else {
+ Point2f[] attenuation = new Point2f[arraySize];
+ for (int i=0; i< arraySize; i++)
+ attenuation[i] = new Point2f();
+ attribs.getDistanceFilter(attenuation);
+ double[] distance = new double[arraySize];
+ float[] cutoff = new float[arraySize];
+ for (int i=0; i< arraySize; i++) {
+ distance[i] = attenuation[i].x;
+ cutoff[i] = attenuation[i].y;
+ }
+ audioDevice3D.setDistanceFilter(attribs.filterType,
+ distance, cutoff);
+ if (debugFlag) {
+ debugPrint(" filtering parameters: " +
+ " distance, cutoff arrays");
+ for (int jj=0; jj<arraySize; jj++)
+ debugPrint(
+ " " + distance[jj]+", "+cutoff[jj]);
+ }
+ }
+ audioDevice3D.setFrequencyScaleFactor(
+ attribs.frequencyScaleFactor);
+ if (debugFlag)
+ debugPrint(" freq.scale " +
+ attribs.frequencyScaleFactor);
+ audioDevice3D.setVelocityScaleFactor(
+ attribs.velocityScaleFactor);
+ if (debugFlag)
+ debugPrint(" velocity scale " +
+ attribs.velocityScaleFactor);
+ audioDevice3D.setReflectionCoefficient(
+ attribs.reflectionCoefficient);
+ if (audioDevice3DL2 != null) {
+ audioDevice3DL2.setReverbCoefficient(
+ attribs.reverbCoefficient);
+ audioDevice3DL2.setDecayFilter(attribs.decayFilter);
+ audioDevice3DL2.setDiffusion(attribs.diffusion);
+ audioDevice3DL2.setDensity(attribs.density);
+ if (debugFlag) {
+ debugPrint(" reflect coeff " +
+ attribs.reflectionCoefficient);
+ debugPrint(" reverb coeff " +
+ attribs.reverbCoefficient);
+ debugPrint(" decay filter " +
+ attribs.decayFilter);
+ debugPrint(" diffusion " +
+ attribs.diffusion);
+ debugPrint(" density " +
+ attribs.density);
+ }
+ }
+
+ // Precidence for determining Reverb Delay
+ // 1) If ReverbBounds set, bounds used to calculate delay.
+ // Default ReverbBounds is null meaning it's not defined.
+ // 2) If ReverbBounds is null, ReverbDelay used
+ // (implitic default value is defined as 40ms)
+
+ // If Bounds null, use Reverb Delay
+ // else calculate Reverb Delay from Bounds
+ Bounds reverbVolume = attribs.reverbBounds;
+ if (reverbVolume == null) {
+ audioDevice3D.setReverbDelay(attribs.reverbDelay);
+ if (debugFlag)
+ debugPrint(
+ " reverbDelay " + attribs.reverbDelay);
+ }
+ else {
+ float reverbDelay;
+ if (reverbVolume instanceof BoundingSphere) {
+ // worst (slowest) case is if sound in center of
+ // bounding sphere
+ reverbDelay = (float)
+ ((2.0*((BoundingSphere)reverbVolume).radius)/
+ AuralAttributesRetained.SPEED_OF_SOUND);
+ }
+ else {
+ // create a bounding sphere the surrounds the bounding
+ // object then calcalate the worst case as above
+ BoundingSphere tempSphere =
+ new BoundingSphere(reverbVolume);
+ reverbDelay = (float)
+ ((2.0 * tempSphere.radius)/
+ AuralAttributesRetained.SPEED_OF_SOUND);
+ }
+ audioDevice3D.setReverbDelay(reverbDelay);
+ if (debugFlag)
+ debugPrint(" reverbDelay " + reverbDelay);
+ }
+
+ // Audio device makes calculations for reverb decay
+ // using API's precidence where positive reverb order
+ // is used to clamp reverb decay time.
+ // Because of that the values are just passed along.
+ audioDevice3D.setReverbOrder(attribs.reverbOrder);
+ if (audioDevice3DL2 != null)
+ audioDevice3DL2.setDecayTime(attribs.decayTime);
+ } // sync attribs
+ resetAA = true;
+ } // attribs not null
+
+ else if (lastAA != null) {
+ // Even when there are no active auralAttributes, still check
+ // if a set of auralAttributes was passed to the AudioDevice,
+ // thus is now inactive and must be cleared out (set to
+ // default values).
+ // Gain scale factor of 1.0 implicit by default - this value
+ // passed to AudioDevice3D as part of InitialScaleFactor value.
+ if (debugFlag)
+ debugPrint(": set updateDefaultAAs");
+ audioDevice3D.setRolloff(1.0f);
+ audioDevice3D.setReflectionCoefficient(0.0f);
+ audioDevice3D.setReverbDelay(40.0f);
+ audioDevice3D.setReverbOrder(0);
+ // Distance filter parameters
+ audioDevice3D.setDistanceFilter(
+ AuralAttributesRetained.NO_FILTERING, null, null);
+ audioDevice3D.setFrequencyScaleFactor(1.0f);
+ audioDevice3D.setVelocityScaleFactor(0.0f);
+ if (audioDevice3DL2 != null) {
+ audioDevice3DL2.setReverbCoefficient(0.0f);
+ audioDevice3DL2.setReflectionDelay(20.0f);
+ audioDevice3DL2.setDecayTime(1000.0f);
+ audioDevice3DL2.setDecayFilter(5000.0f);
+ audioDevice3DL2.setDiffusion(1.0f);
+ audioDevice3DL2.setDensity(1.0f);
+ }
+
+ // Any change in AuralAttrib should force an update of all
+ // active sounds as passed to AudioDevice3D.
+ resetAA = true;
+ }
+ else {
+ if (debugFlag)
+ debugPrint(" updateAuralAttribs: none set or cleared");
+ }
+/*
+ attribs.aaDirty = false;
+*/
+ lastAA = attribs;
+ auralAttribsChanged = false;
+ }
+ }
+
+
+ void processSoundAtom(SoundSchedulerAtom soundAtom) {
+ SoundRetained mirSound = soundAtom.sound;
+ SoundRetained sound = mirSound.sgSound;
+
+ // Sounds that have finished playing are not put into list
+ if ((soundAtom.status == SoundSchedulerAtom.SOUND_COMPLETE) &&
+ (soundAtom.enabled != SoundSchedulerAtom.PENDING_ON )) {
+ // TODO:/QUESTION test for immediate mode (?)
+
+ // Unless the sound has been re-started, there's no need
+ // to process sound the finished playing the last time thru
+ // the run() loop
+ return; // don't need to process unless re-started
+ }
+
+ // Sound must be loaded if it hasn't been successfully loaded already
+ if (soundAtom.loadStatus != SoundRetained.LOAD_COMPLETE) {
+ // should be OK for soundData to be NULL - this will unclear
+ if (debugFlag)
+ debugPrint(": LOAD_PENDING - try attaching");
+ attachSoundData(soundAtom, sound.soundData, false);
+ }
+
+ // if the loadStatus is STILL NOT COMPLETE, wait to schedule
+ if (soundAtom.loadStatus != SoundRetained.LOAD_COMPLETE) {
+ if (debugFlag)
+ debugPrint(": not LOAD_COMPLETE yet, so bail");
+ // if sound is still not loaded, or loaded with null
+ return; // don't process this sound now
+ }
+
+ if (resetAA) { // explicitly force update of gain for sound
+ soundAtom.setAttribsDirtyFlag(SoundRetained.INITIAL_GAIN_DIRTY_BIT);
+ }
+
+ // Determine if sound is "active"
+ // Immediate mode sounds are always active.
+ // Sounds (both background and positional) are active only when their
+ // scheduling region intersects the viewPlatform.
+ boolean intersected = false;
+ if (!updateThread.active) {
+ region = null;
+ intersected = false; // force sound to be made inactive
+ if (debugFlag)
+ debugPrint(": updateThread NOT active");
+ }
+ else if (sound.getInImmCtx()) {
+ region = null;
+ intersected = true;
+ if (debugFlag)
+ debugPrint(": sound.getInImmCtx TRUE");
+ }
+ else {
+ if ( sound.schedulingRegion != null &&
+ mirSound.transformedRegion != null ) {
+ // QUESTION: shouldn't mirror sound transformedRegion be
+ // set to null when sound node's region set null?
+ if ((region instanceof BoundingSphere &&
+ mirSound.transformedRegion instanceof BoundingSphere) ||
+ (region instanceof BoundingBox &&
+ mirSound.transformedRegion instanceof BoundingBox) ||
+ (region instanceof BoundingPolytope &&
+ mirSound.transformedRegion instanceof BoundingPolytope)){
+ mirSound.transformedRegion.getWithLock(region);
+ } else {
+ region = (Bounds)mirSound.transformedRegion.clone();
+ }
+ } else {
+ region = null;
+ }
+
+ if (region != null) {
+ if (debugFlag)
+ debugPrint(": region is " + region );
+ intersected = viewPlatform.schedSphere.intersect(region);
+ if (debugFlag)
+ debugPrint(" intersection with viewPlatform is " +
+ intersected );
+ }
+ else {
+ intersected = false;
+ if (debugFlag)
+ debugPrint(": region is null, " +
+ "so region does NOT intersect viewPlatform");
+ }
+ }
+
+ // Get scheduling action based on sound state (flags, status)
+ // Sound must be unmuted or pending unmuting and
+ // either immediate mode node, or if retained
+ // then intersecting active region and switch state must be on.
+ if (debugFlag) {
+ debugPrint(": intersected = " + intersected);
+ debugPrint(": switchState = " + mirSound.switchState);
+ if (mirSound.switchState != null)
+ debugPrint(": switchOn = " + mirSound.switchState.currentSwitchOn);
+ debugPrint(": soundAtom.muted = " + soundAtom.muted);
+ }
+ if ( (sound.getInImmCtx() ||
+ (intersected &&
+ mirSound.switchState != null &&
+ mirSound.switchState.currentSwitchOn) ) &&
+ (soundAtom.muted == soundAtom.UNMUTED ||
+ soundAtom.muted == soundAtom.PENDING_UNMUTE) ) {
+ if (debugFlag)
+ debugPrint(": calcActiveSchedAction");
+ soundAtom.schedulingAction = soundAtom.calcActiveSchedAction();
+ }
+ else {
+ if (debugFlag)
+ debugPrint(": calcInactiveSchedAction");
+ soundAtom.schedulingAction = soundAtom.calcInactiveSchedAction();
+ }
+
+ if (debugFlag) {
+ debugPrint(": scheduling action calculated " +
+ "as "+ soundAtom.schedulingAction);
+ debugPrint(" dirtyFlag test of LISTENER_CHANGED " +
+ testListenerFlag() );
+ }
+
+ // If state has not changed but parameters have, set
+ // action to UPDATE.
+ // This test includes checking that SoundScheduler dirtyFlag
+ // set when Eye, Ear or ImagePlate-to-Vworld Xform changed
+ // even for non-BackgroundSounds
+ if ((soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_AUDIBLE) &&
+ (soundAtom.testDirtyFlags() || (testListenerFlag() &&
+ !(sound instanceof BackgroundSoundRetained)))) {
+ if (debugFlag) {
+ if (testListenerFlag()) {
+ debugPrint(" testListenerFlag = " +
+ testListenerFlag());
+ }
+ debugPrint(": action changed from " +
+ "as LEAVE_AUDIBLE to UPDATE");
+ }
+ soundAtom.schedulingAction = SoundSchedulerAtom.UPDATE;
+ }
+
+ // Update prioritized list of sounds whose scheduling action
+ // demands processing...
+
+ // Ensure sound are not stopped while looping thru prioritized sounds
+ switch (soundAtom.schedulingAction) {
+ case SoundSchedulerAtom.TURN_OFF:
+ soundAtom.status = SoundSchedulerAtom.SOUND_OFF;
+ turnOff(soundAtom); // Stop sound that need to be turned off
+ if (debugFlag)
+ debugPrint(": sound " + soundAtom.sampleId +
+ " action OFF results in call to stop");
+ soundAtom.schedulingAction = SoundSchedulerAtom.LEAVE_OFF;
+ break;
+
+ case SoundSchedulerAtom.MAKE_AUDIBLE:
+ case SoundSchedulerAtom.MAKE_SILENT:
+ case SoundSchedulerAtom.LEAVE_AUDIBLE:
+ case SoundSchedulerAtom.LEAVE_SILENT:
+ case SoundSchedulerAtom.PAUSE_AUDIBLE:
+ case SoundSchedulerAtom.PAUSE_SILENT:
+ case SoundSchedulerAtom.RESUME_AUDIBLE:
+ case SoundSchedulerAtom.RESUME_SILENT:
+ case SoundSchedulerAtom.UPDATE:
+
+ // Test for sound finishing playing since the last
+ // thru the run() loop.
+ //
+ // test current time against endTime of sound to determine
+ // if sound is Completely finished playing
+ long currentTime = System.currentTimeMillis();
+ if (soundAtom.endTime>0 && soundAtom.endTime<=currentTime) {
+ // sound's completed playing, force action
+ soundAtom.schedulingAction = SoundSchedulerAtom.COMPLETE;
+ if (debugFlag)
+ debugPrint(": sample complete;"+
+ " endTime = " + soundAtom.endTime +
+ ", currentTime = " + currentTime +
+ " so turned off");
+ soundAtom.status = SoundSchedulerAtom.SOUND_COMPLETE;
+ turnOff(soundAtom); // Stop sound in device that are complete
+ if (debugFlag)
+ debugPrint(": sound "+soundAtom.sampleId+
+ " action COMPLETE results in call to stop");
+ }
+ break;
+
+ case SoundSchedulerAtom.RESTART_AUDIBLE:
+ case SoundSchedulerAtom.START_AUDIBLE:
+ case SoundSchedulerAtom.RESTART_SILENT:
+ case SoundSchedulerAtom.START_SILENT:
+ break;
+
+ default: // includes COMPLETE, DO_NOTHING
+ soundAtom.schedulingAction = SoundSchedulerAtom.DO_NOTHING;
+ break;
+ } // switch
+
+ if (debugFlag)
+ debugPrint(": final scheduling action " +
+ "set to " + soundAtom.schedulingAction);
+ }
+
+
+ /**
+ * Determine scheduling action for each live sound
+ */
+ int calcSchedulingAction() {
+ // Temp variables
+ SoundRetained sound;
+ SoundRetained mirSound;
+ SoundSchedulerAtom soundAtom;
+ SoundRetained jSound;
+ int nSounds = 0;
+ boolean processSound;
+ // number of sounds to process including scene graph and immediate nodes
+ int numSoundsToProcess = 0;
+
+ if (universe == null) {
+ if (debugFlag)
+ debugPrint(
+ ": calcSchedulingAction: univ NULL");
+ return 0;
+ }
+ if (universe.soundStructure == null) {
+ if (debugFlag)
+ debugPrint(
+ ": calcSchedulingAction: soundStructure NULL");
+ return 0;
+ }
+
+ // List of prioritized "live" sounds taken from universe list of sounds.
+ // Maintained as an expandable array - start out with a small number of
+ // elements for this array then grow the list larger if necessary...
+ synchronized (prioritizedSounds) {
+ nSounds = prioritizedSounds.size();
+ if (debugFlag)
+ debugPrint(
+ ": calcSchedulingAction: soundsList size = " +
+ nSounds);
+
+ // (Large) Loop over all switched on sounds and conditionally put
+ // these into a order prioritized list of sound.
+ // Try throw out as many sounds as we can:
+ // Sounds finished playing (reached end before stopped)
+ // Sounds still yet to be loaded
+ // Positional sounds whose regions don't intersect view
+ // Sound to be stopped
+ // Those sounds remaining are inserted into a prioritized list
+
+ for (int i=0; i<nSounds; i++) {
+ soundAtom = ((SoundSchedulerAtom)prioritizedSounds.get(i));
+ mirSound = soundAtom.sound;
+ sound = mirSound.sgSound;
+ if (debugFlag) {
+ debugPrint(" calcSchedulingAction: sound at " + sound);
+ printAtomState(soundAtom);
+ }
+ // First make a list of switched on live sounds
+ // make sure to process turned-off sounds even if they are
+ // NOT active
+ processSound = false;
+ if ( (!sound.source.isLive() &&
+ !sound.getInImmCtx() ) ) {
+ if (debugFlag) {
+ debugPrint(" calcSchedulingAction sound " +
+ sound + " is NOT Live");
+ if (mirSound.source != null) {
+ if (mirSound.source.isLive()!=sound.source.isLive())
+ debugPrint(
+ " !=!=!= sound.isLive != mirSound");
+ }
+ }
+ if (soundAtom.playing ||
+ (soundAtom.enabled == SoundSchedulerAtom.ON)) {
+ soundAtom.setEnableState(SoundSchedulerAtom.PENDING_OFF);
+ processSound = true;
+ if (debugFlag)
+ debugPrint(" calcSchedulingAction !isLive: " +
+ "sound playing or ON, so set PENDING_OFF");
+ }
+ else if (soundAtom.enabled==SoundSchedulerAtom.PENDING_OFF){
+ processSound = true;
+ if (debugFlag)
+ debugPrint(" calcSchedulingAction !isLive: " +
+ "sound == PENDING_OFF so process");
+ }
+ else if (soundAtom.enabled==SoundSchedulerAtom.PENDING_ON) {
+ soundAtom.setEnableState(SoundSchedulerAtom.OFF);
+ if (debugFlag)
+ debugPrint(" calcSchedulingAction !isLive: " +
+ "sound == PENDING_ON so set OFF");
+ }
+ }
+ else { // live and switched on retained node or
+ // non-retained (immediate mode) node
+ processSound = true;
+ }
+
+ if (processSound) {
+ numSoundsToProcess++;
+ if (debugFlag) {
+ debugPrint(".testListenerFlag = " +
+ testListenerFlag() + "....");
+ debugPrint(" contents of live sound " +
+ soundAtom.sampleId + " before processing," );
+ debugPrint(" >>>>>>sound using sgSound at " + sound);
+ printAtomState(soundAtom);
+ }
+ processSoundAtom(soundAtom);
+ } // end of process sound
+ else {
+ soundAtom.schedulingAction = SoundSchedulerAtom.DO_NOTHING;
+ } // end of not process sound
+
+ } // end loop over all sound in soundList
+ } // sync
+
+ if (debugFlag) {
+ if (numSoundsToProcess > 0)
+ debugPrint(": number of liveSounds = " + numSoundsToProcess);
+ else
+ debugPrint(": number of liveSounds <= 0");
+ }
+
+ return numSoundsToProcess;
+ }
+
+
+ /**
+ * Mute sounds that are to be played silently.
+ *
+ * Not all the sound in the prioritized enabled sound list
+ * may be able to be played. Due to low priority, some sounds
+ * must be muted/silenced (if such an action frees up channel
+ * resources) to make way for sounds with higher priority.
+ * For each sound in priority list:
+ * For sounds whose actions are X_SILENT:
+ * Mute sounds to be silenced
+ * Add the number of channels used by this muted sound to
+ * current total number of channels used
+ * For all remaining sounds (with actions other than above)
+ * The number of channels that 'would be used' to play
+ * potentially audible sounds is compared with
+ * the number left on the device:
+ * If this sound would use more channels than available
+ * Change it's X_AUDIBLE action to X_SILENT
+ * Mute sounds to be silenced
+ * Add the number of channels used by this sound, muted
+ * or not, to current total number of channels used
+ *
+ * NOTE: requests for sounds to play beyond channel capability of
+ * the audio device do NOT throw an exception when more sounds are
+ * started than can be played. Rather the unplayable sounds are
+ * muted. It is up to the AudioDevice3D implementation to determine
+ * how muted/silent sounds are implememted (playing with gain zero
+ * and thus using up channel resources, or stop and restarted with
+ * correct offset when inactivated then re-actived.
+ */
+ void muteSilentSounds() {
+ // Temp variables
+ SoundRetained sound;
+ SoundRetained mirSound;
+ int totalChannelsUsed = 0;
+ SoundSchedulerAtom soundAtom;
+ int nAtoms;
+ synchronized (prioritizedSounds) {
+ nAtoms = prioritizedSounds.size();
+ if (debugFlag)
+ debugPrint(".muteSilentSounds(): Loop over prioritizedSounds list, " +
+ "size = " + nAtoms);
+ for (int i=0; i<nAtoms; i++) {
+ soundAtom = (SoundSchedulerAtom)prioritizedSounds.get(i);
+ mirSound = (SoundRetained)soundAtom.sound;
+ sound = mirSound.sgSound;
+ int sampleId = soundAtom.sampleId;
+ int status = soundAtom.status;
+
+ if (debugFlag) {
+ debugPrint(": contents of current sound " +
+ soundAtom.sampleId + " before switch on sAction" );
+ printAtomState(soundAtom);
+ }
+
+ if (soundAtom.status == SoundSchedulerAtom.SOUND_COMPLETE) {
+ continue;
+ }
+ if (soundAtom.schedulingAction == SoundSchedulerAtom.DO_NOTHING) {
+ continue;
+ }
+ if (sampleId == SoundRetained.NULL_SOUND) {
+ // skip it until next time thru calcSchedulingAction
+ continue;
+ }
+ if ( (soundAtom.schedulingAction == SoundSchedulerAtom.MAKE_SILENT) ||
+ (soundAtom.schedulingAction == SoundSchedulerAtom.RESTART_SILENT) ||
+ (soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_SILENT) ||
+ (soundAtom.schedulingAction == SoundSchedulerAtom.START_SILENT) ) {
+ // Mute sounds that are not already silent
+ if (status != SoundSchedulerAtom.SOUND_SILENT) {
+ // old status is not already muted/silent
+ audioDevice3D.muteSample(sampleId);
+ if (debugFlag)
+ debugPrint(": sound " + sampleId +
+ " action is x_SILENT, sound muted");
+ }
+ // now that the exact muting state is known get the actual
+ // number of channels used by this sound and add to total
+ int numberChannels =
+ audioDevice3D.getNumberOfChannelsUsed(sampleId);
+ soundAtom.numberChannels = numberChannels; // used in audio device
+ totalChannelsUsed += numberChannels; // could return zero
+ } // scheduling is for silent sound
+
+ else {
+ // First, test to see if the sound can play as unmuted.
+ int numberChannels =
+ audioDevice3D.getNumberOfChannelsUsed(sampleId, false);
+ // Mute sounds that have too low priority
+ if ((totalChannelsUsed+numberChannels)>totalChannels) {
+ if ((soundAtom.schedulingAction == SoundSchedulerAtom.MAKE_AUDIBLE) ||
+ (soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_AUDIBLE)) {
+ soundAtom.schedulingAction = SoundSchedulerAtom.MAKE_SILENT;
+ }
+ else if (soundAtom.schedulingAction == SoundSchedulerAtom.RESTART_AUDIBLE)
+ soundAtom.schedulingAction = SoundSchedulerAtom.RESTART_SILENT;
+ else if (soundAtom.schedulingAction == SoundSchedulerAtom.START_AUDIBLE)
+ soundAtom.schedulingAction = SoundSchedulerAtom.START_SILENT;
+ else if (soundAtom.schedulingAction == SoundSchedulerAtom.PAUSE_AUDIBLE)
+ soundAtom.schedulingAction = SoundSchedulerAtom.PAUSE_SILENT;
+ else if (soundAtom.schedulingAction == SoundSchedulerAtom.RESUME_AUDIBLE)
+ soundAtom.schedulingAction = SoundSchedulerAtom.RESUME_SILENT;
+ audioDevice3D.muteSample(sampleId);
+ if (debugFlag) {
+ debugPrint(": sound " + sampleId +
+ "number of channels needed is " +
+ numberChannels);
+ debugPrint(": sound " + sampleId +
+ " action is x_AUDIBLE but " +
+ "not enough channels free (" +
+ (totalChannels - totalChannelsUsed) +
+ ") so, sound muted");
+ }
+ }
+ // sound has enough channels to play
+ else if (status != SoundSchedulerAtom.SOUND_AUDIBLE) {
+ // old status is not already unmuted/audible
+ audioDevice3D.unmuteSample(sampleId);
+ if (debugFlag)
+ debugPrint(": sound " + sampleId +
+ " action is x_AUDIBLE and channels free so, " +
+ "sound unmuted");
+ }
+ // now that the exact muting state is known (re-)get actual
+ // number of channels used by this sound and add to total
+ numberChannels =
+ audioDevice3D.getNumberOfChannelsUsed(sampleId);
+ soundAtom.numberChannels = numberChannels; // used in audio device
+ totalChannelsUsed += numberChannels;
+ } // otherwise, scheduling is for potentally audible sound
+ // No sound in list should have action TURN_ or LEAVE_OFF
+ } // of for loop over sounds in list
+ }
+ }
+
+
+ void muteSilentSound(SoundSchedulerAtom soundAtom) {
+ // Temp variables
+ SoundRetained sound;
+ SoundRetained mirSound;
+ mirSound = (SoundRetained)soundAtom.sound;
+ sound = mirSound.sgSound;
+ int sampleId = soundAtom.sampleId;
+ int status = soundAtom.status;
+
+ if (status == SoundSchedulerAtom.SOUND_COMPLETE) {
+ return;
+ }
+ if (sampleId == SoundRetained.NULL_SOUND) {
+ return;
+ }
+ if (debugFlag) {
+ debugPrint(": contents of current sound " +
+ soundAtom.sampleId + " before switch on sAction" );
+ printAtomState(soundAtom);
+ }
+
+ if ( (soundAtom.schedulingAction == SoundSchedulerAtom.MAKE_SILENT) ||
+ (soundAtom.schedulingAction == SoundSchedulerAtom.RESTART_SILENT) ||
+ (soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_SILENT) ||
+ (soundAtom.schedulingAction == SoundSchedulerAtom.START_SILENT) ) {
+ // Mute sounds that are not already silent
+ if (status != SoundSchedulerAtom.SOUND_SILENT) {
+ // old status is not already muted/silent
+ audioDevice3D.muteSample(sampleId);
+ if (debugFlag)
+ debugPrint(": sound " + sampleId +
+ " action is x_SILENT, sound muted");
+ }
+ } // scheduling is for silent sound
+ }
+
+ /**
+ * Determine amount of time before next playing sound will be
+ * is complete.
+ *
+ * find the atom that has the least amount of time before is
+ * finished playing and return this time
+ * @return length of time in millisecond until the next active sound
+ * will be complete. Returns -1 if no sounds are playing (or all are
+ * complete).
+ */
+ long shortestTimeToFinish() {
+ long currentTime = System.currentTimeMillis();
+ long shortestTime = -1L;
+ SoundSchedulerAtom soundAtom;
+ synchronized (prioritizedSounds) {
+ int nAtoms = prioritizedSounds.size();
+ for (int i=0; i<nAtoms; i++) {
+ soundAtom = (SoundSchedulerAtom)prioritizedSounds.get(i);
+ if (soundAtom.status == SoundSchedulerAtom.SOUND_OFF ||
+ soundAtom.status == SoundSchedulerAtom.SOUND_COMPLETE )
+ continue;
+ long endTime = soundAtom.endTime;
+ if (endTime < 0)
+ // skip sounds that are to play infinitely (until stopped)
+ continue;
+ if (debugFlag) {
+ if (endTime == 0)
+ debugPrint(".shortestTimeToFinish: " +
+ "Internal Error - endTime 0 while sound playing");
+ }
+ // for all playing sounds (audible and silent) find how much
+ // time is left before the sound completed playing
+ long timeLeft = endTime - currentTime;
+ if (debugFlag)
+ debugPrint(
+ " shortestTimeToFinish timeLeft = " +
+ timeLeft);
+ if (timeLeft < 0L) {
+ // normalize completed sounds; force to zero
+ // so no waiting occurs before scheduler re-entered
+ timeLeft = 0L;
+ }
+ if (shortestTime < 0L) {
+ // save first atom's time as shortest
+ shortestTime = timeLeft;
+ }
+ else if (timeLeft < shortestTime) {
+ shortestTime = timeLeft;
+ }
+ }
+ }
+ if (debugFlag)
+ debugPrint(".shortestTimeToFinish returns " + shortestTime );
+ return shortestTime;
+ }
+
+
+ /**
+ * Perform the scheduling action for each prioritized sound.
+ *
+ * Now, finally, the scheduling action value reflects what is
+ * requested and what is physically posible to perform.
+ * So, for each sound in list of prioritized enabled sounds,
+ * start/update sounds that are REALLY supposed to be either
+ * playing audibly or playing silently.
+ * @return number of active (audible and silent) sounds
+ */
+ int performActions() {
+ // Temp variables
+ SoundRetained sound;
+ SoundRetained mirSound;
+ int nAtoms;
+ SoundSchedulerAtom soundAtom;
+ AuralAttributesRetained attribs;
+ int numActiveSounds = 0;
+ int sampleId;
+
+ synchronized (prioritizedSounds) {
+ nAtoms = prioritizedSounds.size();
+ for (int i=0; i<nAtoms; i++) {
+ // TODO: (Enhancement) Get all sound node fields here
+ // and store locally for performance
+ soundAtom = (SoundSchedulerAtom)prioritizedSounds.get(i);
+ mirSound = soundAtom.sound;
+ sound = mirSound.sgSound;
+ sampleId = soundAtom.sampleId;
+
+ if (sampleId == SoundRetained.NULL_SOUND) {
+ // skip it until next time thru calcSchedulingAction
+ continue;
+ }
+
+ // Two flags denoting that AuralAttributes have be changed and thus
+ // sounds have to potentially be rendered are maintained and set in
+ // updateAuralAttribs().
+ resetAA = false;
+
+ // check to see if aural attributes changed and have to be updated
+ // must be done before list of sound processed so that Aural Attributes
+ // that affect Sound fields can be set in AudioDevice
+ // TODO: this is not effient if auralAttribs always the same
+ if (sound.getInImmCtx()) {
+ if (graphicsCtx !=null && graphicsCtx.auralAttributes !=null) {
+ aaImmed = (AuralAttributesRetained)
+ (graphicsCtx.auralAttributes.retained);
+ attribs = aaImmed;
+ }
+ else {
+ attribs = null;
+ }
+ }
+ else {
+ attribs = aaRetained;
+ }
+ updateAuralAttribs(attribs);
+
+ if (debugFlag) {
+ debugPrint(": contents of current sound " +
+ sampleId + " before start/update " );
+ printAtomState(soundAtom);
+ }
+
+ switch (soundAtom.schedulingAction) {
+ case SoundSchedulerAtom.RESTART_AUDIBLE:
+ // stop sound first then fall thru to re-start
+ turnOff(soundAtom);
+ case SoundSchedulerAtom.START_AUDIBLE:
+ // Pause and Resume related actions are checked when sound
+ // is to be started or restarted
+ if (soundAtom.paused == soundAtom.PENDING_PAUSE)
+ pause(soundAtom);
+ if (soundAtom.paused == soundAtom.PENDING_UNPAUSE)
+ unpause(soundAtom);
+ if (soundAtom.paused == soundAtom.UNPAUSED) {
+ // if its unpaused, start audible sound
+ soundAtom.status = SoundSchedulerAtom.SOUND_AUDIBLE;
+ render(true, soundAtom, attribs);
+ }
+ else { // sound paused
+ soundAtom.status = SoundSchedulerAtom.SOUND_PAUSED;
+ // start it after when the sound is not paused
+ soundAtom.setEnableState(SoundSchedulerAtom.PENDING_ON);
+ }
+ numActiveSounds++;
+ break;
+
+ case SoundSchedulerAtom.RESTART_SILENT:
+ // stop sound first then fall thru to re-start
+ turnOff(soundAtom);
+ case SoundSchedulerAtom.START_SILENT:
+ // Pause and Resume related actions are checked when sound
+ // is to be started or restarted
+ if (soundAtom.paused == soundAtom.PENDING_PAUSE)
+ pause(soundAtom);
+ if (soundAtom.paused == soundAtom.PENDING_UNPAUSE)
+ unpause(soundAtom);
+ if (soundAtom.paused == soundAtom.UNPAUSED) {
+ // if the sound is unpaused, start silent sound
+ soundAtom.status = SoundSchedulerAtom.SOUND_SILENT;
+ render(true, soundAtom, attribs);
+ }
+ else { // sound paused
+ soundAtom.status = SoundSchedulerAtom.SOUND_PAUSED;
+ // start it after when the sound is not paused
+ soundAtom.setEnableState(SoundSchedulerAtom.PENDING_ON);
+ }
+ numActiveSounds++;
+ break;
+
+ case SoundSchedulerAtom.RESUME_AUDIBLE:
+ // pause then fall thru set make audible
+ unpause(soundAtom);
+ case SoundSchedulerAtom.MAKE_AUDIBLE:
+ // change status to audible then update sound
+ soundAtom.status = SoundSchedulerAtom.SOUND_AUDIBLE;
+ render(false, soundAtom, attribs);
+ numActiveSounds++;
+ break;
+ case SoundSchedulerAtom.RESUME_SILENT:
+ // pause then fall thru set make silent
+ unpause(soundAtom);
+ case SoundSchedulerAtom.MAKE_SILENT:
+ // change status to silent AFTER calling render so
+ // that a currently audible sound will be muted.
+ // TODO: why set status AFTER??
+ render(false, soundAtom, attribs);
+ soundAtom.status = SoundSchedulerAtom.SOUND_SILENT;
+ numActiveSounds++;
+ break;
+
+ case SoundSchedulerAtom.PAUSE_AUDIBLE:
+ case SoundSchedulerAtom.PAUSE_SILENT:
+ pause(soundAtom);
+ soundAtom.status = SoundSchedulerAtom.SOUND_PAUSED;
+ numActiveSounds++;
+ break;
+
+ case SoundSchedulerAtom.UPDATE:
+ render(false, soundAtom, attribs);
+ numActiveSounds++;
+ break;
+
+ case SoundSchedulerAtom.LEAVE_AUDIBLE:
+ case SoundSchedulerAtom.LEAVE_SILENT:
+ case SoundSchedulerAtom.LEAVE_PAUSED:
+ if (resetAA || soundAtom.testDirtyFlags())
+ render(false, soundAtom, attribs);
+ if (debugFlag)
+ debugPrint(": LEAVE_AUDIBLE or _SILENT " +
+ "seen");
+ numActiveSounds++;
+ break;
+
+ case SoundSchedulerAtom.TURN_OFF:
+ turnOff(soundAtom);
+ break;
+
+ case SoundSchedulerAtom.LEAVE_OFF:
+ case SoundSchedulerAtom.COMPLETE:
+ case SoundSchedulerAtom.DO_NOTHING:
+ break;
+
+ default:
+ if (internalErrors)
+ debugPrint(": Internal Error"+
+ " unknown action");
+ break;
+ }
+ // Clear atom state and attrib dirty flags
+ soundAtom.clearStateDirtyFlag();
+ soundAtom.clearAttribsDirtyFlag();
+
+ } // for sounds in priority list
+ }
+
+ // Now that aural attribute change forced each processed sounds
+ // to be updated, clear this special reset aural attrubute flag
+ resetAA = false;
+
+ return numActiveSounds;
+ }
+
+
+ /**
+ * render (start or update) the oscillator associated with this sound
+ */
+ void render(boolean startFlag,
+ SoundSchedulerAtom soundAtom,
+ AuralAttributesRetained attribs) {
+
+ SoundRetained mirrorSound = soundAtom.sound;
+ SoundRetained sound = mirrorSound.sgSound;
+ if (debugFlag)
+ debugPrint(".render " + sound);
+
+ if ( soundAtom.sampleId == SoundRetained.NULL_SOUND ||
+ soundAtom.soundData == null ) {
+ if (internalErrors)
+ debugPrint(".render - Internal Error: " +
+ "null sample data");
+ return;
+ }
+
+ int index = soundAtom.sampleId;
+
+ // Depending on Mute and/or pause flags, set sound parameters
+ if (startFlag) {
+ if ( (sound instanceof PointSoundRetained) ||
+ (sound instanceof ConeSoundRetained) ) {
+ updateXformedParams(true, soundAtom);
+ }
+ updateSoundParams(true, soundAtom, attribs);
+ start(soundAtom);
+ }
+ else {
+ if (soundAtom.status == SoundSchedulerAtom.SOUND_AUDIBLE) {
+ if ( (sound instanceof PointSoundRetained) ||
+ (sound instanceof ConeSoundRetained) ) {
+ updateXformedParams(false, soundAtom);
+ }
+ updateSoundParams(false, soundAtom, attribs);
+ update(soundAtom);
+ }
+ } // if sound Audible
+ } // render
+
+
+ /**
+ * Start the sample associated with this sound
+ * Do everything necessary to start the sound:
+ * set start time
+ * the oscillator associated with this sound
+ */
+ void start(SoundSchedulerAtom soundAtom) {
+ SoundRetained sound = soundAtom.sound.sgSound;
+ int index = soundAtom.sampleId;
+ int startStatus = -1;
+ if (index != SoundRetained.NULL_SOUND &&
+ (startStatus = audioDevice3D.startSample(index)) >= 0) {
+ if (debugFlag)
+ debugPrint(".start: " + index );
+ soundAtom.playing = true;
+ soundAtom.startTime = audioDevice3D.getStartTime(index);
+ soundAtom.calculateEndTime();
+ if (debugFlag)
+ debugPrint(".start: begintime = " +
+ soundAtom.startTime + ", endtime " + soundAtom.endTime);
+ }
+ else { // error returned by audio device when trying to start
+ soundAtom.startTime = 0;
+ soundAtom.endTime = 0;
+ soundAtom.playing = false;
+ if (debugFlag) {
+ debugPrint(".start: error " + startStatus +
+ " returned by audioDevice3D.startSample(" + index
+ + ")" );
+ debugPrint(
+ " start/endTime set to zero");
+ }
+ }
+ }
+
+
+ /**
+ * Exlicitly update the sound parameters associated with a sample
+ */
+ void update(SoundSchedulerAtom soundAtom) {
+ int index = soundAtom.sampleId;
+
+ if (index == SoundRetained.NULL_SOUND) {
+ return;
+ }
+ SoundRetained sound = soundAtom.sound;
+ audioDevice3D.updateSample(index);
+ if (debugFlag) {
+ debugPrint(".update: " + index );
+ }
+ soundAtom.calculateEndTime();
+ if (sound instanceof PointSoundRetained ||
+ sound instanceof ConeSoundRetained) {
+ positionalSoundUpdated = true;
+ }
+ }
+
+
+ /**
+ * stop playing one specific sound node
+ *
+ * If setPending flag true, sound is stopped but enable state
+ * is set to pending-on so that it is restarted.
+ */
+ void stopSound(SoundSchedulerAtom soundAtom, boolean setPending) {
+ if (audioDevice3D == null)
+ return;
+
+ if (debugFlag)
+ debugPrint(":stopSound(" + soundAtom +
+ "), enabled = " + soundAtom.enabled);
+ switch (soundAtom.enabled) {
+ case SoundSchedulerAtom.ON:
+ if (setPending)
+ soundAtom.setEnableState(SoundSchedulerAtom.PENDING_ON);
+ else
+ soundAtom.setEnableState(SoundSchedulerAtom.SOUND_OFF);
+ break;
+ case SoundSchedulerAtom.PENDING_OFF:
+ soundAtom.setEnableState(SoundSchedulerAtom.SOUND_OFF);
+ break;
+ case SoundSchedulerAtom.PENDING_ON:
+ if (!setPending)
+ // Pending sounds to be stop from playing later
+ soundAtom.setEnableState(SoundSchedulerAtom.SOUND_OFF);
+ break;
+ default:
+ break;
+ }
+ soundAtom.status = SoundSchedulerAtom.SOUND_OFF;
+ turnOff(soundAtom);
+ }
+
+ /**
+ * Deactive all playing sounds
+ * If the sound is continuous thendSilence it but leave it playing
+ * otherwise stop sound
+ */
+ synchronized void deactivateAllSounds() {
+ SoundRetained sound;
+ SoundRetained mirSound;
+ SoundSchedulerAtom soundAtom;
+
+ if (audioDevice3D == null)
+ return;
+
+ if (debugFlag)
+ debugPrint(".deactivateAllSounds");
+
+ // sync this method from interrupting run() while loop
+ synchronized (prioritizedSounds) {
+ if (prioritizedSounds != null) {
+ int nAtoms = prioritizedSounds.size();
+ if (debugFlag)
+ debugPrint("silenceAll " + nAtoms + " Sounds");
+ for (int i=0; i<nAtoms; i++) {
+ // TODO: (Enhancement) Get all sound node fields here
+ // and store locally for performance
+ soundAtom = (SoundSchedulerAtom)prioritizedSounds.get(i);
+ mirSound = soundAtom.sound;
+ sound = mirSound.sgSound;
+ if (sound.continuous) {
+ // make playing sound silent
+ if (debugFlag)
+ debugPrint("deactivateAll atomScheduling " +
+ "before calcInactiveSchedAction" +
+ soundAtom.schedulingAction);
+ soundAtom.schedulingAction =
+ soundAtom.calcInactiveSchedAction();
+ if (debugFlag)
+ debugPrint("deactivateAll atomScheduling " +
+ "after calcInactiveSchedAction" +
+ soundAtom.schedulingAction);
+ // perform muting of sound
+ muteSilentSound(soundAtom); // mark sound as silence
+ }
+ else {
+ // stop playing sound but make pending on
+ stopSound(soundAtom, true); // force pendingOn TRUE
+ soundAtom.schedulingAction = soundAtom.LEAVE_OFF;
+ if (debugFlag)
+ debugPrint("deactivateAll atomScheduling " +
+ "forced to TURN_OFF, set pending On");
+ }
+
+ } // for sounds in priority list
+ }
+ }
+ performActions();
+ }
+
+
+ /**
+ * Pause all activity playing sounds
+ */
+ synchronized void pauseAllSounds() {
+ SoundRetained sound;
+ SoundRetained mirSound;
+
+ if (audioDevice3D == null)
+ return;
+
+ stallThread = true;
+ if (debugFlag)
+ debugPrint(".pauseAll stallThread set to true");
+
+ // sync this method from interrupting run() while loop
+ synchronized (prioritizedSounds) {
+ if (prioritizedSounds != null) {
+ int nAtoms = prioritizedSounds.size();
+ if (debugFlag)
+ debugPrint(":pauseAll " + nAtoms + " Sounds");
+ for (int i=0; i<nAtoms; i++) {
+ // TODO: (Enhancement) Get all sound node fields here
+ // and store locally for performance
+ SoundSchedulerAtom soundAtom =
+ (SoundSchedulerAtom)prioritizedSounds.get(i);
+ mirSound = soundAtom.sound;
+ sound = mirSound.sgSound;
+
+ switch (soundAtom.enabled) {
+ case SoundSchedulerAtom.ON:
+ case SoundSchedulerAtom.PENDING_OFF:
+ pause(soundAtom);
+ if (debugFlag)
+ debugPrint(
+ ".pauseAllSounds PAUSE sound " + sound);
+ break;
+ default:
+ break;
+ }
+ } // for sounds in priority list
+ }
+ }
+ }
+
+
+ /**
+ * Resume playing all paused active sounds
+ */
+ synchronized void resumeAllSounds() {
+ SoundRetained sound;
+ SoundRetained mirSound;
+
+ if (audioDevice3D == null)
+ return;
+
+ if (debugFlag)
+ debugPrint(".resumeAll stallThread set to true");
+
+ // sync this method from interrupting run() while loop
+ synchronized (prioritizedSounds) {
+ if (prioritizedSounds != null) {
+ int nAtoms = prioritizedSounds.size();
+ if (debugFlag)
+ debugPrint(": resumeAll " + nAtoms + " Sounds ");
+
+ for (int i=0; i<nAtoms; i++) {
+ // TODO: (Enhancement) Get all sound node fields here
+ // and store locally for performance
+ SoundSchedulerAtom soundAtom =
+ (SoundSchedulerAtom)prioritizedSounds.get(i);
+ mirSound = soundAtom.sound;
+ sound = mirSound.sgSound;
+
+ switch (soundAtom.enabled) {
+ case SoundSchedulerAtom.ON:
+ case SoundSchedulerAtom.PENDING_OFF:
+ unpause(soundAtom);
+ if (debugFlag)
+ debugPrint(".resumeAll - sound = " + sound);
+ break;
+ default:
+ break;
+ }
+ } // for sounds in priority list
+ }
+ }
+ stallThread = false;
+ }
+
+ /**
+ * Stop all activity playing sounds
+ */
+ synchronized void stopAllSounds() {
+ stopAllSounds(false);
+ }
+
+ synchronized void stopAllSounds(boolean setPlayingSoundsPending) {
+ // QUESTION: how can I assure that all sounds on device
+ // are stopped before thread paused/shutdown
+ if (debugFlag)
+ debugPrint(".stopAllSounds entered");
+ SoundRetained sound;
+ SoundRetained mirSound;
+
+ if (audioDevice3D == null)
+ return;
+
+ if (lastEventReceived == WindowEvent.WINDOW_ICONIFIED) {
+ return; // leave sounds playing
+ }
+
+ // sync this method from interrupting run() while loop
+ synchronized (prioritizedSounds) {
+ if (prioritizedSounds != null) {
+ int nAtoms = prioritizedSounds.size();
+ if (debugFlag)
+ debugPrint(": stopAll " + nAtoms + " Sounds ");
+
+ for (int i=0; i<nAtoms; i++) {
+ // TODO: (Enhancement) Get all sound node fields here
+ // and store locally for performance
+ SoundSchedulerAtom soundAtom =
+ (SoundSchedulerAtom)prioritizedSounds.get(i);
+ if (debugFlag)
+ debugPrint(" stop(" + soundAtom + ")");
+ // stop playing Sound - optionally set pending enabled
+ stopSound(soundAtom, setPlayingSoundsPending);
+ } // for sounds in priority list
+
+ // QUESTION: - removed the code that empties out prioritized
+ // sound atom list. Are there cases when core calling
+ // StopAllSounds expects sounds to be cleared??
+ }
+ }
+ if (debugFlag)
+ debugPrint(".stopAllSounds exited");
+ }
+
+ // TODO: Mute All Sounds, complementary to Stop All Sounds
+ // "should return from run loop - but simply WAIT until sounds
+ // are unmuted. " ???
+
+
+ /**
+ * pause the sample associated with this sound
+ */
+ void pause(SoundSchedulerAtom soundAtom) {
+ if (soundAtom.sampleId == SoundRetained.NULL_SOUND)
+ return;
+ // Ensure sound are not modified while looping thru prioritized sounds
+ if (debugFlag)
+ debugPrint(".pause");
+ audioDevice3D.pauseSample(soundAtom.sampleId);
+ soundAtom.setPauseState(soundAtom.PAUSED);
+ }
+
+ void unpause(SoundSchedulerAtom soundAtom) {
+ if (soundAtom.sampleId == SoundRetained.NULL_SOUND)
+ return;
+ if (debugFlag)
+ debugPrint(".unpause");
+ audioDevice3D.unpauseSample(soundAtom.sampleId);
+ soundAtom.setPauseState(soundAtom.UNPAUSED);
+ }
+
+ /**
+ * stop the sample associated with this sound
+ */
+ void turnOff(SoundSchedulerAtom soundAtom) {
+ // Ensure sound are not stopped while looping thru prioritized sounds
+ if (soundAtom.sampleId == SoundRetained.NULL_SOUND)
+ return;
+ if (debugFlag)
+ debugPrint(".turnOff");
+ if (audioDevice3D.stopSample(soundAtom.sampleId) < 0) {
+ if (internalErrors) {
+ debugPrint("Internal Error: stop sample error");
+ }
+ }
+ soundAtom.playing = false;
+ soundAtom.startTime = 0;
+ soundAtom.endTime = 0;
+
+ }
+
+
+ /**
+ * Update VirtualWorld local transform, sound position and direction.
+ *
+ * This is done dynamically from PointSoundRetained as these fields
+ * are updated (when soundAtom.status is AUDIBLE or SILENT), or by this.
+ * render() method when sound is started (sound.enabled is true).
+ *
+ * This method should only be called if mirror sound is a Point or
+ * ConeSound.
+ *
+ * Important: pre-transformed position and direction sent to AudioDevice.
+ */
+ void updateXformedParams(boolean updateAll, SoundSchedulerAtom soundAtom) {
+ PointSoundRetained mirrorPtSound = (PointSoundRetained)soundAtom.sound;
+ PointSoundRetained ptSound = (PointSoundRetained)mirrorPtSound.sgSound;
+ int index = soundAtom.sampleId;
+ if (index == SoundRetained.NULL_SOUND)
+ return;
+ PointSoundRetained ps = (PointSoundRetained)mirrorPtSound;
+
+ // Set Transform
+
+ /*
+ // TODO: per sound tranforms can now be passed to AudioDevice
+ // modify and execute the code below
+
+ // MoveAppBoundingLeaf > ~/Current/MoveAppBoundingLeaf.outted,
+ // instead transformed position and direction
+ // points/vectors will be passed to AudioDevice directly.
+
+ // vvvvvvvvvvvvvvvvvvvvvvvvvvv
+ if (updateAll || soundAtom.testDirtyFlag(SoundRetained.XFORM_DIRTY_BIT){
+ Transform3D xform = new Transform3D();
+ ps.trans.getWithLock(xform);
+ if (debugFlag) {
+ debugPrint(".updateXformedParams " +
+ "setVworldXfrm for ps @ " + ps + ":");
+ debugPrint(" xformPosition " +
+ ps.xformPosition.x + ", " +
+ ps.xformPosition.y + ", " +
+ ps.xformPosition.z );
+ debugPrint(" column-major transform ");
+ debugPrint(" " +
+ xform.mat[0]+", " + xform.mat[1]+", "+
+ xform.mat[2]+", " + xform.mat[3]);
+ debugPrint(" " +
+ xform.mat[4]+", " + xform.mat[5]+", "+
+ xform.mat[6]+", " + xform.mat[7]);
+ debugPrint(" " +
+ xform.mat[8]+", " + xform.mat[9]+", "+
+ xform.mat[10]+", " + xform.mat[11]);
+ debugPrint(" " +
+ xform.mat[12]+", " + xform.mat[13]+", "+
+ xform.mat[14]+", " + xform.mat[15]);
+ }
+ audioDevice3D.setVworldXfrm(index, xform);
+ soundAtom.clearStateDirtyFlag( SoundRetained.XFORM_DIRTY_BIT);
+ // TODO: make sure position and direction are already transformed and stored
+ // into xformXxxxxxx fields.
+ }
+ // ^^^^^^^^^^^^^^^^^^^^^
+ */
+
+ // Set Position
+ if (updateAll || testListenerFlag() ||
+ soundAtom.testDirtyFlag(soundAtom.attribsDirty,
+ SoundRetained.POSITION_DIRTY_BIT) ||
+ soundAtom.testDirtyFlag(soundAtom.stateDirty,
+ SoundRetained.XFORM_DIRTY_BIT) )
+ {
+ Point3f xformLocation = new Point3f();
+ mirrorPtSound.getXformPosition(xformLocation);
+ Point3d positionD = new Point3d(xformLocation);
+ if (debugFlag)
+ debugPrint("xform'd Position: ("+positionD.x+", "+
+ positionD.y+", "+ positionD.z+")" );
+ audioDevice3D.setPosition(index, positionD);
+ }
+
+ // Set Direction
+ if (mirrorPtSound instanceof ConeSoundRetained) {
+ ConeSoundRetained cn = (ConeSoundRetained)mirrorPtSound;
+ ConeSoundRetained cnSound = (ConeSoundRetained)mirrorPtSound.sgSound;
+ if (updateAll ||
+ // TODO: test for XFORM_DIRTY only in for 1.2
+ soundAtom.testDirtyFlag(soundAtom.attribsDirty,
+ (SoundRetained.DIRECTION_DIRTY_BIT |
+ SoundRetained.XFORM_DIRTY_BIT) ) ) {
+
+ Vector3f xformDirection = new Vector3f();
+ cn.getXformDirection(xformDirection);
+ Vector3d directionD = new Vector3d(xformDirection);
+ audioDevice3D.setDirection(index, directionD);
+ }
+ }
+ }
+
+
+ void updateSoundParams(boolean updateAll, SoundSchedulerAtom soundAtom,
+ AuralAttributesRetained attribs) {
+
+ SoundRetained mirrorSound = soundAtom.sound;
+ SoundRetained sound = mirrorSound.sgSound;
+ int index = soundAtom.sampleId;
+ int arraySize;
+
+ if (index == SoundRetained.NULL_SOUND)
+ return;
+ if (debugFlag)
+ debugPrint(".updateSoundParams(dirytFlags=" +
+ soundAtom.attribsDirty + ", " + soundAtom.stateDirty + ")");
+
+ // since the sound is audible, make sure that the parameter for
+ // this sound are up-to-date.
+ if (updateAll || soundAtom.testDirtyFlag(
+ soundAtom.attribsDirty, SoundRetained.INITIAL_GAIN_DIRTY_BIT)) {
+
+ if (attribs != null) {
+ audioDevice3D.setSampleGain(index,
+ (sound.initialGain * attribs.attributeGain));
+ }
+ else {
+ audioDevice3D.setSampleGain(index, sound.initialGain);
+ }
+ }
+
+ if (updateAll || soundAtom.testDirtyFlag(
+ soundAtom.attribsDirty, SoundRetained.LOOP_COUNT_DIRTY_BIT)) {
+ if (debugFlag)
+ debugPrint(" audioDevice.setLoop(" + sound.loopCount +
+ ") called");
+ audioDevice3D.setLoop(index, sound.loopCount);
+ }
+
+ if (updateAll || soundAtom.testDirtyFlag(
+ soundAtom.attribsDirty, SoundRetained.RATE_DIRTY_BIT)) {
+ if (audioDevice3DL2 != null) {
+ if (debugFlag)
+ debugPrint(" audioDevice.setRateScaleFactor(" +
+ sound.rate + ") called");
+ audioDevice3DL2.setRateScaleFactor(index, sound.rate);
+ }
+ }
+
+ if (updateAll || soundAtom.testDirtyFlag(
+ soundAtom.attribsDirty, SoundRetained.DISTANCE_GAIN_DIRTY_BIT)){
+ if (sound instanceof ConeSoundRetained) {
+ ConeSoundRetained cnSound = (ConeSoundRetained)sound;
+
+ // set distance attenuation
+ arraySize = cnSound.getDistanceGainLength();
+ if (arraySize == 0) {
+ // send default
+ audioDevice3D.setDistanceGain(index, null, null, null, null);
+ }
+ else {
+ Point2f[] attenuation = new Point2f[arraySize];
+ Point2f[] backAttenuation = new Point2f[arraySize];
+ for (int i=0; i< arraySize; i++) {
+ attenuation[i] = new Point2f();
+ backAttenuation[i] = new Point2f();
+ }
+ cnSound.getDistanceGain(attenuation, backAttenuation);
+ double[] frontDistance = new double[arraySize];
+ float[] frontGain = new float[arraySize];
+ double[] backDistance = new double[arraySize];
+ float[] backGain = new float[arraySize];
+ for (int i=0; i< arraySize; i++) {
+ frontDistance[i] = attenuation[i].x;
+ frontGain[i] = attenuation[i].y;
+ backDistance[i] = backAttenuation[i].x;
+ backGain[i] = backAttenuation[i].y;
+ }
+ audioDevice3D.setDistanceGain(index,
+ frontDistance, frontGain, backDistance, backGain);
+ }
+ } // ConeSound distanceGain
+ else if (sound instanceof PointSoundRetained) {
+ PointSoundRetained ptSound = (PointSoundRetained)sound;
+
+ // set distance attenuation
+ arraySize = ptSound.getDistanceGainLength();
+ if (arraySize == 0) {
+ // send default
+ audioDevice3D.setDistanceGain(index, null, null, null, null);
+ }
+ else {
+ Point2f[] attenuation = new Point2f[arraySize];
+ for (int i=0; i< arraySize; i++)
+ attenuation[i] = new Point2f();
+ ptSound.getDistanceGain(attenuation);
+ double[] frontDistance = new double[arraySize];
+ float[] frontGain = new float[arraySize];
+ for (int i=0; i< arraySize; i++) {
+ frontDistance[i] = attenuation[i].x;
+ frontGain[i] = attenuation[i].y;
+ }
+ audioDevice3D.setDistanceGain(index, frontDistance,
+ frontGain, null, null);
+ }
+ } // PointSound distanceGain
+ }
+
+ if ((sound instanceof ConeSoundRetained) &&
+ (updateAll || soundAtom.testDirtyFlag(soundAtom.attribsDirty,
+ SoundRetained.ANGULAR_ATTENUATION_DIRTY_BIT)) ) {
+
+ // set angular attenuation
+ ConeSoundRetained cnSound = (ConeSoundRetained)sound;
+ arraySize = cnSound.getAngularAttenuationLength();
+ if (arraySize == 0) {
+ // send default
+ double[] angle = new double[2];
+ float[] scaleFactor = new float[2];
+ angle[0] = 0.0;
+ angle[1] = (Math.PI)/2.0;
+ scaleFactor[0] = 1.0f;
+ scaleFactor[1] = 0.0f;
+ audioDevice3D.setAngularAttenuation(index,
+ cnSound.NO_FILTERING,
+ angle, scaleFactor, null);
+ }
+ else {
+ Point3f[] attenuation = new Point3f[arraySize];
+ for (int i=0; i< arraySize; i++) {
+ attenuation[i] = new Point3f();
+ }
+ cnSound.getAngularAttenuation(attenuation);
+ double[] angle = new double[arraySize];
+ float[] scaleFactor = new float[arraySize];
+ float[] cutoff = new float[arraySize];
+ for (int i=0; i< arraySize; i++) {
+ angle[i] = attenuation[i].x;
+ scaleFactor[i] = attenuation[i].y;
+ cutoff[i] = attenuation[i].z;
+ }
+ audioDevice3D.setAngularAttenuation(index,
+ cnSound.filterType,
+ angle, scaleFactor, cutoff);
+ }
+ }
+ }
+
+
+ /**
+ * Check (and set if necessary) AudioDevice3D field
+ */
+ boolean checkAudioDevice3D() {
+ if (universe != null) {
+ if (universe.currentView != null)
+ if (universe.currentView.physicalEnvironment != null) {
+ audioDevice = universe.currentView.physicalEnvironment.audioDevice;
+ if (audioDevice != null) {
+ if (audioDevice instanceof AudioDevice3DL2) {
+ audioDevice3DL2 = (AudioDevice3DL2)audioDevice;
+ }
+ if (audioDevice instanceof AudioDevice3D) {
+ audioDevice3D = (AudioDevice3D)audioDevice;
+ }
+ else { // audioDevice is only an instance of AudioDevice
+ if (internalErrors)
+ debugPrint("AudioDevice implementation not supported");
+ // audioDevice3D should already be null
+ }
+ }
+ else {
+ // if audioDevice is null, clear extended class fields
+ audioDevice3DL2 = null;
+ audioDevice3D = null;
+ }
+ }
+ }
+ if (audioDevice3D == null)
+ return false;
+
+ if (audioDevice3D.getTotalChannels() == 0)
+ return false; // can not render sounds on AudioEngine that has no channels
+
+ return true;
+ }
+
+
+ /**
+ * Clears the fields associated with sample data for this sound.
+ * Assumes soundAtom is non-null, and that non-null atom
+ * would have non-null sound field.
+ */
+ void clearSoundData(SoundSchedulerAtom soundAtom) {
+ if (checkAudioDevice3D() &&
+ soundAtom.sampleId != SoundRetained.NULL_SOUND) {
+ stopSound(soundAtom, false); // force stop of playing sound
+ // Unload sound data from AudioDevice
+ audioDevice3D.clearSound(soundAtom.sampleId);
+ }
+
+ soundAtom.sampleId = SoundRetained.NULL_SOUND;
+ // set load state into atom
+ soundAtom.loadStatus = SoundRetained.LOAD_NULL;
+ // NOTE: setting node load status not 1-to-1 w/actual load;
+ // this is incorrect
+ SoundRetained sound = soundAtom.sound;
+ soundAtom.loadStatus = SoundRetained.LOAD_NULL;
+ soundAtom.soundData = null;
+ sound.changeAtomList(soundAtom, SoundRetained.LOAD_NULL);
+ }
+
+
+ /**
+ * Attempts to load sound data for a particular sound source onto
+ * the chosen/initialized audio device
+ * If this called, it is assumed that SoundRetained.audioDevice is
+ * NOT null.
+ * If an error in loading occurs (an exception is caught,...)
+ * an error is printed out to stderr - an exception is not thrown.
+ * @param soundData descrition of sound source data
+ */
+ // QUESTION: should this method be synchronized?
+ void attachSoundData(SoundSchedulerAtom soundAtom,
+ MediaContainer soundData, boolean forceReload) {
+
+ if (!forceReload && (soundAtom.soundData == soundData)) {
+ return;
+ }
+ SoundRetained sound = soundAtom.sound.sgSound;
+ if (!checkAudioDevice3D()) {
+ if (debugFlag)
+ debugPrint(".attachSoundData audioDevice3D null");
+ soundAtom.loadStatus = SoundRetained.LOAD_PENDING;
+ sound.changeAtomList(soundAtom, SoundRetained.LOAD_PENDING);
+ return;
+ }
+ if (soundAtom.soundData != null) {
+ // clear sound data field for view specific atom NOT sound node
+ clearSoundData(soundAtom);
+ if (soundData == null) {
+ if (debugFlag)
+ debugPrint(".attachSoundData with null soundData");
+ return;
+ }
+ }
+
+ URL url = ((MediaContainerRetained)sound.soundData.retained).url;
+ String path = ((MediaContainerRetained)sound.soundData.retained).urlString;
+ InputStream stream = ((MediaContainerRetained)sound.soundData.retained).inputStream;
+ if (url == null && path == null && stream == null) {
+ if (debugFlag)
+ debugPrint(".attachSoundData with null soundData");
+ // clear non-null sample associated with this soundData
+ if (soundAtom.sampleId != SoundRetained.NULL_SOUND) {
+ clearSoundData(soundAtom);
+ }
+ return;
+ }
+
+ int id;
+ if (sound instanceof ConeSoundRetained)
+ sound.soundType = AudioDevice3D.CONE_SOUND;
+ else if (sound instanceof PointSoundRetained)
+ sound.soundType = AudioDevice3D.POINT_SOUND;
+ else
+ sound.soundType = AudioDevice3D.BACKGROUND_SOUND;
+ if (debugFlag) {
+ debugPrint(".attachSoundData soundType = " + sound.soundType);
+ debugPrint(".attachSoundData this is = " + sound);
+ }
+
+ // Clone the MediaContainer associated with this node and
+ // set the capability bits for this clone to allow access to
+ // all fields; this copy is passed to the audioDevice.
+ // As the fields of the MediaContainer expands, this code must
+ // be appended.
+ MediaContainer cloneMediaContainer = new MediaContainer();
+ cloneMediaContainer.duplicateAttributes(soundData, true);
+ cloneMediaContainer.setCapability(MediaContainer.ALLOW_CACHE_READ);
+ cloneMediaContainer.setCapability(MediaContainer.ALLOW_URL_READ);
+
+ id = audioDevice3D.prepareSound(sound.soundType, cloneMediaContainer);
+ if (debugFlag)
+ debugPrint(".attachSoundData prepareSound returned " + id);
+
+ if (id == SoundRetained.NULL_SOUND) {
+ soundAtom.loadStatus = SoundRetained.LOAD_FAILED;
+ // NOTE: setting node load status not 1-to-1 with actual load;
+ // this is incorrect
+ sound.changeAtomList(soundAtom, SoundRetained.LOAD_FAILED);
+ //System.err.println(path + ": "+ J3dI18N.getString("SoundRetained1"));
+ }
+ else {
+ if (debugFlag)
+ debugPrint(".attachSoundData - sampleId set");
+ soundAtom.sampleId = id;
+
+ // For now loopLength=sampleLength, loop points not supported
+ long duration = audioDevice3D.getSampleDuration(id);
+ soundAtom.sampleLength = duration;
+ soundAtom.loopLength = soundAtom.sampleLength;
+
+ // TODO: for most this will be 0 but not all
+ soundAtom.loopStartOffset = 0;
+ soundAtom.attackLength = 0; // portion of sample before loop section
+ soundAtom.releaseLength = 0; // portion of sample after loop section
+ soundAtom.loadStatus = SoundRetained.LOAD_COMPLETE;
+ soundAtom.soundData = soundData;
+ sound.changeAtomList(soundAtom, SoundRetained.LOAD_COMPLETE);
+ if (debugFlag)
+ debugPrint(" attachSoundData; index = "+soundAtom.sampleId);
+ }
+ }
+
+
+ SoundSchedulerAtom findSoundAtom(SoundRetained node, int nthInstance) {
+ // find nth sound atom in the list of prioritized sounds that
+ // references this sound node
+ // nthInstance=1 would look for first instance
+ if (node == null)
+ return null;
+ SoundSchedulerAtom returnAtom = null;
+ synchronized (prioritizedSounds) {
+ if (!prioritizedSounds.isEmpty()) {
+ SoundSchedulerAtom soundAtom = null;
+ int atomFound = 0;
+ // find sound in list and remove it
+ int arrSize = prioritizedSounds.size();
+ for (int index=0; index<arrSize; index++) {
+ soundAtom = (SoundSchedulerAtom)prioritizedSounds.get(index);
+ if (soundAtom.sound == null)
+ continue;
+ // soundAtom.sound is address of mirror sound not org node
+ if (soundAtom.sound.sgSound == node) {
+ atomFound++;
+ // orginal app node pass into method
+ // QUESTION: is mirror node still correct?
+ // TODO: ensure only mirror nodes passed into method
+ if (atomFound == nthInstance) {
+ returnAtom = soundAtom;
+ break;
+ }
+ }
+ else if (soundAtom.sound.sgSound == node.sgSound) {
+ atomFound++;
+ // store potentially new mirror sound into soundAtom
+ soundAtom.sound = node;
+ if (atomFound == nthInstance) {
+ returnAtom = soundAtom;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return returnAtom;
+ }
+
+
+ /**
+ * 'Dirty' flag == listenerUpdated flag
+ * The ambiguous name 'dirtyFlag' is a legacy from when there was only a
+ * single dirty flag set by Core yet tested and cleared in this scheduler.
+ * These methods specifically set/test/clear the local listenerUpdated flag.
+ */
+ // Called by CanvasViewCache when listener parameter(s) changes
+ void setListenerFlag(int flag) {
+ listenerUpdated |= flag;
+ }
+
+ void clearListenerFlag() {
+ listenerUpdated = 0x0;
+ }
+
+ boolean testListenerFlag() {
+ // Test if any bits are on
+ if (listenerUpdated > 0)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * set dirty flags associated with SoundSchedulerAtom
+ */
+ void setAttribsDirtyFlag(SoundRetained node, int dirtyFlag) {
+ if (debugFlag)
+ debugPrint(".setAttribsDirtyFlag " + node );
+ // find sound atom that references this sound node
+ SoundSchedulerAtom soundAtom = null;
+ for (int i=1; ;i++) {
+ soundAtom = findSoundAtom(node, i);
+ if (soundAtom == null)
+ break;
+ soundAtom.setAttribsDirtyFlag(dirtyFlag);
+ }
+ }
+
+ void setStateDirtyFlag(SoundRetained node, int dirtyFlag) {
+ if (debugFlag)
+ debugPrint(".setStateDirtyFlag " + node );
+ // find sound atom that references this sound node
+ SoundSchedulerAtom soundAtom = null;
+ for (int i=1; ;i++) {
+ soundAtom = findSoundAtom(node, i);
+ if (soundAtom == null)
+ break;
+ soundAtom.setStateDirtyFlag(dirtyFlag);
+ }
+ }
+
+
+ void printAtomState(SoundSchedulerAtom atom) {
+ SoundRetained sound = atom.sound.sgSound;
+ debugPrint(" this atom = " + atom + " ");
+ debugPrint(" references sound = " + sound + " ");
+ debugPrint(" enabled " + atom.enabled);
+ debugPrint(" status " + atom.status);
+ debugPrint(" activated " + atom.activated);
+ debugPrint(" released " + sound.release);
+ debugPrint(" continuous " + sound.continuous);
+ debugPrint(" scheduling " + atom.schedulingAction);
+ }
+
+ // Debug print mechanism for Sound nodes
+
+ static final boolean debugFlag = false;
+ static final boolean internalErrors = false;
+
+ void debugPrint(String message) {
+ if (debugFlag)
+ System.out.println("SS."+message);
+ }
+
+ void processViewSpecificGroupChanged(J3dMessage m) {
+ int component = ((Integer)m.args[0]).intValue();
+ Object[] objAry = (Object[])m.args[1];
+ if (((component & ViewSpecificGroupRetained.ADD_VIEW) != 0) ||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ View v = (View)objAry[0];
+ ArrayList leafList = (ArrayList)objAry[2];
+ // View being added is this view
+ if (v == view) {
+ int size = leafList.size();
+ for (i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof SoundRetained) {
+ nRetainedSounds++;
+ addSound((SoundRetained) obj);
+ }
+ else if (obj instanceof SoundscapeRetained) {
+ auralAttribsChanged = true;
+ }
+ }
+
+ }
+
+ }
+ if (((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0)||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ ArrayList leafList;
+ View v;
+
+ if ((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0) {
+ v = (View)objAry[0];
+ leafList = (ArrayList)objAry[2];
+ }
+ else {
+ v = (View)objAry[4];
+ leafList = (ArrayList)objAry[6];
+ }
+ if (v == view) {
+ int size = leafList.size();
+ for (i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof SoundRetained) {
+ SoundSchedulerAtom soundAtom = null;
+ for (int arrIndx=1; ;arrIndx++) {
+ soundAtom = findSoundAtom((SoundRetained)obj,
+ arrIndx);
+ if (soundAtom == null)
+ break;
+ stopSound(soundAtom, false);
+ }
+ }
+ else if (obj instanceof SoundscapeRetained) {
+ auralAttribsChanged = true;
+ }
+ }
+ }
+ }
+
+ }
+
+ void processBoundingLeafChanged(J3dMessage m) {
+ // Notify all users of this bounding leaf, it may
+ // result in the re-evaluation of the lights/fogs/backgrounds
+ Object[] users = (Object[])(m.args[3]);
+ int i;
+
+ for (i = 0; i < users.length; i++) {
+ LeafRetained leaf = (LeafRetained)users[i];
+ if (leaf instanceof SoundRetained && universe.soundStructure.isSoundScopedToView(leaf, view)) {
+ auralAttribsChanged = true;
+ }
+ else if (leaf instanceof SoundscapeRetained && universe.soundStructure.isSoundscapeScopedToView(leaf, view)){
+ auralAttribsChanged = true;
+ }
+ }
+ }
+
+ void cleanup() {
+ // clean up any messages that are queued up, since they are
+ // irrelevant
+ // clearMessages();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SoundSchedulerAtom.java b/src/classes/share/javax/media/j3d/SoundSchedulerAtom.java
new file mode 100644
index 0000000..224d807
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SoundSchedulerAtom.java
@@ -0,0 +1,694 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * A SoundSchedulerAtom is the smallest object representing a Sound within
+ * SoundScheduler. This class contains View-Depedent fields. Some of these
+ * fields may appear to over lap fields in the Sound Node classes, but
+ * remember that the Sound Node fields are universal, user-defined fields
+ * and do not take into account specific Audio Device view-dependent
+ * conditions.
+ */
+
+class SoundSchedulerAtom extends Object {
+
+ /**
+ * The mirror sound node component of this sound scheduler atom
+ */
+ SoundRetained sound = null;
+
+ /**
+ * MediaContainer currently loaded for this atom
+ */
+ MediaContainer soundData = null;
+
+ // Maintain continuously playing silent sound sources.
+ long startTime = 0;
+ long endTime = 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
+
+ int loadStatus = SoundRetained.LOAD_NULL;
+ boolean playing = false;
+ int numberChannels = 0;
+
+ /**
+ * Is this sound in an active scheduling region
+ */
+ boolean activated = false;
+
+ /**
+ * Switch for turning sound on or off while the sound is "active"
+ */
+ static final int OFF = 0;
+ static final int ON = 1;
+ static final int PENDING_ON = 2;
+ static final int PENDING_OFF = 3;
+ int enabled = OFF;
+
+ /**
+ * Switch for muting and unmuting sound while it is playing
+ */
+ static final int UNMUTED = 0;
+ static final int MUTED = 1;
+ static final int PENDING_UNMUTE = 2;
+ static final int PENDING_MUTE = 3;
+ int muted = UNMUTED;
+
+ /**
+ * Switch for pausing and unpausing sound while it is playing
+ */
+ static final int UNPAUSED = 0; // or resumed
+ static final int PAUSED = 1;
+ static final int PENDING_UNPAUSE = 2; // or pending resume
+ static final int PENDING_PAUSE = 3;
+ int paused = UNPAUSED;
+
+
+ /**
+ * Pending action for this sound determined by the SoundScheduler
+ */
+ static final int DO_NOTHING = 0;
+ static final int LEAVE_OFF = 1;
+ static final int LEAVE_SILENT = 2;
+ static final int LEAVE_AUDIBLE = 3;
+ static final int LEAVE_PAUSED = 4;
+
+ static final int RESTART_AUDIBLE = 5;
+ static final int START_AUDIBLE = 6;
+ static final int RESTART_SILENT = 7;
+ static final int START_SILENT = 8;
+
+ static final int MAKE_AUDIBLE = 11;
+ static final int MAKE_SILENT = 12;
+ static final int PAUSE_AUDIBLE = 13;
+ static final int PAUSE_SILENT = 14;
+ static final int RESUME_AUDIBLE = 15;
+ static final int RESUME_SILENT = 16;
+ static final int TURN_OFF = 17;
+ static final int UPDATE = 18;
+ static final int COMPLETE = 19;
+ int schedulingAction = DO_NOTHING;
+
+ /**
+ * This status flag is used for sound scheduling
+ */
+ static final int SOUND_OFF = 0; // The sound is not playing
+ static final int SOUND_AUDIBLE = 1; // The sound is potentially audible
+ static final int SOUND_SILENT = 2; // The sound is playing silently
+ static final int SOUND_PAUSED = 3; // The sound is playing silently
+ static final int SOUND_COMPLETE = 4; // The sound is finished playing
+ int status = SOUND_OFF;
+
+ // Sound atoms have two dirty flags: attribsDirty for sound node fields
+ // and stateDirty for changes to sound state not reflected by sound fields.
+ // When the field/parameter associated with the dirty bit has been:
+ // passed to all SoundSchedulers to update sound rendering or 'run' state
+ // the bit for that field is cleared by the SoundStructure thread.
+
+ /**
+ * attribsDirty bit field
+ * This bitmask is set when sound node attribute is changed by the user.
+ */
+ int attribsDirty = 0x0000;
+
+ /**
+ * stateDirty bit field
+ * This bitmask is set when scene graph state is changed.
+ */
+ int stateDirty = 0x0000;
+
+ // Load Sound Data Status maintained in SoundRetained class
+
+ /**
+ * Identifiers of sample associated with sound source
+ */
+ int sampleId = SoundRetained.NULL_SOUND;
+
+ /**
+ * reference to Sound Scheduler this atom is associated with
+ */
+ SoundScheduler soundScheduler = null;
+
+
+ /**
+ * Calculate absolute time at which sample completes
+ * Checks playing flag denoting if sound is started already or not:
+ * false - calcalutes endTime in relation to startTime
+ * true - re-calculates endTime based on current position in
+ * loop portion of sample plus release length
+ */
+ synchronized void calculateEndTime() {
+ SoundRetained sgSound = sound.sgSound;
+ int loops = sgSound.loopCount;
+ if (debugFlag)
+ debugPrint("calculateEndTime: loop count = " + loops);
+ // test lengths for <= 0; this includes DURATION_UNKNOWN
+ if ( (sampleLength <= 0 || loopLength <= 0 || loops < 0 )
+// QUESTION: removed? but what was this trying to avoid
+// changing endTime when that is already set?
+// but what happens when user changes LoopCount AFTER
+// sound is started - should be able to do this
+// && (enabled == OFF || enabled == PENDING_OFF)
+ ) {
+ endTime = -1;
+ if (debugFlag)
+ debugPrint("calculateEndTime: set to -1");
+ }
+ else {
+// QUESTION: if "&& playing" is in above test; won't we have to test for
+// length unknown and loop = -1??
+ if (playing && (startTime > 0)) {
+ endTime = startTime + attackLength +
+ (loopLength * (loops+1)) + releaseLength; if (debugFlag)
+ debugPrint("calculateEndTime: isPlaying so = " + endTime); }
+ else {
+ // Called when release flag is true
+ // Determine where within the loop portion sample the
+ // sound is currently playing, then set endTime to
+ // play remaining portion of loop portion plus the
+ // release portion.
+ long currentTime = System.currentTimeMillis();
+ endTime = currentTime + ( (loopLength -
+ ((currentTime - startTime - attackLength) % loopLength)) +
+ releaseLength );
+ if (debugFlag)
+ debugPrint("calculateEndTime: NOT Playing so = " + endTime);
+ }
+ }
+ }
+
+
+ void enable(boolean enabled) {
+ if (enabled) {
+ setEnableState(PENDING_ON);
+ if (debugFlag)
+ debugPrint(" enableSound calls soundAtom " +
+ this + " setEnableState PENDING_ON");
+ }
+ else {
+ setEnableState(PENDING_OFF);
+ if (debugFlag)
+ debugPrint(" enableSound calls soundAtom " +
+ this + " setEnableState PENDING_OFF");
+ }
+ }
+
+
+ void mute(boolean muted) {
+ if (muted) {
+ setMuteState(PENDING_MUTE);
+ if (debugFlag)
+ debugPrint(" muteSound() calls soundAtom " +
+ this + " setMuteState PENDING_ON");
+ }
+ else {
+ setMuteState(PENDING_UNMUTE);
+ if (debugFlag)
+ debugPrint(" muteSound() calls soundAtom " +
+ this + " setMuteState PENDING_UNMUTE");
+ }
+ }
+
+ void pause(boolean paused) {
+ if (paused) {
+ setPauseState(PENDING_PAUSE);
+ if (debugFlag)
+ debugPrint(this + ".pause calls setPauseState(PENDING_PAUSE)");
+ }
+ else {
+ setPauseState(PENDING_UNPAUSE);
+ if (debugFlag)
+ debugPrint(this +".pause calls setPauseState(PENDING_UNPAUSE)");
+ }
+ }
+
+
+// TODO: remove this
+// just set the state after debug no longer needed
+ void setEnableState(int state) {
+ enabled = state;
+ switch (state) {
+ case PENDING_ON:
+ if (debugFlag)
+ debugPrint("set enabled to PENDING_ON");
+ break;
+ case ON:
+ if (debugFlag)
+ debugPrint("set enabled to ON");
+ break;
+ case PENDING_OFF:
+ if (debugFlag)
+ debugPrint("set enabled to PENDING_OFF");
+ break;
+ case OFF:
+ if (debugFlag)
+ debugPrint("set enabled to OFF");
+ break;
+ default:
+ if (debugFlag)
+ debugPrint("state = " + state);
+ break;
+ }
+ }
+
+// TODO: remove this
+// just set the state after debug no longer needed
+ void setMuteState(int state) {
+ muted = state;
+ switch (state) {
+ case PENDING_MUTE:
+ if (debugFlag)
+ debugPrint("set mute to PENDING_MUTE");
+ break;
+ case MUTED:
+ if (debugFlag)
+ debugPrint("set mute to MUTE");
+ break;
+ case PENDING_UNMUTE:
+ if (debugFlag)
+ debugPrint("set mute to PENDING_UNMUTE");
+ break;
+ case UNMUTED:
+ if (debugFlag)
+ debugPrint("set mute to UNMUTE");
+ break;
+ default:
+ if (debugFlag)
+ debugPrint("state = " + state);
+ break;
+ }
+ }
+
+// TODO: remove this
+// just set the state after debug no longer needed
+ void setPauseState(int state) {
+ paused = state;
+ switch (state) {
+ case PENDING_PAUSE:
+ if (debugFlag)
+ debugPrint("set pause to PENDING_PAUSE");
+ break;
+ case PAUSED:
+ if (debugFlag)
+ debugPrint("set pause to PAUSE");
+ break;
+ case PENDING_UNPAUSE:
+ if (debugFlag)
+ debugPrint("set pause to PENDING_UNPAUSE");
+ break;
+ case UNPAUSED:
+ if (debugFlag)
+ debugPrint("set pause to UNPAUSE");
+ break;
+ default:
+ if (debugFlag)
+ debugPrint("state = " + state);
+ break;
+ }
+ }
+
+
+ /**
+ * calcActiveSchedAction()
+ * Calculate Sound Scheduler Action for Active sound (it's region
+ * intersects the viewPlatform).
+ *
+ * A big switch testing various SoundRetained fields to determine
+ * what SoundScheduler action to perform when sound is Active
+ * set sound active flag true
+ * switch on enable value, to set pending scheduling action
+ * depending on continuous and release flags and sound status
+ */
+ synchronized int calcActiveSchedAction() {
+ SoundRetained sgSound = sound.sgSound;
+ int action = DO_NOTHING;
+ activated = true;
+ switch (enabled) {
+ case PENDING_ON:
+ setEnableState(ON);
+ if (debugFlag)
+ debugPrint(" calcActiveSchedAction: PENDING_ON");
+ if (status == SOUND_OFF ||
+ status == SOUND_PAUSED)
+ action = START_AUDIBLE;
+ else
+ action = RESTART_AUDIBLE;
+ break;
+ case ON:
+ if (debugFlag)
+ debugPrint(" calcActiveSchedAction: ON");
+ if (status == SOUND_OFF)
+ // should NOT see this, but if we do...
+ action = START_AUDIBLE;
+ else if (status == SOUND_SILENT)
+ action = MAKE_AUDIBLE;
+ else // status == SOUND_AUDIBLE
+ action = LEAVE_AUDIBLE;
+ break;
+ case PENDING_OFF:
+ setEnableState(OFF);
+ if (debugFlag)
+ debugPrint("enable = " + enabled +
+ "enabled set to OFF");
+ // fail thru
+ case OFF:
+ // QUESTION: Why would enable status ever be OFF yet
+ // status SOUND_AUDIBLE or _SILENT?
+ if (status == SOUND_AUDIBLE) {
+ if (sgSound.release) {
+ if (debugFlag)
+ debugPrint("enable = " + enabled +
+ ", AUDIBLE, released, " +
+ "action <- LEAVE_AUDIBLE");
+ if (enabled == PENDING_OFF) {
+ // re-calculate EndTime
+ calculateEndTime();
+ }
+ action = LEAVE_AUDIBLE;
+ }
+ else {
+ if (debugFlag)
+ debugPrint("enable = " + enabled +
+ ", AUDIBLE, not released, "+
+ "action <- TURN_OFF");
+ action = TURN_OFF;
+ }
+ }
+ else if (status == SOUND_SILENT) {
+ if (sgSound.release) {
+ if (debugFlag)
+ debugPrint("enable = " + enabled +
+ ", SILENT, released, " +
+ "action <- MAKE_AUDIBLE");
+ // re-calculate EndTime
+ calculateEndTime();
+ action = MAKE_AUDIBLE;
+ }
+ else {
+ if (debugFlag)
+ debugPrint("enable = " + enabled +
+ ", SILENT, not released, " +
+ "action <- TURN_OFF");
+ action = TURN_OFF;
+ }
+ }
+ else { // status == SOUND_OFF
+ action = LEAVE_OFF;
+ }
+ break;
+ } // switch on enabled flag
+
+ // if sounds pause state is PENDING_PAUSE modify action to perform.
+ if (paused == PENDING_PAUSE) {
+ // if this pause state is set to PAUSE then assume the sound is
+ // already paused, so any incoming action that leave the state
+ // as it already is, leaves the sound paused.
+ if (debugFlag)
+ debugPrint(" PENDING_PAUSE");
+ switch (action) {
+ case MAKE_AUDIBLE:
+ case LEAVE_AUDIBLE:
+ case RESUME_AUDIBLE:
+ action = PAUSE_AUDIBLE;
+ break;
+ case MAKE_SILENT:
+ case LEAVE_SILENT:
+ case RESUME_SILENT:
+ action = PAUSE_SILENT;
+ break;
+ default:
+ // don't change action for any other cases
+ break;
+ }
+ }
+ // if sounds pause state is PENDING_UNPAUSE modify action
+ else if (paused == PENDING_UNPAUSE) {
+ debugPrint(" PENDING_UNPAUSE");
+ switch (action) {
+ // When restart (audible or silent) pause flag is checked and
+ // explicitly set in SoundScheduler
+ case MAKE_AUDIBLE:
+ case LEAVE_AUDIBLE:
+ case PAUSE_AUDIBLE:
+ action = RESUME_AUDIBLE;
+ break;
+ case MAKE_SILENT:
+ case LEAVE_SILENT:
+ case PAUSE_SILENT:
+ action = RESUME_SILENT;
+ break;
+ default:
+ // don't change action for any other cases
+ break;
+ }
+ }
+ return(action);
+ } // end of calcActiveSchedAction
+
+
+ /**
+ * calcInactiveSchedAction()
+ * Calculate Sound Scheduler action for Inactive sound
+ *
+ * A big switch testing various SoundRetained fields to determine
+ * what SoundScheduler action to perform when sound is inactive.
+ * set sound active flag false
+ * switch on enable value, to set pending scheduling action
+ * depending on continuous and release flags and sound status
+ */
+ synchronized int calcInactiveSchedAction() {
+ int action = DO_NOTHING;
+ SoundRetained sgSound = sound.sgSound;
+
+ // Sound is Inactive
+ // Generally, sound is OFF unless continuous flag true
+ // then sound is silently playing if on.
+ activated = false;
+
+ switch (enabled) {
+ case PENDING_ON:
+ if (debugFlag)
+ debugPrint(" calcInactiveSchedAction: PENDING_ON ");
+ setEnableState(ON);
+ if (sgSound.continuous) {
+ if (status == SOUND_OFF)
+ action = START_SILENT;
+ else // status == SOUND_AUDIBLE or SOUND_SILENT
+ action = RESTART_SILENT;
+ }
+ else { // sound is not continuous
+ if (status == SOUND_OFF)
+ action = LEAVE_OFF;
+ else // status == SOUND_SILENT || SOUND_AUDIBLE
+ action = TURN_OFF;
+ }
+ break;
+ case ON:
+ if (debugFlag)
+ debugPrint(" calcInactiveSchedActio: ON ");
+ if (sgSound.continuous) {
+ if (status == SOUND_AUDIBLE)
+ action = MAKE_SILENT;
+ else if (status == SOUND_OFF)
+ action = START_SILENT;
+ else // status == SOUND_SILENT
+ action = LEAVE_SILENT;
+ }
+ else { // sound is not continuous
+ // nothing to do if already off
+ if (status == SOUND_OFF)
+ action = LEAVE_OFF;
+ else // status == SOUND_SILENT or SOUND_AUDIBLE
+ action = TURN_OFF;
+ }
+ break;
+ case PENDING_OFF:
+ setEnableState(OFF);
+ if (debugFlag)
+ debugPrint("Enable = " + enabled +
+ "enabled set to OFF");
+ // fall thru
+
+ case OFF:
+ if (sgSound.release && sgSound.continuous) {
+ if (enabled == PENDING_OFF) {
+ // re-calculate EndTime
+ calculateEndTime();
+ }
+ if (status == SOUND_AUDIBLE) {
+ if (debugFlag)
+ debugPrint("Enable = " + enabled +
+ ", AUDIBLE, released & continuous - " +
+ "action <- MAKE_SILENT");
+ action = MAKE_SILENT;
+ }
+ else if (status == SOUND_SILENT) {
+ if (debugFlag)
+ debugPrint("Enable = " + enabled +
+ ", SILENT, released & continuous - " +
+ "action <- TURN_OFF");
+ action = LEAVE_SILENT;
+ }
+ else {
+ if (debugFlag)
+ debugPrint("Enable = " + enabled +
+ ", already OFF, action <- LEAVE_OFF");
+ action = LEAVE_OFF;
+ }
+ }
+ else { // continuous and release flag not both true
+ if (status == SOUND_OFF) {
+ if (debugFlag)
+ debugPrint("Enable = " + enabled +
+ ", already OFF, action <- LEAVE_OFF");
+ action = LEAVE_OFF;
+ }
+ else {
+ if (debugFlag)
+ debugPrint("Enable = " + enabled +
+ ", not already OFF, action <- TURN_OFF");
+ action = TURN_OFF;
+ }
+ }
+ break;
+ default:
+ break;
+ } // switch
+
+ // if sounds pause state is PENDING_PAUSE modify action to perform.
+ if (paused == PENDING_PAUSE) {
+ // if this pause state is set to PAUSE then assume the sound is
+ // already paused, so any incoming action that leave the state
+ // as it already is, leaves the sound paused.
+ switch (action) {
+ case MAKE_SILENT:
+ case LEAVE_SILENT:
+ case RESUME_SILENT:
+ action = PAUSE_SILENT;
+ break;
+ default:
+ // don't change action for any other cases
+ break;
+ }
+ }
+ // if sounds pause state is PENDING_UNPAUSE modify action
+ else if (paused == PENDING_UNPAUSE) {
+ switch (action) {
+ case LEAVE_SILENT:
+ action = RESUME_SILENT;
+ break;
+ default:
+ // don't change action for any other cases
+ break;
+ }
+ }
+ return (action);
+ } // end of calcInactiveSchedAction
+
+// TODO isPLaying
+// TODO setLoadingState
+
+ // Debug print mechanism for Sound nodes
+ static final boolean debugFlag = false;
+ static final boolean internalErrors = false;
+
+ void debugPrint(String message) {
+ if (debugFlag) {
+ System.out.println(message);
+ }
+ }
+
+
+ /**
+ * Set bit(s) in soundDirty field
+ * @param binary flag denotes bits to set ON
+ */
+ void setAttribsDirtyFlag(int bitFlag) {
+ attribsDirty |= bitFlag;
+ if (debugFlag)
+ debugPrint("setAttribsDirtyFlag = " + bitFlag);
+ return ;
+ }
+ void setStateDirtyFlag(int bitFlag) {
+ stateDirty |= bitFlag;
+ if (debugFlag)
+ debugPrint("setStateDirtyFlag = " + bitFlag);
+ return ;
+ }
+
+ /**
+ * Clear sound's dirty flag bit value.
+ * @param binary flag denotes bits to set OFF
+ */
+ void clearAttribsDirtyFlag(int bitFlag) {
+ if (debugFlag)
+ debugPrint("clearAttribsDirtyFlag = " + bitFlag);
+ attribsDirty &= ~bitFlag;
+ return ;
+ }
+ void clearAttribsDirtyFlag() {
+ // clear all bits
+ if (debugFlag)
+ debugPrint("clearAttribsDirtyFlag = ALL");
+ attribsDirty = 0x0;
+ return ;
+ }
+ void clearStateDirtyFlag(int bitFlag) {
+ if (debugFlag)
+ debugPrint("clearStateDirtyFlag = " + bitFlag);
+ stateDirty &= ~bitFlag;
+ return ;
+ }
+ void clearStateDirtyFlag() {
+ if (debugFlag)
+ debugPrint("clearStateDirtyFlag = ALL");
+ stateDirty = 0x0;
+ return ;
+ }
+
+
+ /**
+ * Test sound's dirty flag bit(s)
+ * @param field denotes which bitmask to set into
+ * @param binary flag denotes bits to set Test
+ * @return true if bit(s) in bitFlag are set dirty (on)
+ */
+ boolean testDirtyFlag(int field, int bitFlag) {
+ if ((field & bitFlag) > 0)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Test sound's dirty flags for ANY bits on
+ * @return true if any bit in bitFlag is flipped on
+ */
+ boolean testDirtyFlags() {
+ if ((attribsDirty & 0xFFFF) > 0)
+ return true;
+ else if ((stateDirty & 0xFFFF) > 0)
+ return true;
+ else
+ return false;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SoundStructure.java b/src/classes/share/javax/media/j3d/SoundStructure.java
new file mode 100644
index 0000000..ac36949
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SoundStructure.java
@@ -0,0 +1,721 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * A sound structure is a object that organizes Sounds and
+ * soundscapes.
+ * This structure parallels the RenderingEnv structure and
+ * is used for sounds
+ */
+
+class SoundStructure extends J3dStructure {
+ /**
+ * The list of Sound nodes
+ */
+ UnorderList nonViewScopedSounds = new UnorderList(SoundRetained.class);
+ HashMap viewScopedSounds = new HashMap();
+
+ /**
+ * The list of Soundscapes
+ */
+ UnorderList nonViewScopedSoundscapes = new UnorderList(SoundscapeRetained.class);
+ HashMap viewScopedSoundscapes = new HashMap();
+
+ /**
+ * The list of view platforms
+ */
+ UnorderList viewPlatforms = new UnorderList(ViewPlatformRetained.class);
+
+ /**
+ * A bounds used for getting a view platform scheduling BoundingSphere
+ */
+ BoundingSphere tempSphere = new BoundingSphere();
+ BoundingSphere vpsphere = new BoundingSphere();
+
+ // ArrayList of leafRetained object whose mirrorObjects
+ // should be updated
+ ArrayList objList = new ArrayList();
+
+ // ArrayList of leafRetained object whose boundingleaf xform
+ // should be updated
+ ArrayList xformChangeList = new ArrayList();
+
+ // ArrayList of switches that have changed
+ ArrayList switchChangeLeafNodes = new ArrayList();
+ ArrayList switchChangeLeafMasks = new ArrayList();
+
+ // variables for processing transform messages
+ boolean transformMsg = false;
+ UpdateTargets targets = null;
+
+ /**
+ * This constructor does nothing
+ */
+ SoundStructure(VirtualUniverse u) {
+ super(u, J3dThread.UPDATE_SOUND);
+ if (debugFlag)
+ debugPrint("SoundStructure constructed");
+ }
+
+ void processMessages(long referenceTime) {
+ J3dMessage messages[] = getMessages(referenceTime);
+ int nMsg = getNumMessage();
+ J3dMessage m;
+
+ if (nMsg <= 0) {
+ return;
+ }
+
+
+ for (int i=0; i < nMsg; i++) {
+ m = messages[i];
+
+ switch (m.type) {
+ case J3dMessage.INSERT_NODES :
+ // Prioritize retained and non-retained sounds for this view
+ insertNodes(m);
+ break;
+ case J3dMessage.REMOVE_NODES:
+ removeNodes(m);
+ break;
+ case J3dMessage.SOUND_ATTRIB_CHANGED:
+ changeNodeAttrib(m);
+ break;
+ case J3dMessage.SOUND_STATE_CHANGED:
+ changeNodeState(m);
+ break;
+ case J3dMessage.SOUNDSCAPE_CHANGED:
+ case J3dMessage.AURALATTRIBUTES_CHANGED:
+ // TODO: this needs to be changed
+ changeNodeAttrib(m);
+ break;
+ case J3dMessage.TRANSFORM_CHANGED:
+ transformMsg = true;
+ break;
+ case J3dMessage.SWITCH_CHANGED:
+ processSwitchChanged(m);
+ // may need to process dirty switched-on transform
+ if (universe.transformStructure.getLazyUpdate()) {
+ transformMsg = true;
+ }
+ break;
+ case J3dMessage.VIEWSPECIFICGROUP_CHANGED:
+ updateViewSpecificGroupChanged(m);
+ break;
+ // TODO: case J3dMessage.BOUNDINGLEAF_CHANGED
+ }
+
+ /*
+ // NOTE: this should already be handled by including/ORing
+ // SOUND_SCHEDULER in targetThread for these message types!!
+ // Dispatch a message about a sound change
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+
+ // QUESTION: can I just use this message to pass to all the Sound Bins
+ for (int k=viewPlatforms.arraySize()- 1; k>=0; k--) {
+ View[] views = vpLists[k].getViewList();
+ for (int j=(views.length-1); j>=0; j--) {
+ View v = (View)(views[j]);
+ m.view = v;
+ VirtualUniverse.mc.processMessage(m);
+ }
+ }
+ */
+ m.decRefcount();
+ }
+ if (transformMsg) {
+ targets = universe.transformStructure.getTargetList();
+ updateTransformChange(targets, referenceTime);
+ transformMsg = false;
+ targets = null;
+ }
+
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ void insertNodes(J3dMessage m) {
+ Object[] nodes = (Object[])m.args[0];
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ Object node;
+
+ for (int i=0; i<nodes.length; i++) {
+ node = (Object) nodes[i];
+ if (node instanceof SoundRetained) {
+ addNonScopedSound((SoundRetained) node);
+ }
+ if (node instanceof SoundscapeRetained) {
+ addNonSoundscape((SoundscapeRetained) node);
+ }
+ }
+ // Handle ViewScoped Nodes
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+ for (int i = 0; i < size; i++) {
+ node = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ int vsize = vl.size();
+ if (node instanceof SoundRetained) {
+ ((SoundRetained)node).isViewScoped = true;
+ for (int k = 0; k < vsize; k++) {
+ View view = (View) vl.get(k);
+ addScopedSound((SoundRetained) node, view);
+ }
+ }
+ else if (node instanceof SoundscapeRetained) {
+ ((SoundscapeRetained)node).isViewScoped = true;
+ for (int k = 0; k < vsize; k++) {
+ View view = (View) vl.get(k);
+ addScopedSoundscape((SoundscapeRetained) node, view);
+ }
+ }
+ }
+ }
+ /*
+ // TODO:
+ if (node instanceof AuralAttributesRetained) {
+ }
+ else if (node instanceof ViewPlatformRetained) {
+ addViewPlatform((ViewPlatformRetained) node);
+ }
+ */
+ }
+
+
+ /**
+ * Add sound to sounds list.
+ */
+ void addScopedSound(SoundRetained mirSound, View view) {
+ if (debugFlag)
+ debugPrint("SoundStructure.addSound()");
+ ArrayList l = (ArrayList) viewScopedSounds.get(view);
+ if (l == null) {
+ l = new ArrayList();
+ viewScopedSounds.put(view, l);
+ }
+ l.add(mirSound);
+ } // end addSound()
+
+ void addNonScopedSound(SoundRetained mirSound) {
+ if (debugFlag)
+ debugPrint("SoundStructure.addSound()");
+ nonViewScopedSounds.add(mirSound);
+ } // end addSound()
+
+
+ void addScopedSoundscape(SoundscapeRetained soundscape, View view) {
+ if (debugFlag)
+ debugPrint("SoundStructure.addSoundscape()");
+ ArrayList l = (ArrayList) viewScopedSoundscapes.get(view);
+ if (l == null) {
+ l = new ArrayList();
+ viewScopedSoundscapes.put(view, l);
+ }
+ l.add(soundscape);
+ }
+
+ void addNonSoundscape(SoundscapeRetained soundscape) {
+ if (debugFlag)
+ debugPrint("SoundStructure.addSoundscape()");
+ nonViewScopedSoundscapes.add(soundscape);
+ }
+
+
+ void removeNodes(J3dMessage m) {
+ Object[] nodes = (Object[])m.args[0];
+ ArrayList viewScopedNodes = (ArrayList)m.args[3];
+ ArrayList scopedNodesViewList = (ArrayList)m.args[4];
+ Object node;
+
+ for (int i=0; i<nodes.length; i++) {
+ node = (Object) nodes[i];
+ if (node instanceof SoundRetained) {
+ deleteNonScopedSound((SoundRetained) node);
+ }
+ if (node instanceof SoundscapeRetained) {
+ deleteNonScopedSoundscape((SoundscapeRetained) node);
+ }
+ }
+ // Handle ViewScoped Nodes
+ if (viewScopedNodes != null) {
+ int size = viewScopedNodes.size();
+ int vlsize;
+ for (int i = 0; i < size; i++) {
+ node = (NodeRetained)viewScopedNodes.get(i);
+ ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
+ // If the node object is scoped to this view, then ..
+ int vsize = vl.size();
+
+ if (node instanceof SoundRetained) {
+ ((SoundRetained)node).isViewScoped = false;
+ for (int k = 0; k < vsize; k++) {
+ View view = (View) vl.get(k);
+ deleteScopedSound((SoundRetained) node, view);
+ }
+ }
+ else if (node instanceof SoundscapeRetained) {
+ ((SoundscapeRetained)node).isViewScoped = false;
+ for (int k = 0; k < vsize; k++) {
+ View view = (View) vl.get(k);
+ deleteScopedSoundscape((SoundscapeRetained) node, view);
+ }
+ }
+ }
+ }
+ }
+
+ void deleteNonScopedSound(SoundRetained sound) {
+ if (!nonViewScopedSounds.isEmpty()) {
+ // find sound in list and remove it
+ int index = nonViewScopedSounds.indexOf(sound);
+ nonViewScopedSounds.remove(index);
+ }
+ }
+
+ void deleteNonScopedSoundscape(SoundscapeRetained soundscape) {
+ boolean error = nonViewScopedSoundscapes.remove(soundscape);
+ }
+
+ void deleteScopedSound(SoundRetained sound, View view) {
+ ArrayList l = (ArrayList) viewScopedSounds.get(view);
+ if (!l.isEmpty()) {
+ // find sound in list and remove it
+ int index = l.indexOf(sound);
+ l.remove(index);
+ }
+ if (l.isEmpty())
+ viewScopedSounds.remove(view);
+ }
+
+ void deleteScopedSoundscape(SoundscapeRetained soundscape, View view) {
+ ArrayList l = (ArrayList) viewScopedSoundscapes.get(view);
+ if (!l.isEmpty()) {
+ // find sound in list and remove it
+ int index = l.indexOf(soundscape);
+ l.remove(index);
+ }
+ if (l.isEmpty())
+ viewScopedSoundscapes.remove(view);
+
+ }
+
+ void changeNodeAttrib(J3dMessage m) {
+ int attribDirty;
+ Object node = m.args[0];
+ Object value = m.args[1];
+ if (debugFlag)
+ debugPrint("SoundStructure.changeNodeAttrib:");
+
+ if (node instanceof SoundRetained) {
+ attribDirty = ((Integer)value).intValue();
+ if (debugFlag)
+ debugPrint(" Sound node dirty bit = " + attribDirty);
+ if ((attribDirty & SoundRetained.PRIORITY_DIRTY_BIT) > 0) {
+ // TODO: shuffle in SoundScheduler
+ /*
+ shuffleSound((SoundRetained) node);
+ */
+ }
+ if ((attribDirty & SoundRetained.SOUND_DATA_DIRTY_BIT) > 0) {
+ loadSound((SoundRetained) node, true);
+ }
+ ((SoundRetained)node).updateMirrorObject(m.args);
+ }
+ if (node instanceof SoundscapeRetained) {
+/*
+ attribDirty = ((Integer)value).intValue();
+ if (((attribDirty & SoundscapeRetained.BOUNDING_LEAF_CHANGED) != 0) ||
+ ((attribDirty & SoundscapeRetained.APPLICATION_BOUNDS_CHANGED) != 0) ) {
+*/
+ ((SoundscapeRetained)node).updateTransformChange();
+/*
+ }
+*/
+// TODO: have no dirty flag for soundscape, just auralAttributes...
+// what if reference to AA changes in soundscape???
+ }
+
+ }
+
+
+ void changeNodeState(J3dMessage m) {
+ int stateDirty;
+ Object node = m.args[0];
+ Object value = m.args[1];
+ if (debugFlag)
+ debugPrint("SoundStructure.changeNodeState:");
+ if (node instanceof SoundRetained) {
+ stateDirty = ((Integer)value).intValue();
+ if (debugFlag)
+ debugPrint(" Sound node dirty bit = " + stateDirty);
+ if ((stateDirty & SoundRetained.LIVE_DIRTY_BIT) > 0) {
+ loadSound((SoundRetained) node, false);
+ }
+ if ((stateDirty & SoundRetained.ENABLE_DIRTY_BIT) > 0) {
+ enableSound((SoundRetained) node);
+ }
+ ((SoundRetained)node).updateMirrorObject(m.args);
+ }
+ }
+
+ // return true if one of ViewPlatforms intersect region
+ boolean intersect(Bounds region) {
+ if (region == null)
+ return false;
+
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+
+ for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) {
+ vpLists[i].schedSphere.getWithLock(tempSphere);
+ if (tempSphere.intersect(region)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void loadSound(SoundRetained sound, boolean forceLoad) {
+// QUESTION: should not be calling into soundScheduler directly???
+ MediaContainer mediaContainer = sound.getSoundData();
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+
+ for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) {
+ View[] views = vpLists[i].getViewList();
+ for (int j=(views.length-1); j>=0; j--) {
+ View v = (View)(views[j]);
+// TODO: Shouldn't this be done with messages??
+ v.soundScheduler.loadSound(sound, forceLoad);
+ }
+ }
+ }
+
+ void enableSound(SoundRetained sound) {
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+ for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) {
+ View[] views = vpLists[i].getViewList();
+ for (int j=(views.length-1); j>=0; j--) {
+ View v = (View)(views[j]);
+ v.soundScheduler.enableSound(sound);
+ }
+ }
+ }
+
+ void muteSound(SoundRetained sound) {
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+ for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) {
+ View[] views = vpLists[i].getViewList();
+ for (int j=(views.length-1); j>=0; j--) {
+ View v = (View)(views[j]);
+ v.soundScheduler.muteSound(sound);
+ }
+ }
+ }
+
+ void pauseSound(SoundRetained sound) {
+ ViewPlatformRetained vpLists[] = (ViewPlatformRetained [])
+ viewPlatforms.toArray(false);
+ for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) {
+ View[] views = vpLists[i].getViewList();
+ for (int j=(views.length-1); j>=0; j--) {
+ View v = (View)(views[j]);
+ v.soundScheduler.pauseSound(sound);
+ }
+ }
+ }
+
+ void processSwitchChanged(J3dMessage m) {
+ SoundRetained sound;
+ LeafRetained leaf;
+ UnorderList arrList;
+ int size;
+ Object[] nodes;
+
+ UpdateTargets targets = (UpdateTargets)m.args[0];
+ arrList = targets.targetList[Targets.SND_TARGETS];
+
+ if (arrList != null) {
+ size = arrList.size();
+ nodes = arrList.toArray(false);
+
+ for (int i=size-1; i>=0; i--) {
+ leaf = (LeafRetained)nodes[i];
+ sound = (SoundRetained) leaf;
+ if (sound.switchState.currentSwitchOn) {
+ // System.out.println("SoundStructure.switch on");
+ // add To Schedule List
+ } else {
+ // System.out.println("SoundStructure.switch off");
+ // remove From Schedule List
+ }
+ }
+ }
+ }
+
+// How can active flag (based on View orientataion) be set here for all Views?!?
+
+ UnorderList getSoundList(View view) {
+ ArrayList l = (ArrayList)viewScopedSounds.get(view);
+ // No sounds scoped to this view
+ if (l == null)
+ return nonViewScopedSounds;
+ UnorderList newS = (UnorderList) nonViewScopedSounds.clone();
+ int size = l.size();
+ for (int i = 0; i < size; i++) {
+ newS.add(l.get(i));
+ }
+ return newS;
+
+ }
+ UnorderList getSoundscapeList(View view) {
+ ArrayList l = (ArrayList)viewScopedSoundscapes.get(view);
+ // No sounds scoped to this view
+ if (l == null)
+ return nonViewScopedSoundscapes;
+ UnorderList newS = (UnorderList) nonViewScopedSoundscapes.clone();
+ int size = l.size();
+ for (int i = 0; i < size; i++) {
+ newS.add(l.get(i));
+ }
+ return newS;
+
+ }
+
+
+/*
+// TODO: how is immediate mode handled? below code taken from SoundSchedule
+// Don't know how we'll process immediate mode sounds;
+// Append immediate mode sounds to live sounds list
+ if (graphicsCtx != null) {
+ synchronized (graphicsCtx.sounds) {
+ nImmedSounds = graphicsCtx.numSounds();
+ numSoundsToProcess = nSounds + nImmedSounds;
+ if (sounds.length < numSoundsToProcess) {
+ // increase the array length of sounds array list
+ // by added 32 elements more than universe list size
+ sounds = new SoundRetained[numSoundsToProcess + 32];
+ }
+ for (int i=0; i<nImmedSounds; i++) {
+ sound = (SoundRetained)((graphicsCtx.getSound(i)).retained); if (debugFlag) {
+ debugPrint("#=#=#= sound at " + sound);
+ printSoundState(sound);
+ }
+
+ if (sound != null && sound.getInImmCtx()) {
+ // There is no 'mirror' copy of Immediate mode sounds made.
+ // Put a reference to sound node itself in .sgSound field.
+ // For most purposes (except transforms & transformed fields)
+ // Scheduler code will treat live scenegraph sounds and
+ // immediate mode sound the same way.
+ sound.sgSound = sound;
+ sounds[nSounds] = sound;
+ if (debugFlag) {
+ debugPrint("#=#=#= sounds["+nSounds+"] at " +
+ sounds[nSounds]);
+ printSoundState(sounds[nSounds]);
+ }
+ nSounds++;
+ }
+ }
+ } // sync of GraphicsContext3D.sounds list
+ }
+ else { // graphics context not set yet, try setting it now
+ Canvas3D canvas = view.getFirstCanvas();
+ if (canvas != null)
+ graphicsCtx = canvas.getGraphicsContext3D();
+ }
+
+ if (debugFlag) {
+ debugPrint("SoundStructure: number of sounds in scene graph = "+nRetainedSounds);
+ debugPrint("SoundStructure: number of immediate mode sounds = "+nImmedSounds);
+ }
+*/
+
+
+ void updateTransformChange(UpdateTargets targets, long referenceTime) {
+ // QUESTION: how often and when should xformChangeList be processed
+ // node.updateTransformChange() called immediately rather than
+ // waiting for updateObject to be called and process xformChangeList
+ // which apprears to only happen when sound started...
+
+ UnorderList arrList = targets.targetList[Targets.SND_TARGETS];
+ if (arrList != null) {
+ int j,i;
+ Object nodes[], nodesArr[];
+ int size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (j = 0; j<size; j++) {
+ nodes = (Object[])nodesArr[j];
+
+ for (i = 0; i < nodes.length; i++) {
+
+ if (nodes[i] instanceof ConeSoundRetained) {
+ xformChangeList.add(nodes[i]);
+ ConeSoundRetained cnSndNode =
+ (ConeSoundRetained)nodes[i];
+ cnSndNode.updateTransformChange();
+
+ } else if (nodes[i] instanceof PointSoundRetained) {
+ xformChangeList.add(nodes[i]);
+ PointSoundRetained ptSndNode =
+ (PointSoundRetained)nodes[i];
+ ptSndNode.updateTransformChange();
+
+ } else if (nodes[i] instanceof SoundRetained) {
+ xformChangeList.add(nodes[i]);
+ SoundRetained sndNode = (SoundRetained)nodes[i];
+ sndNode.updateTransformChange();
+
+ } else if (nodes[i] instanceof SoundscapeRetained) {
+ xformChangeList.add(nodes[i]);
+ SoundscapeRetained sndScapeNode =
+ (SoundscapeRetained)nodes[i];
+ sndScapeNode.updateTransformChange();
+
+ } else if (nodes[i] instanceof AuralAttributesRetained) {
+ xformChangeList.add(nodes[i]);
+ }
+ }
+ }
+ }
+ }
+
+ // Debug print mechanism for Sound nodes
+ static final boolean debugFlag = false;
+ static final boolean internalErrors = false;
+
+ void debugPrint(String message) {
+ if (debugFlag) {
+ System.out.println(message);
+ }
+ }
+
+
+ boolean isSoundScopedToView(Object obj, View view) {
+ SoundRetained s = (SoundRetained)obj;
+ if (s.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedSounds.get(view);
+ if (!l.contains(s))
+ return false;
+ }
+ return true;
+ }
+
+ boolean isSoundscapeScopedToView(Object obj, View view) {
+ SoundscapeRetained s = (SoundscapeRetained)obj;
+ if (s.isViewScoped) {
+ ArrayList l = (ArrayList)viewScopedSoundscapes.get(view);
+ if (!l.contains(s))
+ return false;
+ }
+ return true;
+ }
+
+ void updateViewSpecificGroupChanged(J3dMessage m) {
+ int component = ((Integer)m.args[0]).intValue();
+ Object[] objAry = (Object[])m.args[1];
+
+ ArrayList soundList = null;
+ ArrayList soundsScapeList = null;
+
+ if (((component & ViewSpecificGroupRetained.ADD_VIEW) != 0) ||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ View view = (View)objAry[0];
+ ArrayList leafList = (ArrayList)objAry[2];
+ int size = leafList.size();
+ // Leaves is non-null only for the top VSG
+ if (size > 0) {
+ // Now process the list of affected leaved
+ for( i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof SoundRetained) {
+ if (soundList == null) {
+ if ((soundList = (ArrayList)viewScopedSounds.get(view)) == null) {
+ soundList = new ArrayList();
+ viewScopedSounds.put(view, soundList);
+ }
+ }
+ soundList.add(obj);
+ }
+ else if (obj instanceof SoundscapeRetained) {
+ if (soundsScapeList == null) {
+ if ((soundsScapeList = (ArrayList)viewScopedSoundscapes.get(view)) == null) {
+ soundsScapeList = new ArrayList();
+ viewScopedSoundscapes.put(view, soundsScapeList);
+ }
+ }
+ soundsScapeList.add(obj);
+ }
+ }
+ }
+ }
+ if (((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0)||
+ ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
+ int i;
+ Object obj;
+ ArrayList leafList;
+ View view;
+
+
+ if ((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0) {
+ view = (View)objAry[0];
+ leafList = (ArrayList)objAry[2];
+ }
+ else {
+ view = (View)objAry[4];
+ leafList = (ArrayList)objAry[6];
+ }
+ int size = leafList.size();
+ // Leaves is non-null only for the top VSG
+ if (size > 0) {
+ // Now process the list of affected leaved
+ for( i = 0; i < size; i++) {
+ obj = leafList.get(i);
+ if (obj instanceof SoundRetained) {
+ if (soundList == null) {
+ soundList = (ArrayList)viewScopedSounds.get(view);
+ }
+ soundList.remove(obj);
+ }
+ if (obj instanceof SoundscapeRetained) {
+ if (soundsScapeList == null) {
+ soundsScapeList = (ArrayList)viewScopedSoundscapes.get(view);
+ }
+ soundsScapeList.remove(obj);
+ }
+ }
+ // If there are no more lights scoped to the view,
+ // remove the mapping
+ if (soundList != null && soundList.size() == 0)
+ viewScopedSounds.remove(view);
+ if (soundsScapeList != null && soundsScapeList.size() == 0)
+ viewScopedSoundscapes.remove(view);
+ }
+
+ }
+
+ }
+
+ void cleanup() {}
+}
diff --git a/src/classes/share/javax/media/j3d/Soundscape.java b/src/classes/share/javax/media/j3d/Soundscape.java
new file mode 100644
index 0000000..ce81e74
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Soundscape.java
@@ -0,0 +1,332 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The Soundscape Leaf Node defines the attributes that characterize the
+ * listener's environment as it pertains to sound. This node defines an
+ * application region and an associated aural attribute component object
+ * that controls reverberation and atmospheric properties that affect sound
+ * source rendering. Multiple Soundscape nodes can be included in a single
+ * scene graph.
+ * <P>
+ * The Soundscape application region, different from a Sound node's scheduling
+ * region, is used to select which Soundscape (and thus which aural attribute
+ * object) is to be applied to the sounds being rendered. This selection is
+ * based on the position of the ViewPlatform (i.e., the listener), not the
+ * position of the sound.
+ * <P>
+ * It will be common that multiple Soundscape regions are contained within a
+ * scene graph. For example, two Soundscape regions within a single space the
+ * listener can move about: a region with a large open area on the right, and
+ * a smaller more constricted, less reverberant area on the left. The rever-
+ * beration attributes for these two regions could be set to approximate their
+ * physical differences so that active sounds are rendered differently depending
+ * on which region the listener is in.
+ */
+public class Soundscape extends Leaf {
+
+ // Constants
+ //
+ // These flags, when enabled using the setCapability method, allow an
+ // application to invoke methods that respectively read and write the
+ // application region and the aural attributes. These capability flags
+ // are enforced only when the node is part of a live or compiled scene
+ // graph.
+
+ /**
+ * For Soundscape component objects, specifies that this object
+ * allows read access to its application bounds
+ */
+ public static final int
+ ALLOW_APPLICATION_BOUNDS_READ = CapabilityBits.SOUNDSCAPE_ALLOW_APPLICATION_BOUNDS_READ;
+
+ /**
+ * For Soundscape component objects, specifies that this object
+ * allows write access to its application bounds
+ */
+ public static final int
+ ALLOW_APPLICATION_BOUNDS_WRITE = CapabilityBits.SOUNDSCAPE_ALLOW_APPLICATION_BOUNDS_WRITE;
+
+ /**
+ * For Soundscape component objects, specifies that this object
+ * allows the reading of it's aural attributes information
+ */
+ public static final int
+ ALLOW_ATTRIBUTES_READ = CapabilityBits.SOUNDSCAPE_ALLOW_ATTRIBUTES_READ;
+
+ /**
+ * For Soundscape component objects, specifies that this object
+ * allows the writing of it's aural attribute information
+ */
+ public static final int
+ ALLOW_ATTRIBUTES_WRITE = CapabilityBits.SOUNDSCAPE_ALLOW_ATTRIBUTES_WRITE;
+
+ /**
+ * Constructs and initializes a new Sound node using following
+ * defaults:
+ *<UL> application region: null (no active region)</UL>
+ *<UL> aural attributes: null (uses default aural attributes)</UL>
+ */
+ public Soundscape() {
+ // Just use default values
+ }
+
+ /**
+ * Constructs and initializes a new Sound node using specified
+ * parameters
+ * @param region application region
+ * @param attributes array of aural attribute component objects
+ */
+ public Soundscape(Bounds region,
+ AuralAttributes attributes) {
+ ((SoundscapeRetained)this.retained).setApplicationBounds(region);
+ ((SoundscapeRetained)this.retained).setAuralAttributes(attributes);
+ }
+
+ /**
+ * Creates the retained mode SoundscapeRetained object that this
+ * component object will point to.
+ */
+ void createRetained() {
+ this.retained = new SoundscapeRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Set the Soundscape's application region to the specified bounds
+ * specified in local coordinates of this leaf node. The aural
+ * attributes associated with this Soundscape are used to render
+ * the active sounds when this application region intersects the
+ * ViewPlatform's activation volume. The getApplicationBounds method
+ * returns a new Bounds object.
+ * This region is used when the application bounding leaf is null.
+ * @param region the bounds that contains the Soundscape's new application
+ * region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setApplicationBounds(Bounds region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Soundscape0"));
+
+ ((SoundscapeRetained)this.retained).setApplicationBounds(region);
+ }
+
+ /**
+ * Retrieves the Soundscape node's application bounds.
+ * @return this Soundscape's application bounds information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Bounds getApplicationBounds() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Soundscape1"));
+
+ return ((SoundscapeRetained)this.retained).getApplicationBounds();
+ }
+
+ /**
+ * Set the Soundscape's application region to the specified bounding leaf.
+ * When set to a value other than null, this overrides the application
+ * bounds object.
+ * @param region the bounding leaf node used to specify the Soundscape
+ * node's new application region.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setApplicationBoundingLeaf(BoundingLeaf region) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Soundscape0"));
+
+ ((SoundscapeRetained)this.retained).setApplicationBoundingLeaf(region);
+ }
+
+ /**
+ * Retrieves the Soundscape node's application bounding leaf.
+ * @return this Soundscape's application bounding leaf information
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BoundingLeaf getApplicationBoundingLeaf() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_APPLICATION_BOUNDS_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Soundscape1"));
+
+ return ((SoundscapeRetained)this.retained).getApplicationBoundingLeaf();
+ }
+
+ /**
+ * Set a set of aural attributes for this Soundscape
+ * @param attributes aural attributes
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setAuralAttributes(AuralAttributes attributes) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ATTRIBUTES_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Soundscape4"));
+
+ ((SoundscapeRetained)this.retained).setAuralAttributes(attributes);
+ }
+
+ /**
+ * Retrieve reference of Aural Attributes
+ * @return reference to aural attributes
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public AuralAttributes getAuralAttributes() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ATTRIBUTES_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Soundscape5"));
+
+ return ((SoundscapeRetained)this.retained).getAuralAttributes();
+ }
+
+
+ /**
+ * Creates 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) {
+ Soundscape s = new Soundscape();
+ 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 <code>NodeComponent</code> objects
+ * contained by the object being duplicated, each <code>NodeComponent</code>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <code>NodeComponent</code> 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>.
+ * <br>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @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 ClassCastException if originalNode is not an instance of
+ * <code>Soundscape</code>
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ checkDuplicateNode(originalNode, forceDuplicate);
+ }
+
+ /**
+ * Copies all Soundscape 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ SoundscapeRetained attr = (SoundscapeRetained) originalNode.retained;
+ SoundscapeRetained rt = (SoundscapeRetained) retained;
+
+ rt.setApplicationBounds(attr.getApplicationBounds());
+
+ rt.setAuralAttributes((AuralAttributes) getNodeComponent(
+ attr.getAuralAttributes(),
+ forceDuplicate,
+ originalNode.nodeHashtable));
+
+ // the following reference will set correctly in updateNodeReferences
+ rt.setApplicationBoundingLeaf(attr.getApplicationBoundingLeaf());
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+
+ SoundscapeRetained rt = (SoundscapeRetained) retained;
+
+ BoundingLeaf bl = rt.getApplicationBoundingLeaf();
+
+ if (bl != null) {
+ Object o = referenceTable.getNewObjectReference(bl);
+ rt.setApplicationBoundingLeaf((BoundingLeaf) o);
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SoundscapeRetained.java b/src/classes/share/javax/media/j3d/SoundscapeRetained.java
new file mode 100644
index 0000000..026bbbe
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SoundscapeRetained.java
@@ -0,0 +1,440 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.ArrayList;
+
+/**
+ * The SoundscapeRetained object defines all soundscape rendering state
+ * as a subclass of a Leaf node.
+ */
+class SoundscapeRetained extends LeafRetained
+{
+ static final int ATTRIBUTES_CHANGED = 0x00001;
+ static final int BOUNDING_LEAF_CHANGED = 0x00002;
+ static final int APPLICATION_BOUNDS_CHANGED = 0x00004;
+
+ /**
+ * Soundscape nodes application region
+ */
+ Bounds applicationRegion = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * The transformed Application Region
+ */
+ Bounds transformedRegion = null;
+
+ /**
+ * Aural attributes associated with this Soundscape
+ */
+ AuralAttributesRetained attributes = null;
+
+ // A bitmask that indicates that the something has changed.
+ int isDirty = 0xffff;
+
+ // Target threads to be notified when sound changes
+ int targetThreads = J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER;
+
+
+ // Is true, if the mirror light is viewScoped
+ boolean isViewScoped = false;
+
+ void dispatchMessage(int dirtyBit, Object argument) {
+ // Send message including a integer argument
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.type = J3dMessage.SOUNDSCAPE_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(dirtyBit);
+ createMessage.args[2] = new Integer(0);
+ createMessage.args[3] = null;
+ createMessage.args[4] = argument;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+
+ SoundscapeRetained() {
+ super();
+ this.nodeType = NodeRetained.SOUNDSCAPE;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ }
+
+
+ /**
+ * Set the Soundscape's application region.
+ * @param region a region that contains the Soundscape's new application region
+ */
+ void setApplicationBounds(Bounds region)
+ {
+ if (region != null) {
+ applicationRegion = (Bounds) region.clone();
+ if (staticTransform != null) {
+ applicationRegion.transform(staticTransform.transform);
+ }
+ }
+ else {
+ applicationRegion = null;
+ }
+ updateTransformChange();
+ this.isDirty |= APPLICATION_BOUNDS_CHANGED;
+ dispatchMessage(APPLICATION_BOUNDS_CHANGED, region);
+
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Get the Soundscape's application region.
+ * @return this Soundscape's application region information
+ */
+ Bounds getApplicationBounds()
+ {
+ Bounds b = null;
+
+ if (this.applicationRegion == null)
+ return (Bounds)null;
+ else {
+ b = (Bounds) applicationRegion.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ return b;
+ }
+ }
+
+ /**
+ * Set the Soundscape's application region to the specified Leaf node.
+ */
+ void setApplicationBoundingLeaf(BoundingLeaf region) {
+ if (boundingLeaf != null) {
+ // Remove the soundscape as users of the original bounding leaf
+ boundingLeaf.mirrorBoundingLeaf.removeUser(this);
+ }
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ boundingLeaf.mirrorBoundingLeaf.addUser(this);
+ } else {
+ boundingLeaf = null;
+ }
+ updateTransformChange();
+ this.isDirty |= BOUNDING_LEAF_CHANGED;
+ dispatchMessage(BOUNDING_LEAF_CHANGED, region);
+// QUESTION needed??
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Get the Soundscape's application region
+ */
+ BoundingLeaf getApplicationBoundingLeaf() {
+ if (boundingLeaf != null) {
+ return (BoundingLeaf)boundingLeaf.source;
+ } else {
+ return (BoundingLeaf)null;
+ }
+ }
+
+ /**
+ * Set a set of aural attributes for this Soundscape
+ * @param attributes aural attributes to be set
+ */
+ void setAuralAttributes(AuralAttributes attributes)
+ {
+ if (this.source.isLive()) {
+ if (this.attributes != null) {
+ this.attributes.clearLive(refCount);
+ }
+
+ if (attributes != null) {
+ ((AuralAttributesRetained)attributes.retained).setLive(inBackgroundGroup, refCount);
+ }
+ }
+
+ if (this.attributes != null) {
+ this.attributes.removeUser(this);
+ }
+
+ if (attributes != null) {
+ this.attributes = (AuralAttributesRetained)attributes.retained;
+ this.attributes.addUser(this);
+ } else {
+ this.attributes = null;
+ }
+
+ // copy all fields out of attributes and put into our copy of attributes
+ this.isDirty |= ATTRIBUTES_CHANGED;
+ dispatchMessage(ATTRIBUTES_CHANGED, attributes);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieve a reference to Aural Attributes
+ * @return attributes aural attributes to be returned
+ */
+ AuralAttributes getAuralAttributes()
+ {
+ if (attributes != null) {
+ return ((AuralAttributes) attributes.source);
+ }
+ else
+ return ((AuralAttributes) null);
+ }
+
+/*
+// NOTE: OLD CODE
+ // The update Object function.
+ public synchronized void updateObject() {
+ if ((attributes != null) && (attributes.aaDirty)) {
+ if (attributes.mirrorAa == null) {
+ attributes.mirrorAa = new AuralAttributesRetained();
+ }
+ attributes.mirrorAa.update(attributes);
+ }
+ }
+*/
+
+ // The update Object function.
+ synchronized void updateMirrorObject(Object[] objs) {
+ // NOTE: There doesn't seem to be a use for mirror objects since
+ // Soundscapes can't be shared.
+ // This method updates the transformed region from either bounding
+ // leaf or application bounds. Bounding leaf takes precidence.
+ Transform3D trans = null;
+ int component = ((Integer)objs[1]).intValue();
+ if ((component & BOUNDING_LEAF_CHANGED) != 0) {
+ if (this.boundingLeaf != null) {
+ transformedRegion = boundingLeaf.transformedRegion;
+ }
+ else { // evaluate Application Region if not null
+ if (applicationRegion != null) {
+ transformedRegion = (Bounds)applicationRegion.clone();
+ transformedRegion.transform(applicationRegion,
+ getLastLocalToVworld());
+ }
+ else {
+ transformedRegion = null;
+ }
+ }
+ }
+ else if ((component & APPLICATION_BOUNDS_CHANGED) != 0) {
+ // application bounds only used when bounding leaf null
+ if (boundingLeaf == null) {
+ transformedRegion = (Bounds)applicationRegion.clone();
+ transformedRegion.transform(applicationRegion,
+ getLastLocalToVworld());
+ }
+ else {
+ transformedRegion = null;
+ }
+ }
+ }
+
+ // The update tranform fields
+ synchronized void updateTransformChange() {
+ if (boundingLeaf != null) {
+ transformedRegion = boundingLeaf.transformedRegion;
+ }
+ else { // evaluate Application Region if not null
+ if (applicationRegion != null) {
+ transformedRegion = applicationRegion.copy(transformedRegion);
+ transformedRegion.transform(applicationRegion,
+ getLastLocalToVworld());
+ }
+ else {
+ transformedRegion = null;
+ }
+ }
+ }
+
+ void updateBoundingLeaf(long refTime) {
+ // This is necessary, if for example, the region
+ // changes from sphere to box.
+ if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) {
+ transformedRegion = boundingLeaf.transformedRegion;
+ } else { // evaluate Application Region if not null
+ if (applicationRegion != null) {
+ transformedRegion = applicationRegion.copy(transformedRegion);
+ transformedRegion.transform(applicationRegion,
+ getLastLocalToVworld());
+ } else {
+ transformedRegion = null;
+ }
+ }
+ }
+
+// QUESTION: not needed?
+/*
+ synchronized void initMirrorObject(SoundscapeRetained ms) {
+ GroupRetained group;
+ Transform3D trans;
+ Bounds region = null;
+
+ if (ms == null)
+ return;
+}
+ ms.isDirty = isDirty;
+ ms.setApplicationBounds(getApplicationBounds());
+ ms.setApplicationBoundingLeaf(getApplicationBoundingLeaf());
+ ms.setAuralAttributes(getAuralAttributes());
+
+// QUESTION: no lineage of mirror node kept??
+ ms.sgSound = sgSound;
+ ms.key = null;
+ ms.mirrorSounds = new SoundscapeRetained[1];
+ ms.numMirrorSounds = 0;
+ ms.parent = parent;
+ ms.transformedRegion = null;
+ if (boundingLeaf != null) {
+ if (ms.boundingLeaf != null)
+ ms.boundingLeaf.removeUser(ms);
+ ms.boundingLeaf = boundingLeaf.mirrorBoundingLeaf;
+ // Add this mirror object as user
+ ms.boundingLeaf.addUser(ms);
+ ms.transformedRegion = ms.boundingLeaf.transformedRegion;
+ }
+ else {
+ ms.boundingLeaf = null;
+ }
+
+ if (applicationRegion != null) {
+ ms.applicationRegion = (Bounds) applicationRegion.clone();
+ // Assign region only if bounding leaf is null
+ if (ms.transformedRegion == null) {
+ ms.transformedRegion = (Bounds) ms.applicationRegion.clone();
+ ms.transformedRegion.transform(ms.applicationRegion,
+ ms.getLastLocalToVworld());
+ }
+
+ }
+ else {
+ ms.applicationRegion = null;
+ }
+ }
+*/
+
+ /**
+ * This setLive routine first calls the superclass's method, then
+ * it adds itself to the list of soundscapes
+ */
+ void setLive(SetLiveState s) {
+ super.doSetLive(s);
+
+ if (attributes != null) {
+ attributes.setLive(inBackgroundGroup, s.refCount);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(this);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(this);
+ }
+
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("SoundscapeRetained1"));
+ }
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("SoundscapeRetained0"));
+ }
+
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.SND_TARGETS);
+ }
+ switchState = (SwitchState)s.switchStates.get(0);
+ s.notifyThreads |= (J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER);
+
+ super.markAsLive();
+ }
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it removes itself to the list of lights
+ */
+ void clearLive(SetLiveState s) {
+ super.clearLive(s);
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.SND_TARGETS);
+ }
+
+ if (attributes != null) {
+ attributes.clearLive(s.refCount);
+ }
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(this);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(this);
+ }
+ s.notifyThreads |= (J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER);
+ }
+
+ // Simply pass along to the NodeComponents
+ /*
+ void compile(CompileState compState) {
+ setCompiled();
+
+ if (attributes != null)
+ attributes.compile(compState);
+ }
+ */
+
+ // This makes this sound look just like the one passed in
+ void update(SoundscapeRetained ss) {
+ applicationRegion = (Bounds)ss.applicationRegion.clone();
+ attributes = ss.attributes;
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (applicationRegion != null) {
+ applicationRegion.transform(xform.transform);
+ }
+ }
+
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ leafList.add(this);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SpotLight.java b/src/classes/share/javax/media/j3d/SpotLight.java
new file mode 100644
index 0000000..dbb687c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SpotLight.java
@@ -0,0 +1,341 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * The SpotLight object specifies an attenuated light source at a
+ * fixed point in space that radiates light in a specified direction
+ * from the light source. A SpotLight has the same attributes as a
+ * PointLight node, with the addition of the following:<P>
+ * <UL>
+ * <LI>Direction - The axis of the cone of light. The default
+ * direction is (0.0, 0.0, -1.0). The spot light direction is
+ * significant only when the spread angle is not PI radians
+ * (which it is by default).</LI>
+ * <P>
+ * <LI>Spread angle - The angle in radians between the direction axis
+ * and a ray along the edge of the cone. Note that the angle of the
+ * cone at the apex is then twice this value. The range of values
+ * is [0.0,PI/2] radians, with a special value of PI radians. Values
+ * lower than 0 are clamped to 0 and values over PI/2 are clamped
+ * to PI. The default spread angle is PI radians. </LI>
+ * <P>
+ * <LI>Concentration - Specifies how quickly the light intensity
+ * attenuates as a function of the angle of radiation as measured from
+ * the direction of radiation. The light's intensity is highest at the
+ * center of the cone and is attenuated toward the edges of the cone
+ * by the cosine of the angle between the direction of the light
+ * and the direction from the light to the object being lit, raised
+ * to the power of the spot concentration exponent.
+ * The higher the concentration value, the more focused the light
+ * source. The range of values is [0.0,128.0]. The default
+ * concentration is 0.0, which provides uniform light
+ * distribution.</LI><P>
+ * </UL>
+ * A spot light contributes to diffuse and specular reflections, which
+ * depend on the orientation and position of an object's surface.
+ * A spot light does not contribute to ambient reflections.
+ */
+
+public class SpotLight extends PointLight {
+ /**
+ * Specifies that the Node allows writing to its spot lights spread angle
+ * information.
+ */
+ public static final int
+ ALLOW_SPREAD_ANGLE_WRITE = CapabilityBits.SPOT_LIGHT_ALLOW_SPREAD_ANGLE_WRITE;
+
+ /**
+ * Specifies that the Node allows reading its spot lights spread angle
+ * information.
+ */
+ public static final int
+ ALLOW_SPREAD_ANGLE_READ = CapabilityBits.SPOT_LIGHT_ALLOW_SPREAD_ANGLE_READ;
+
+ /**
+ * Specifies that the Node allows writing to its spot lights concentration
+ * information.
+ */
+ public static final int
+ ALLOW_CONCENTRATION_WRITE = CapabilityBits.SPOT_LIGHT_ALLOW_CONCENTRATION_WRITE;
+
+ /**
+ * Specifies that the Node allows reading its spot lights concentration
+ * information.
+ */
+ public static final int
+ ALLOW_CONCENTRATION_READ = CapabilityBits.SPOT_LIGHT_ALLOW_CONCENTRATION_READ;
+
+ /**
+ * Specifies that the Node allows writing to its spot lights direction
+ * information.
+ */
+ public static final int
+ ALLOW_DIRECTION_WRITE = CapabilityBits.SPOT_LIGHT_ALLOW_DIRECTION_WRITE;
+
+ /**
+ * Specifies that the Node allows reading its spot lights direction
+ * information.
+ */
+ public static final int
+ ALLOW_DIRECTION_READ = CapabilityBits.SPOT_LIGHT_ALLOW_DIRECTION_READ;
+
+ /**
+ * Constructs a SpotLight node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * direction : (0,0,-1)<br>
+ * spread angle : <i>PI</i> radians<br>
+ * concentration : 0.0<br>
+ * </ul>
+ */
+ public SpotLight() {
+ }
+
+ /**
+ * Constructs and initializes a SpotLight node using the
+ * specified parameters.
+ * @param color the color of the light source
+ * @param position the position of the light in three-space
+ * @param attenuation the attenuation (constant, linear, quadratic)
+ * of the light
+ * @param direction the direction of the light
+ * @param spreadAngle the spread angle of the light
+ * @param concentration the concentration of the light
+ */
+ public SpotLight(Color3f color,
+ Point3f position,
+ Point3f attenuation,
+ Vector3f direction,
+ float spreadAngle,
+ float concentration) {
+ super(color, position, attenuation);
+ ((SpotLightRetained)this.retained).initDirection(direction);
+ ((SpotLightRetained)this.retained).initSpreadAngle(spreadAngle);
+ ((SpotLightRetained)this.retained).initConcentration(concentration);
+ }
+
+ /**
+ * Constructs and initializes a SpotLight node using the
+ * specified parameters.
+ * @param lightOn flag indicating whether this light is on or off
+ * @param color the color of the light source
+ * @param position the position of the light in three-space
+ * @param attenuation the attenuation (constant, linear, quadratic) of the light
+ * @param direction the direction of the light
+ * @param spreadAngle the spread angle of the light
+ * @param concentration the concentration of the light
+ */
+ public SpotLight(boolean lightOn,
+ Color3f color,
+ Point3f position,
+ Point3f attenuation,
+ Vector3f direction,
+ float spreadAngle,
+ float concentration) {
+ super(lightOn, color, position, attenuation);
+ ((SpotLightRetained)this.retained).initDirection(direction);
+ ((SpotLightRetained)this.retained).initSpreadAngle(spreadAngle);
+ ((SpotLightRetained)this.retained).initConcentration(concentration);
+ }
+
+ /**
+ * Creates the retained mode SpotLightRetained object that this
+ * SpotLight component object will point to.
+ */
+ void createRetained() {
+ this.retained = new SpotLightRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Sets spot light spread angle.
+ * @param spreadAngle the new spread angle for spot light
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph.
+ */
+ public void setSpreadAngle(float spreadAngle) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SPREAD_ANGLE_WRITE))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("SpotLight0"));
+
+ if (isLive())
+ ((SpotLightRetained)this.retained).setSpreadAngle(spreadAngle);
+ else
+ ((SpotLightRetained)this.retained).initSpreadAngle(spreadAngle);
+ }
+
+ /**
+ * Gets spot light spread angle.
+ * @return the new spread angle for spot light. The value returned
+ * is the clamped value.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getSpreadAngle() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SPREAD_ANGLE_READ))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("SpotLight1"));
+
+ return ((SpotLightRetained)this.retained).getSpreadAngle();
+ }
+
+ /**
+ * Sets spot light concentration.
+ * @param concentration the new concentration for spot light
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setConcentration(float concentration) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CONCENTRATION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("SpotLight2"));
+
+ if (isLive())
+ ((SpotLightRetained)this.retained).setConcentration(concentration);
+ else
+ ((SpotLightRetained)this.retained).initConcentration(concentration);
+ }
+
+ /**
+ * Gets spot light concentration.
+ * @return the new concentration for spot light
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getConcentration() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CONCENTRATION_READ))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("SpotLight3"));
+ return ((SpotLightRetained)this.retained).getConcentration();
+ }
+
+ /**
+ * Sets light direction.
+ * @param x the new X direction
+ * @param y the new Y direction
+ * @param z the new Z direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDirection(float x, float y, float z) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_WRITE))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("SpotLight4"));
+
+ if (isLive())
+ ((SpotLightRetained)this.retained).setDirection(x,y,z);
+ else
+ ((SpotLightRetained)this.retained).initDirection(x,y,z);
+ }
+
+ /**
+ * Sets this Light's current direction and places it in the parameter specified.
+ * @param direction the vector that will receive this node's direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setDirection(Vector3f direction) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_WRITE))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("SpotLight4"));
+
+ if (isLive())
+ ((SpotLightRetained)this.retained).setDirection(direction);
+ else
+ ((SpotLightRetained)this.retained).initDirection(direction);
+ }
+
+ /**
+ * Gets this Light's current direction and places it in the
+ * parameter specified.
+ * @param direction the vector that will receive this node's direction
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getDirection(Vector3f direction) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_DIRECTION_READ))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("SpotLight6"));
+ ((SpotLightRetained)this.retained).getDirection(direction);
+ }
+
+ /**
+ * 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) {
+ SpotLight s = new SpotLight();
+ s.duplicateNode(this, forceDuplicate);
+ return s;
+ }
+
+
+ /**
+ * Copies all SpotLight 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
+ */
+ void duplicateAttributes(Node originalNode, boolean
+ forceDuplicate) {
+
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ SpotLightRetained attr = (SpotLightRetained) originalNode.retained;
+ SpotLightRetained rt = (SpotLightRetained) retained;
+
+ rt.initSpreadAngle(attr.getSpreadAngle());
+ rt.initConcentration(attr.getConcentration());
+ Vector3f v = new Vector3f();
+ attr.getDirection(v);
+ rt.initDirection(v);
+
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SpotLightRetained.java b/src/classes/share/javax/media/j3d/SpotLightRetained.java
new file mode 100644
index 0000000..8f88e63
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SpotLightRetained.java
@@ -0,0 +1,341 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * A local spot light source object.
+ */
+
+class SpotLightRetained extends PointLightRetained {
+ static final int DIRECTION_CHANGED = LAST_POINTLIGHT_DEFINED_BIT << 1;
+ static final int ANGLE_CHANGED = LAST_POINTLIGHT_DEFINED_BIT << 2;
+ static final int CONCENTRATION_CHANGED = LAST_POINTLIGHT_DEFINED_BIT << 3;
+
+ /**
+ * The spot light's direction.
+ */
+ Vector3f direction = new Vector3f(0.0f, 0.0f, -1.0f);
+
+ // The transformed direction of this light
+ Vector3f xformDirection = new Vector3f(0.0f, 0.0f, -1.0f);
+
+ /**
+ * The spot light's spread angle.
+ */
+ float spreadAngle = (float)Math.PI;
+
+ /**
+ * The spot light's concentration.
+ */
+ float concentration = 0.0f;
+
+
+ SpotLightRetained() {
+ this.nodeType = NodeRetained.SPOTLIGHT;
+ lightType = 4;
+ }
+
+ /**
+ * Initializes the spot light's spread angle.
+ * @param spreadAngle the light's spread angle
+ */
+ void initSpreadAngle(float spreadAngle) {
+ if (spreadAngle < 0.0) {
+ this.spreadAngle = 0.0f;
+ }
+ else if (spreadAngle > (float) Math.PI * 0.5f) {
+ this.spreadAngle = (float)Math.PI;
+ }
+ else {
+ this.spreadAngle = spreadAngle;
+ }
+ }
+
+
+ void setLive(SetLiveState s) {
+ super.doSetLive(s);
+ J3dMessage createMessage = super.initMessage(12);
+ Object[] objs = (Object[])createMessage.args[4];
+ objs[9] = new Float(spreadAngle);
+ objs[10] = new Float(concentration) ;
+ objs[11] = new Vector3f(direction);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ /**
+ * Sets the spot light's spread angle.
+ * @param spreadAngle the light's spread angle
+ */
+ void setSpreadAngle(float spreadAngle) {
+ initSpreadAngle(spreadAngle);
+ sendMessage(ANGLE_CHANGED, new Float(this.spreadAngle));
+ }
+
+ /**
+ * Returns the spot light's spread angle.
+ * @return the spread angle of the light
+ */
+ float getSpreadAngle() {
+ return this.spreadAngle;
+ }
+
+ /**
+ * Initializes the spot light's concentration.
+ * @param concentration the concentration of the light
+ */
+ void initConcentration(float concentration) {
+ this.concentration = concentration;
+ }
+
+ /**
+ * Sets the spot light's concentration.
+ * @param concentration the concentration of the light
+ */
+ void setConcentration(float concentration) {
+ initConcentration(concentration);
+ sendMessage(CONCENTRATION_CHANGED, new Float(concentration));
+ }
+
+ /**
+ * Retrieves the spot light's concentration.
+ * @return the concentration of the light
+ */
+ float getConcentration() {
+ return this.concentration;
+ }
+
+ /**
+ * Initializes the spot light's direction from the vector provided.
+ * @param direction the new direction of the light
+ */
+ void initDirection(Vector3f direction) {
+ this.direction.set(direction);
+
+ if (staticTransform != null) {
+ staticTransform.transform.transform(this.direction, this.direction);
+ }
+ }
+
+ /**
+ * Sets the spot light's direction from the vector provided.
+ * @param direction the new direction of the light
+ */
+ void setDirection(Vector3f direction) {
+ initDirection(direction);
+ sendMessage(DIRECTION_CHANGED, new Vector3f(direction));
+ }
+
+
+ /**
+ * Initializes this light's direction from the three values provided.
+ * @param x the new x direction
+ * @param y the new y direction
+ * @param z the new z direction
+ */
+ void initDirection(float x, float y, float z) {
+ this.direction.x = x;
+ this.direction.y = y;
+ this.direction.z = z;
+ if (staticTransform != null) {
+ staticTransform.transform.transform(this.direction, this.direction);
+ }
+ }
+
+ /**
+ * Sets this light's direction from the three values provided.
+ * @param x the new x direction
+ * @param y the new y direction
+ * @param z the new z direction
+ */
+ void setDirection(float x, float y, float z) {
+ setDirection(new Vector3f(x, y, z));
+ }
+
+
+ /**
+ * Retrieves this light's direction and places it in the
+ * vector provided.
+ * @param direction the variable to receive the direction vector
+ */
+ void getDirection(Vector3f direction) {
+ direction.set(this.direction);
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ invTransform.transform(direction, direction);
+ }
+ }
+
+ /**
+ * This update function, and its native counterpart,
+ * updates a spot light. This includes its color, attenuation,
+ * transformed position, spread angle, concentration,
+ * and its transformed position.
+ */
+ native void updateLight(long ctx,
+ int lightSlot, float red, float green,
+ float blue, float ax, float ay, float az,
+ float px, float py, float pz, float spreadAngle,
+ float concentration, float dx, float dy,
+ float dz);
+
+ void update(long ctx, int lightSlot, double scale) {
+ validateAttenuationInEc(scale);
+ updateLight(ctx, lightSlot, color.x, color.y, color.z,
+ attenuation.x, linearAttenuationInEc,
+ quadraticAttenuationInEc,
+ xformPosition.x, xformPosition.y,
+ xformPosition.z, spreadAngle, concentration,
+ xformDirection.x, xformDirection.y,
+ xformDirection.z);
+ }
+
+
+ /**
+ * This update function, and its native counterpart,
+ * updates a directional light. This includes its
+ * color and its transformed direction.
+ */
+ // Note : if you add any more fields here , you need to update
+ // updateLight() in RenderingEnvironmentStructure
+ void updateMirrorObject(Object[] objs) {
+
+ int component = ((Integer)objs[1]).intValue();
+ Transform3D trans;
+ int i;
+ int numLgts = ((Integer)objs[2]).intValue();
+ LightRetained[] mLgts = (LightRetained[]) objs[3];
+ if ((component & DIRECTION_CHANGED) != 0) {
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i].nodeType == NodeRetained.SPOTLIGHT) {
+ SpotLightRetained ml = (SpotLightRetained)mLgts[i];
+ ml.direction = (Vector3f)objs[4];
+ ml.getLastLocalToVworld().transform(ml.direction,
+ ml.xformDirection);
+ ml.xformDirection.normalize();
+ }
+ }
+ }
+ else if ((component & ANGLE_CHANGED) != 0) {
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i].nodeType == NodeRetained.SPOTLIGHT) {
+ SpotLightRetained ml = (SpotLightRetained)mLgts[i];
+ ml.spreadAngle = ((Float)objs[4]).floatValue();
+ }
+ }
+
+ }
+ else if ((component & CONCENTRATION_CHANGED) != 0) {
+
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i].nodeType == NodeRetained.SPOTLIGHT) {
+ SpotLightRetained ml = (SpotLightRetained)mLgts[i];
+ ml.concentration = ((Float)objs[4]).floatValue();
+ }
+ }
+ }
+ else if ((component & INIT_MIRROR) != 0) {
+ for (i = 0; i < numLgts; i++) {
+ if (mLgts[i].nodeType == NodeRetained.SPOTLIGHT) {
+ SpotLightRetained ml = (SpotLightRetained)mLgts[i];
+ ml.spreadAngle = ((Float)((Object[])objs[4])[9]).floatValue();
+ ml.concentration = ((Float)((Object[])objs[4])[10]).floatValue();
+ ml.direction = (Vector3f)((Object[])objs[4])[11];
+ ml.getLastLocalToVworld().transform(ml.direction,
+ ml.xformDirection);
+ ml.xformDirection.normalize();
+ }
+ }
+ }
+
+ // call the parent's mirror object update routine
+ super.updateMirrorObject(objs);
+ }
+
+
+ /*
+ // This update function, and its native counterpart,
+ // updates a spot light. This includes its color, attenuation,
+ // transformed position, spread angle, concentration,
+ // and its transformed position.
+ native void updateLight(int lightSlot, float red, float green,
+ float blue, float ax, float ay, float az,
+ float px, float py, float pz, float spreadAngle,
+ float concentration, float dx, float dy,
+ float dz);
+ void update(int lightSlot, double scale) {
+ updateLight(lightSlot, color.x, color.y, color.z,
+ attenuation.x, linearAttenuationInEc,
+ quadraticAttenuationInEc,
+ xformPosition.x, xformPosition.y,
+ xformPosition.z, spreadAngle, concentration,
+ xformDirection.x, xformDirection.y,
+ xformDirection.z);
+ }
+
+ synchronized void update(LightRetained l, boolean clear) {
+ SpotLightRetained sl = (SpotLightRetained)l;
+ super.update(sl, clear);
+
+ l.sgLight.getLocalToVworld(trans, l.key);
+ trans.transform(direction, sl.xformDirection);
+ sl.xformDirection.normalize();
+ trans.transform(position, sl.xformPosition);
+ sl.spreadAngle = spreadAngle;
+ sl.concentration = concentration;
+ }
+ */
+
+
+ // Clones only the retained side, internal use only
+ protected Object clone() {
+ SpotLightRetained sr = (SpotLightRetained)super.clone();
+ sr.direction = new Vector3f(direction);
+ sr.xformDirection = new Vector3f();
+ return sr;
+ }
+
+
+
+ // Called on the mirror object
+ void updateTransformChange() {
+ super.updateTransformChange();
+
+ getLastLocalToVworld().transform(direction, xformDirection);
+ xformDirection.normalize();
+
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = targetThreads;
+ createMessage.universe = universe;
+ createMessage.type = J3dMessage.LIGHT_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorLights);
+ else
+ createMessage.args[2] = new Integer(1);
+ createMessage.args[3] = mirrorLights.clone();
+ createMessage.args[4] = attr;
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ xform.transform.transform(direction, direction);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/StructureUpdateThread.java b/src/classes/share/javax/media/j3d/StructureUpdateThread.java
new file mode 100644
index 0000000..30c79b4
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/StructureUpdateThread.java
@@ -0,0 +1,85 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The StructureUpdateThread is thread that passes messages to its structure
+ */
+
+class StructureUpdateThread extends J3dThread {
+ /**
+ * The structure that this thread works for
+ */
+ J3dStructure structure;
+
+ /**
+ * Some variables used to name threads correctly
+ */
+ private static int numInstances[] = new int[7];
+ private int instanceNum[] = new int[7];
+
+ private synchronized int newInstanceNum(int idx) {
+ return (++numInstances[idx]);
+ }
+
+ int getInstanceNum(int idx) {
+ if (instanceNum[idx] == 0)
+ instanceNum[idx] = newInstanceNum(idx);
+ return instanceNum[idx];
+ }
+
+ /**
+ * Just saves the structure
+ */
+ StructureUpdateThread(ThreadGroup t, J3dStructure s, int threadType) {
+ super(t);
+ structure = s;
+ type = threadType;
+ classification = J3dThread.UPDATE_THREAD;
+
+ switch (type) {
+ case J3dThread.UPDATE_GEOMETRY:
+ setName("J3D-GeometryStructureUpdateThread-" + getInstanceNum(0));
+ break;
+ case J3dThread.UPDATE_RENDER:
+ setName("J3D-RenderStructureUpdateThread-" + getInstanceNum(1));
+ break;
+ case J3dThread.UPDATE_BEHAVIOR:
+ setName("J3D-BehaviorStructureUpdateThread-" + getInstanceNum(2));
+ break;
+ case J3dThread.UPDATE_SOUND:
+ setName("J3D-SoundStructureUpdateThread-" + getInstanceNum(3));
+ break;
+ case J3dThread.UPDATE_RENDERING_ATTRIBUTES:
+ // Only one exists in Java3D system
+ setName("J3D-RenderingAttributesStructureUpdateThread");
+ break;
+ case J3dThread.UPDATE_RENDERING_ENVIRONMENT:
+ setName("J3D-RenderingEnvironmentStructureUpdateThread-"+
+ getInstanceNum(4));
+ break;
+ case J3dThread.UPDATE_TRANSFORM:
+ setName("J3D-TransformStructureUpdateThread-"+ getInstanceNum(5));
+ break;
+ case J3dThread.SOUND_SCHEDULER:
+ setName("J3D-SoundSchedulerUpdateThread-"+ getInstanceNum(6));
+ break;
+
+ }
+
+ }
+
+ void doWork(long referenceTime) {
+ structure.processMessages(referenceTime);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Switch.java b/src/classes/share/javax/media/j3d/Switch.java
new file mode 100644
index 0000000..da80df3
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Switch.java
@@ -0,0 +1,247 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.BitSet;
+
+/**
+ * The Switch node controls which of its children will be rendered.
+ * It defines a child selection value (a switch value) that can either
+ * select a single child, or it can select 0 or more children using a
+ * mask to indicate which children are selected for rendering.
+ * The Switch node contains an ordered list of children, but the
+ * index order of the children in the list is only used for selecting
+ * the appropriate child or children and does not specify rendering
+ * order.
+ */
+
+public class Switch extends Group {
+
+ /**
+ * Specifies that this node allows reading its child selection
+ * and mask values and its current child.
+ */
+ public static final int
+ ALLOW_SWITCH_READ = CapabilityBits.SWITCH_ALLOW_SWITCH_READ;
+
+ /**
+ * Specifies that this node allows writing its child selection
+ * and mask values.
+ */
+ public static final int
+ ALLOW_SWITCH_WRITE = CapabilityBits.SWITCH_ALLOW_SWITCH_WRITE;
+
+ /**
+ * Specifies that no children are rendered.
+ * This value may be used in place of a non-negative child
+ * selection index.
+ */
+ public static final int CHILD_NONE = -1;
+
+ /**
+ * Specifies that all children are rendered. This setting causes
+ * the switch node to function as an ordinary group node.
+ * This value may be used in place of a non-negative child
+ * selection index.
+ */
+ public static final int CHILD_ALL = -2;
+
+ /**
+ * Specifies that the childMask BitSet is
+ * used to select which children are rendered.
+ * This value may be used in place of a non-negative child
+ * selection index.
+ */
+ public static final int CHILD_MASK = -3;
+
+ /**
+ * Constructs a Switch node with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * child selection index : CHILD_NONE<br>
+ * child selection mask : false (for all children)<br>
+ * </ul>
+ */
+ public Switch() {
+ }
+
+ /**
+ * Constructs and initializes a Switch node using the specified
+ * child selection index.
+ * @param whichChild the initial child selection index
+ */
+ public Switch(int whichChild) {
+ ((SwitchRetained)this.retained).setWhichChild(whichChild, true);
+ }
+
+ /**
+ * Constructs and initializes a Switch node using the specified
+ * child selection index and mask.
+ * @param whichChild the initial child selection index
+ * @param childMask the initial child selection mask
+ */
+ public Switch(int whichChild, BitSet childMask){
+ ((SwitchRetained)this.retained).setWhichChild(whichChild, true);
+ ((SwitchRetained)this.retained).setChildMask(childMask);
+ }
+
+ /**
+ * Creates the retained mode SwitchRetained object that this
+ * Switch object will point to.
+ */
+ void createRetained() {
+ this.retained = new SwitchRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Sets the child selection index that specifies which child is rendered.
+ * If the value is out of range, then no children are drawn.
+ * @param child a non-negative integer index value, indicating a
+ * specific child, or one of the following constants: CHILD_NONE,
+ * CHILD_ALL, or CHILD_MASK.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #CHILD_NONE
+ * @see #CHILD_ALL
+ * @see #CHILD_MASK
+ */
+ public void setWhichChild(int child) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SWITCH_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Switch0"));
+
+ ((SwitchRetained)this.retained).setWhichChild(child, false);
+ }
+
+ /**
+ * Retrieves the current child selection index that specifies which
+ * child is rendered.
+ * @return a non-negative integer index value, indicating a
+ * specific child, or one of the following constants: CHILD_NONE,
+ * CHILD_ALL, or CHILD_MASK
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #CHILD_NONE
+ * @see #CHILD_ALL
+ * @see #CHILD_MASK
+ */
+ public int getWhichChild() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SWITCH_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Switch1"));
+
+ return ((SwitchRetained)this.retained).getWhichChild();
+ }
+
+ /**
+ * Sets the child selection mask. This mask is used when
+ * the child selection index is set to CHILD_MASK.
+ * @param childMask a BitSet that specifies which children are rendered
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setChildMask(BitSet childMask) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SWITCH_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Switch2"));
+
+ ((SwitchRetained)this.retained).setChildMask(childMask);
+ }
+
+ /**
+ * Retrieves the current child selection mask. This mask is used when
+ * the child selection index is set to CHILD_MASK.
+ * @return the child selection mask
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public BitSet getChildMask() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SWITCH_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Switch3"));
+
+ return ((SwitchRetained)this.retained).getChildMask();
+ }
+
+ /**
+ * Retrieves the currently selected child. If the child selection index
+ * is out of range or is set to CHILD_NONE, CHILD_ALL, or CHILD_MASK,
+ * then this method returns null.
+ * @return a reference to the current child chosen for rendering
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Node currentChild() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHILDREN_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Switch4"));
+
+ return ((SwitchRetained)this.retained).currentChild();
+ }
+
+
+ /**
+ * 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) {
+ Switch s = new Switch();
+ s.duplicateNode(this, forceDuplicate);
+ return s;
+ }
+
+ /**
+ * Copies all Switch 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 Group#cloneNode
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ SwitchRetained attr = (SwitchRetained) originalNode.retained;
+ SwitchRetained rt = (SwitchRetained) retained;
+
+ rt.setChildMask(attr.getChildMask());
+ rt.setWhichChild(attr.getWhichChild(), true);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SwitchRetained.java b/src/classes/share/javax/media/j3d/SwitchRetained.java
new file mode 100644
index 0000000..93e7f10
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SwitchRetained.java
@@ -0,0 +1,885 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * The switch node controls which one of its children will be rendered.
+ */
+
+class SwitchRetained extends GroupRetained implements TargetsInterface
+{
+ static final int GEO_NODES = 0x0001;
+ static final int ENV_NODES = 0x0002;
+ static final int BEHAVIOR_NODES = 0x0004;
+ static final int SOUND_NODES = 0x0008;
+ static final int BOUNDINGLEAF_NODES = 0x0010;
+
+ /**
+ * The value specifing which child to render.
+ */
+ int whichChild = Switch.CHILD_NONE;
+
+ /**
+ * The BitSet specifying which children are to be selected for
+ * rendering. This is used ONLY if whichChild is set to CHILD_MASK.
+ */
+ BitSet childMask = new BitSet();
+
+ /**
+ * The childmask bitset used for rendering
+ */
+ BitSet renderChildMask = new BitSet();
+
+ // A boolean indication that something changed
+ boolean isDirty = true;
+
+ // switchLevel per key, used in traversing switch children
+ ArrayList switchLevels = new ArrayList(1);
+
+ // key which identifies a unique path from a locale to this switch link
+ HashKey switchKey = new HashKey();
+
+ // switch index counter to identify specific children
+ int switchIndexCount = 0;
+
+ // for message processing
+ UpdateTargets updateTargets = null;
+
+ ArrayList childrenSwitchStates = null;
+
+ SwitchRetained() {
+ this.nodeType = NodeRetained.SWITCH;
+ }
+
+ /**
+ * Sets which child should be drawn.
+ * @param whichChild the child to choose during a render operation
+ */
+ // synchronized with clearLive
+ synchronized void setWhichChild(int whichChild, boolean updateAlways) {
+
+ int i, nchildren;
+
+ this.whichChild = whichChild;
+ isDirty = true;
+
+ if (source != null && source.isLive()) {
+ updateTargets = new UpdateTargets();
+ ArrayList updateList = new ArrayList(1);
+ nchildren = children.size();
+ switch (whichChild) {
+ case Switch.CHILD_ALL:
+ for (i=0; i<nchildren; i++) {
+ if (renderChildMask.get(i) == false || updateAlways) {
+ renderChildMask.set(i);
+ updateSwitchChild(i, true, updateList);
+ }
+ }
+ break;
+ case Switch.CHILD_NONE:
+ for (i=0; i<nchildren; i++) {
+ if (renderChildMask.get(i) == true || updateAlways) {
+ renderChildMask.clear(i);
+ updateSwitchChild(i, false, updateList);
+ }
+ }
+ break;
+ case Switch.CHILD_MASK:
+ for (i=0; i<nchildren; i++) {
+ if (childMask.get(i) == true) {
+ if (renderChildMask.get(i) == false || updateAlways) {
+ renderChildMask.set(i);
+ updateSwitchChild(i, true, updateList);
+ }
+ } else {
+ if (renderChildMask.get(i) == true || updateAlways) {
+ renderChildMask.clear(i);
+ updateSwitchChild(i, false, updateList);
+ }
+ }
+ }
+ break;
+ default:
+ for (i=0; i<nchildren; i++) {
+ if (i == whichChild) {
+ if (renderChildMask.get(i) == false || updateAlways) {
+ renderChildMask.set(i);
+ updateSwitchChild(i, true, updateList);
+ }
+ } else {
+ if (renderChildMask.get(i) == true || updateAlways) {
+ renderChildMask.clear(i);
+ updateSwitchChild(i, false, updateList);
+ }
+ }
+ }
+ break;
+ }
+ sendMessage(updateList);
+ }
+ }
+
+ /**
+ * Returns the index of the current child.
+ * @return the default child's index
+ */
+ int getWhichChild() {
+ return this.whichChild;
+ }
+
+ /**
+ * Sets current childMask.
+ * @param childMask a BitSet to select the children for rendering
+ */
+ // synchronized with clearLive
+ synchronized final void setChildMask(BitSet childMask) {
+ int i, nbits, nchildren;
+
+ if (childMask.size() > this.childMask.size()) {
+ nbits = childMask.size();
+ } else {
+ nbits = this.childMask.size();
+ }
+
+ for (i=0; i<nbits; i++) {
+ if (childMask.get(i)) {
+ this.childMask.set(i);
+ } else {
+ this.childMask.clear(i);
+ }
+ }
+ this.isDirty = true;
+ if (source != null && source.isLive() &&
+ whichChild == Switch.CHILD_MASK) {
+ updateTargets = new UpdateTargets();
+ ArrayList updateList = new ArrayList(1);
+ nchildren = children.size();
+ for (i=0; i<nchildren; i++) {
+ if (childMask.get(i) == true) {
+ if (renderChildMask.get(i) == false) {
+ renderChildMask.set(i);
+ updateSwitchChild(i, true, updateList);
+ }
+ } else {
+ if (renderChildMask.get(i) == true) {
+ renderChildMask.clear(i);
+ updateSwitchChild(i, false, updateList);
+ }
+ }
+ }
+ sendMessage(updateList);
+ }
+ }
+
+ void sendMessage(ArrayList updateList) {
+
+ J3dMessage m ;
+ int i,j,size,threads;
+ Object[] nodesArr, nodes;
+
+ threads = updateTargets.computeSwitchThreads();
+
+ if (threads > 0) {
+
+ m = VirtualUniverse.mc.getMessage();
+ m.type = J3dMessage.SWITCH_CHANGED;
+ m.universe = universe;
+ m.threads = threads;
+ m.args[0] = updateTargets;
+ m.args[2] = updateList;
+ UnorderList blnList =
+ updateTargets.targetList[Targets.BLN_TARGETS];
+
+ if (blnList != null) {
+ BoundingLeafRetained mbleaf;
+ size = blnList.size();
+
+ Object[] boundingLeafUsersArr = new Object[size];
+ nodesArr = blnList.toArray(false);
+ for (j=0; j<size; j++) {
+ nodes = (Object[])nodesArr[j];
+ Object[] boundingLeafUsers = new Object[nodes.length];
+ boundingLeafUsersArr[j] = boundingLeafUsers;
+ for (i=0; i<nodes.length; i++) {
+ mbleaf = (BoundingLeafRetained) nodes[i];
+ boundingLeafUsers[i] = mbleaf.users.toArray();
+ }
+ }
+ m.args[1] = boundingLeafUsersArr;
+ }
+ VirtualUniverse.mc.processMessage(m);
+ }
+
+ UnorderList vpList = updateTargets.targetList[Targets.VPF_TARGETS];
+ if (vpList != null) {
+ ViewPlatformRetained vp;
+ size = vpList.size();
+
+ nodesArr = vpList.toArray(false);
+ for (j=0; j<size; j++) {
+ nodes = (Object[])nodesArr[j];
+ for (i=0; i<nodes.length; i++) {
+ vp = (ViewPlatformRetained)nodes[i];
+ vp.processSwitchChanged();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the current childMask.
+ * @return the current childMask
+ */
+ final BitSet getChildMask() {
+ return (BitSet)this.childMask.clone();
+ }
+
+ /**
+ * Returns the current child.
+ * @return the current child
+ */
+ Node currentChild() {
+ if ((whichChild < 0) || (whichChild >= children.size()))
+ return null;
+ else
+ return getChild(whichChild);
+ }
+
+ void updateSwitchChild(int child, boolean switchOn, ArrayList updateList) {
+ int i;
+ int switchLevel;
+
+ if (inSharedGroup) {
+ for (i=0; i<localToVworldKeys.length; i++) {
+ switchLevel = ((Integer)switchLevels.get(i)).intValue();
+ traverseSwitchChild(child, localToVworldKeys[i], i, this,
+ false, false, switchOn, switchLevel, updateList);
+ }
+ } else {
+ switchLevel = ((Integer)switchLevels.get(0)).intValue();
+ traverseSwitchChild(child, null, 0, this, false, false,
+ switchOn, switchLevel, updateList);
+ }
+ }
+
+ // Switch specific data at SetLive.
+ void setAuxData(SetLiveState s, int index, int hkIndex) {
+ int size;
+ ArrayList switchStates;
+
+ // Group's setAuxData()
+ super.setAuxData(s, index, hkIndex);
+ switchLevels.add(new Integer(s.switchLevels[index]));
+ int nchildren = children.size();
+ for (int i=0; i<nchildren; i++) {
+ switchStates = (ArrayList)childrenSwitchStates.get(i);
+ switchStates.add(hkIndex, new SwitchState(true));
+ }
+ }
+
+ void setNodeData(SetLiveState s) {
+ super.setNodeData(s);
+ // add this node to parent's childSwitchLink
+ if (s.childSwitchLinks != null) {
+ if(!inSharedGroup ||
+ // only add if not already added in sharedGroup
+ !s.childSwitchLinks.contains(this)) {
+ s.childSwitchLinks.add(this);
+ }
+ }
+ // Note that s.childSwitchLinks is updated in super.setLive
+ s.parentSwitchLink = this;
+
+ if (!inSharedGroup) {
+ setAuxData(s, 0, 0);
+ } else {
+ // For inSharedGroup case.
+ int j, hkIndex;
+
+ s.hashkeyIndex = new int[s.keys.length];
+ for(j=0; j<s.keys.length; j++) {
+ hkIndex = s.keys[j].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(hkIndex >= 0) {
+ setAuxData(s, j, hkIndex);
+ } else {
+ System.out.println("Can't Find matching hashKey in setNodeData.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ s.hashkeyIndex[j] = hkIndex;
+ }
+ }
+ }
+
+ void setLive(SetLiveState s) {
+ int i,j,k;
+ boolean switchOn;
+ SwitchRetained switchRoot;
+ int size;
+
+ // save setLiveState
+ Targets[] savedSwitchTargets = s.switchTargets;
+ ArrayList savedSwitchStates = s.switchStates;
+ SwitchRetained[] savedClosestSwitchParents = s.closestSwitchParents;
+ int[] savedClosestSwitchIndices = s.closestSwitchIndices;
+ ArrayList savedChildSwitchLinks = s.childSwitchLinks;
+ GroupRetained savedParentSwitchLink = s.parentSwitchLink;
+ int[] savedHashkeyIndex = s.hashkeyIndex;
+
+ // update setLiveState for this node
+ s.closestSwitchParents = (SwitchRetained[])
+ savedClosestSwitchParents.clone();
+ s.closestSwitchIndices = (int[])savedClosestSwitchIndices.clone();
+
+ // Note that s.containsNodesList is updated in super.setLive
+ // Note that s.closestSwitchIndices is updated in super.setLive
+ for (i=0; i< s.switchLevels.length; i++) {
+ s.switchLevels[i]++;
+ s.closestSwitchParents[i] = this;
+ }
+
+ super.doSetLive(s);
+
+ initRenderChildMask();
+
+ // update switch leaves' compositeSwitchMask
+ // and update switch leaves' switchOn flag if this is top level switch
+ if (inSharedGroup) {
+ for (i=0; i<s.keys.length; i++) {
+ j = s.hashkeyIndex[i];
+ // j is index in ContainNodes
+ if (j < localToVworldKeys.length) {
+ switchRoot = (s.switchLevels[i] == 0)? this : null;
+ size = children.size();
+ for (k=0; k<size; k++) {
+ switchOn = renderChildMask.get(k);
+ traverseSwitchChild(k, s.keys[i], j, switchRoot, true, false,
+ switchOn, s.switchLevels[i], null);
+ }
+ }
+ }
+ } else {
+ switchRoot = (s.switchLevels[0] == 0)? this : null;
+ size = children.size();
+ for (i=0; i<size; i++) {
+ switchOn = renderChildMask.get(i);
+ traverseSwitchChild(i, null, 0, switchRoot, true, false,
+ switchOn, s.switchLevels[0], null);
+ }
+ }
+
+ // restore setLiveState
+ s.switchTargets = savedSwitchTargets;
+ s.switchStates = savedSwitchStates;
+ s.closestSwitchParents = savedClosestSwitchParents;
+ s.closestSwitchIndices = savedClosestSwitchIndices;
+ for (i=0; i< s.switchLevels.length; i++) {
+ s.switchLevels[i]--;
+ }
+ s.childSwitchLinks = savedChildSwitchLinks;
+ s.parentSwitchLink = savedParentSwitchLink;
+ s.hashkeyIndex = savedHashkeyIndex;
+ super.markAsLive();
+ }
+
+
+ void removeNodeData(SetLiveState s) {
+
+ int numChildren = children.size();
+ int i, j;
+ ArrayList switchStates;
+
+ if (refCount <= 0) {
+ // remove this node from parentSwitchLink's childSwitchLinks
+ // clear childSwitchLinks
+ ArrayList switchLinks;
+ if (parentSwitchLink != null) {
+ for(i=0; i<parentSwitchLink.childrenSwitchLinks.size();i++) {
+ switchLinks = (ArrayList)
+ parentSwitchLink.childrenSwitchLinks.get(i);
+ if (switchLinks.contains(this)) {
+ switchLinks.remove(this);
+ break;
+ }
+ }
+ }
+ for (j=0; j<numChildren; j++) {
+ switchStates = (ArrayList)childrenSwitchStates.get(j);
+ switchStates.clear();
+ }
+ switchLevels.remove(0);
+ } else {
+ // remove children dependent data
+ int hkIndex;
+
+ // Must be in reverse, to preserve right indexing.
+ for (i = s.keys.length-1; i >= 0; i--) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(hkIndex >= 0) {
+ for (j=0; j<numChildren; j++) {
+ switchStates = (ArrayList)childrenSwitchStates.get(j);
+ switchStates.remove(hkIndex);
+ }
+ switchLevels.remove(hkIndex);
+ }
+ }
+ }
+
+ super.removeNodeData(s);
+ }
+
+
+
+ // synchronized with setWhichChild and setChildMask
+ synchronized void clearLive(SetLiveState s) {
+ Targets[] savedSwitchTargets = s.switchTargets;
+ s.switchTargets = null;
+ super.clearLive(s);
+ s.switchTargets = savedSwitchTargets;
+ }
+
+ void initRenderChildMask() {
+ int i, nchildren;
+ nchildren = children.size();
+ switch(whichChild) {
+ case Switch.CHILD_ALL:
+ for (i=0; i<nchildren; i++) {
+ renderChildMask.set(i);
+ }
+ break;
+ case Switch.CHILD_NONE:
+ for (i=0; i<nchildren; i++) {
+ renderChildMask.clear(i);
+ }
+ break;
+ case Switch.CHILD_MASK:
+ for (i=0; i<nchildren; i++) {
+ if (childMask.get(i) == true) {
+ renderChildMask.set(i);
+ } else {
+ renderChildMask.clear(i);
+ }
+ }
+ break;
+ default:
+ for (i=0; i<nchildren; i++) {
+
+ if (i == whichChild) {
+ renderChildMask.set(i);
+ } else {
+ renderChildMask.clear(i);
+ }
+ }
+ }
+ }
+
+ void traverseSwitchChild(int child, HashKey key, int index,
+ SwitchRetained switchRoot, boolean init,
+ boolean swChanged, boolean switchOn,
+ int switchLevel, ArrayList updateList) {
+ int i,j,k;
+ SwitchRetained sw;
+ LinkRetained ln;
+ Object obj;
+ ArrayList childSwitchLinks;
+
+ boolean newSwChanged = false;
+ ArrayList childSwitchStates =
+ (ArrayList)childrenSwitchStates.get(child);
+ SwitchState switchState = (SwitchState)childSwitchStates.get(index);
+ switchState.updateCompositeSwitchMask(switchLevel, switchOn);
+
+ if (switchRoot != null) {
+ if (init) {
+ if (!switchState.initialized) {
+ switchState.initSwitchOn();
+ }
+ } else {
+ boolean compositeSwitchOn = switchState.evalCompositeSwitchOn();
+ if (switchState.cachedSwitchOn != compositeSwitchOn) {
+ switchState.updateCachedSwitchOn();
+
+ switchRoot.updateTargets.addCachedTargets(
+ switchState.cachedTargets);
+ newSwChanged = true;
+ updateList.add(switchState);
+ }
+ }
+ }
+
+
+ childSwitchLinks = (ArrayList)childrenSwitchLinks.get(child);
+ int cslSize =childSwitchLinks.size();
+ for (i=0; i<cslSize; i++) {
+
+ obj = childSwitchLinks.get(i);
+ if (obj instanceof SwitchRetained) {
+ sw = (SwitchRetained)obj;
+ int swSize = sw.children.size();
+ for(j=0; j<swSize; j++) {
+ sw.traverseSwitchChild(j, key, index, switchRoot, init,
+ newSwChanged, switchOn, switchLevel,
+ updateList);
+ }
+ } else { // LinkRetained
+ ln = (LinkRetained)obj;
+ if (key == null) {
+ switchKey.reset();
+ switchKey.append(locale.nodeId);
+ } else {
+ switchKey.set(key);
+ }
+ switchKey.append(LinkRetained.plus).append(ln.nodeId);
+
+ if ((ln.sharedGroup != null) &&
+ (ln.sharedGroup.localToVworldKeys != null)) {
+
+ j = switchKey.equals(ln.sharedGroup.localToVworldKeys,0,
+ ln.sharedGroup.localToVworldKeys.length);
+ if(j < 0) {
+ System.out.println("SwitchRetained : Can't find hashKey");
+ }
+
+ if (j<ln.sharedGroup.localToVworldKeys.length) {
+ int lscSize = ln.sharedGroup.children.size();
+ for(k=0; k<lscSize; k++) {
+ ln.sharedGroup.traverseSwitchChild(k, ln.sharedGroup.
+ localToVworldKeys[j],
+ j, switchRoot,
+ init, newSwChanged,
+ switchOn, switchLevel, updateList);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void traverseSwitchParent() {
+ boolean switchOn;
+ int switchLevel;
+ SwitchRetained switchRoot;
+ int i,j;
+ int size;
+
+ // first traverse this node's child
+ if (inSharedGroup) {
+ for (j=0; j<localToVworldKeys.length; j++) {
+ switchLevel = ((Integer)switchLevels.get(j)).intValue();
+ switchRoot = (switchLevel == 0)? this : null;
+ size = children.size();
+ for (i=0; i<size; i++) {
+ switchOn = renderChildMask.get(i);
+ traverseSwitchChild(i, localToVworldKeys[j], j, switchRoot,
+ true, false, switchOn, switchLevel, null);
+ }
+ }
+ } else {
+ switchLevel = ((Integer)switchLevels.get(0)).intValue();
+ switchRoot = (switchLevel == 0)? this : null;
+ size = children.size();
+ for (i=0; i<size; i++) {
+ switchOn = renderChildMask.get(i);
+ traverseSwitchChild(i, null, 0, switchRoot,
+ true, false, switchOn, switchLevel, null);
+ }
+ }
+
+ // now traverse this node's parent
+ if (parentSwitchLink != null) {
+ if (parentSwitchLink instanceof SwitchRetained) {
+ ((SwitchRetained)parentSwitchLink).traverseSwitchParent();
+ } else if (parentSwitchLink instanceof SharedGroupRetained) {
+ ((SharedGroupRetained)parentSwitchLink).traverseSwitchParent();
+ }
+ }
+ }
+
+
+ void computeCombineBounds(Bounds bounds) {
+ int i;
+ NodeRetained child;
+
+ if(boundsAutoCompute) {
+
+ if(whichChild == Switch.CHILD_ALL) {
+ for(i=0; i<children.size(); i++) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.computeCombineBounds(bounds);
+ }
+ }
+ else if(whichChild == Switch.CHILD_MASK) {
+ for(i=0; i<children.size(); i++) {
+ if(childMask.get(i)) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.computeCombineBounds(bounds);
+ }
+ }
+ }
+ else if(whichChild != Switch.CHILD_NONE) {
+ if (whichChild < children.size()) {
+ child = (NodeRetained)children.get(whichChild);
+ if(child != null)
+ child.computeCombineBounds(bounds);
+ }
+ }
+
+ }
+ else
+ // Should this be lock too ? ( MT safe ? )
+ synchronized(localBounds) {
+ bounds.combine(localBounds);
+ }
+ }
+
+
+ /**
+ * Gets the bounding object of a node.
+ * @return the node's bounding object
+ */
+ Bounds getBounds() {
+
+ int i;
+ NodeRetained child;
+
+ if(boundsAutoCompute) {
+ BoundingSphere boundingSphere = new BoundingSphere();
+ boundingSphere.setRadius(-1.0);
+
+ if(whichChild == Switch.CHILD_ALL) {
+ for(i=0; i<children.size(); i++) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.computeCombineBounds((Bounds) boundingSphere);
+ }
+ }
+ else if(whichChild == Switch.CHILD_MASK) {
+ for(i=0; i<children.size(); i++) {
+ if(childMask.get(i)) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.computeCombineBounds((Bounds) boundingSphere);
+ }
+ }
+ }
+ else if(whichChild != Switch.CHILD_NONE &&
+ whichChild >= 0 &&
+ whichChild < children.size()) {
+
+ child = (NodeRetained)children.get(whichChild);
+ if(child != null)
+ child.computeCombineBounds((Bounds) boundingSphere);
+ }
+
+ return (Bounds) boundingSphere;
+ }
+ else
+ return super.getBounds();
+ }
+
+
+ /*
+ void compile(CompileState compState) {
+ setCompiled();
+ compState.startGroup(null); // don't merge at this level
+ compileChildren(compState);
+ compState.endGroup();
+ }
+ */
+
+ /**
+ * Compiles the children of the switch, preventing shape merging at
+ * this level or above
+ */
+ void compile(CompileState compState) {
+
+
+ super.compile(compState);
+
+ // don't remove this group node
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numSwitches++;
+ }
+ }
+
+ void insertChildrenData(int index) {
+ if (childrenSwitchStates == null) {
+ childrenSwitchStates = new ArrayList(1);
+ childrenSwitchLinks = new ArrayList(1);
+ }
+
+ childrenSwitchLinks.add(index, new ArrayList(1));
+
+ ArrayList switchStates = new ArrayList(1);
+ childrenSwitchStates.add(index, switchStates);
+ if (source != null && source.isLive()) {
+ for (int i=0; i<localToVworld.length; i++) {
+ switchStates.add(new SwitchState(true));
+ }
+ }
+ }
+
+ void appendChildrenData() {
+ if (childrenSwitchStates == null) {
+ childrenSwitchStates = new ArrayList(1);
+ childrenSwitchLinks = new ArrayList(1);
+ }
+ childrenSwitchLinks.add(new ArrayList(1));
+
+ ArrayList switchStates = new ArrayList(1);
+ childrenSwitchStates.add(switchStates);
+ if (source != null && source.isLive()) {
+ for (int i=0; i<localToVworld.length; i++) {
+ switchStates.add(new SwitchState(true));
+ }
+ }
+ }
+
+ void removeChildrenData(int index) {
+ ArrayList oldSwitchStates = (ArrayList)childrenSwitchStates.get(index)
+;
+ oldSwitchStates.clear();
+ childrenSwitchStates.remove(index);
+
+ ArrayList oldSwitchLinks = (ArrayList)childrenSwitchLinks.get(index);
+ oldSwitchLinks.clear();
+ childrenSwitchLinks.remove(index);
+ }
+
+ void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) {
+
+ int numPaths = (inSharedGroup)? s.keys.length : 1;
+ s.childSwitchLinks = (ArrayList)childrenSwitchLinks.get(childIndex);
+ for (int j=0; j< numPaths; j++) {
+ s.closestSwitchIndices[j] = switchIndexCount;
+ s.closestSwitchParents[j] = this;
+ }
+ // use switchIndexCount instead of child index to avoid
+ // reordering due to add/remove child later
+ switchIndexCount++;
+
+ Targets[] newTargets = new Targets[numPaths];
+ for(int i=0; i<numPaths; i++) {
+ newTargets[i] = new Targets();
+ }
+ s.switchTargets = newTargets;
+ s.switchStates = (ArrayList)childrenSwitchStates.get(childIndex);
+
+ if(child!=null)
+ child.setLive(s);
+
+ CachedTargets cachedTargets;
+ SwitchState switchState;
+ if (! inSharedGroup) {
+ cachedTargets = s.switchTargets[0].snapShotInit();
+ switchState = (SwitchState) s.switchStates.get(0);
+ switchState.cachedTargets = cachedTargets;
+ } else {
+ for(int i=0; i<numPaths; i++) {
+ cachedTargets = s.switchTargets[i].snapShotInit();
+ switchState = (SwitchState)s.switchStates.get(
+ s.hashkeyIndex[i]);
+ switchState.cachedTargets = cachedTargets;
+ }
+ }
+ }
+
+ // ***************************
+ // TargetsInterface methods
+ // ***************************
+
+ TargetsInterface getClosestTargetsInterface(int type) {
+ return (type == TargetsInterface.SWITCH_TARGETS)?
+ (TargetsInterface)this:
+ (TargetsInterface)parentTransformLink;
+ }
+
+ public CachedTargets getCachedTargets(int type, int index, int child) {
+ if (type == TargetsInterface.SWITCH_TARGETS) {
+ ArrayList switchStates =
+ (ArrayList)childrenSwitchStates.get(child);
+ if (index < switchStates.size()) {
+ SwitchState switchState =
+ (SwitchState)switchStates.get(index);
+ return switchState.cachedTargets;
+ } else {
+ return null;
+ }
+ } else {
+ System.out.println("getCachedTargets: wrong arguments");
+ return null;
+ }
+ }
+
+ public void resetCachedTargets(int type,
+ CachedTargets[] newCtArr, int child) {
+ if (type == TargetsInterface.SWITCH_TARGETS) {
+ ArrayList switchStates = (ArrayList)childrenSwitchStates.get(
+ child);
+ if (newCtArr.length != switchStates.size()) {
+ System.out.println("resetCachedTargets: unmatched length!" +
+ newCtArr.length + " " + switchStates.size());
+ System.out.println(" resetCachedTargets: " + this);
+ }
+ SwitchState switchState;
+ for (int i=0; i<newCtArr.length; i++) {
+ switchState = (SwitchState)switchStates.get(i);
+ switchState.cachedTargets = newCtArr[i];
+ }
+ } else {
+ System.out.println("resetCachedTargets: wrong arguments");
+ }
+ }
+
+ public ArrayList getTargetsData(int type, int child) {
+ if (type == TargetsInterface.SWITCH_TARGETS) {
+ return (ArrayList)childrenSwitchStates.get(child);
+ } else {
+ System.out.println("getTargetsData: wrong arguments");
+ return null;
+ }
+ }
+
+ public int getTargetThreads(int type) {
+ System.out.println("getTargetsThreads: wrong arguments");
+ return -1;
+ }
+
+ public void updateCachedTargets(int type, CachedTargets[] newCt) {
+ System.out.println("updateCachedTarget: wrong arguments");
+ }
+
+ public void computeTargetThreads(int type, CachedTargets[] newCt) {
+ System.out.println("computeTargetThreads: wrong arguments");
+ }
+
+ public void updateTargetThreads(int type, CachedTargets[] newCt) {
+ System.out.println("updateTargetThreads: wrong arguments");
+ }
+
+ public void propagateTargetThreads(int type, int newTargetThreads) {
+ System.out.println("propagateTargetThreads: wrong arguments");
+ }
+
+ public void copyCachedTargets(int type, CachedTargets[] newCt) {
+ System.out.println("copyCachedTarget: wrong arguments");
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/SwitchState.java b/src/classes/share/javax/media/j3d/SwitchState.java
new file mode 100644
index 0000000..82bc348
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SwitchState.java
@@ -0,0 +1,112 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class SwitchState {
+ // a bitmask to track the composite switchOn state in a nested switch
+ // tree. A bit is set if a branch is switched off at the switch
+ // level specified by the position of the bit in the bitmask
+ // It is an array of long in order to support infinite deep nested
+ // switch tree
+ long compositeSwitchMask[] = new long[]{0};
+
+ // switchOn state cached in user thread
+ boolean cachedSwitchOn = true;
+
+ // switchOn state in current time, is true if compositeSwitchMask == 0
+ boolean currentSwitchOn = true;
+
+ // switchOn state in last time, is true if compositeSwitchMask == 0
+ boolean lastSwitchOn = true;
+
+ boolean initialized = false;
+
+ CachedTargets cachedTargets = null;
+
+ boolean inSwitch = false;
+
+ public SwitchState(boolean inSwitch) {
+ this.inSwitch = inSwitch;
+ initialized = !inSwitch;
+ }
+
+ void dump() {
+ System.out.println(
+ " MASK " + compositeSwitchMask[0] +
+ " CACH " + cachedSwitchOn +
+ " CURR " + currentSwitchOn +
+ " LAST " + lastSwitchOn);
+ }
+
+ void updateCompositeSwitchMask(int switchLevel, boolean switchOn) {
+ if (switchLevel < 64) {
+ if (switchOn) {
+ compositeSwitchMask[0] &= ~(1 << switchLevel);
+ } else {
+ compositeSwitchMask[0] |= (1 << switchLevel);
+ }
+ } else {
+ int i;
+ int index = switchLevel/64;
+ int offset = switchLevel%64;
+
+ if (index > compositeSwitchMask.length) {
+ long newCompositeSwitchMask[] = new long[index+1];
+ System.arraycopy(compositeSwitchMask, 0,
+ newCompositeSwitchMask, 0, index);
+ compositeSwitchMask = newCompositeSwitchMask;
+ }
+ if (switchOn) {
+ compositeSwitchMask[index] &= ~(1 << offset);
+ } else {
+ compositeSwitchMask[index] |= (1 << offset);
+ }
+ }
+ }
+
+ void initSwitchOn() {
+ boolean switchOn = evalCompositeSwitchOn();
+ currentSwitchOn = lastSwitchOn = cachedSwitchOn = switchOn;
+ //currentSwitchOn = cachedSwitchOn = switchOn;
+ initialized = true;
+ }
+
+ void updateCurrentSwitchOn() {
+ currentSwitchOn = !currentSwitchOn;
+ }
+
+ void updateLastSwitchOn() {
+ lastSwitchOn = currentSwitchOn;
+ }
+
+ void updateCachedSwitchOn() {
+ cachedSwitchOn = !cachedSwitchOn;
+ }
+
+ boolean evalCompositeSwitchOn() {
+ boolean switchOn;
+ if (compositeSwitchMask.length == 1) {
+ switchOn = (compositeSwitchMask[0] == 0);
+ } else {
+ switchOn = true;
+ for (int i=0; i<compositeSwitchMask.length; i++) {
+ if (compositeSwitchMask[i] != 0) {
+ switchOn = false;
+ break;
+ }
+ }
+ }
+ return switchOn;
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/SwitchValueInterpolator.java b/src/classes/share/javax/media/j3d/SwitchValueInterpolator.java
new file mode 100644
index 0000000..c974db1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SwitchValueInterpolator.java
@@ -0,0 +1,276 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+
+/**
+ * SwitchValueInterpolator behavior. This class defines a
+ * behavior that modifies the selected child of the target
+ * switch node by linearly interpolating between a pair of
+ * specified child index values (using the value generated
+ * by the specified Alpha object).
+ */
+
+public class SwitchValueInterpolator extends Interpolator {
+
+ Switch target;
+ int firstSwitchIndex;
+ int lastSwitchIndex;
+ int childCount;
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ SwitchValueInterpolator() {
+ }
+
+ /**
+ * Constructs a SwitchValueInterpolator behavior that varies its target
+ * Switch node's child index between 0 and <i>n</i>-1, where <i>n</i>
+ * is the number of children in the target Switch node.
+ * @param alpha the alpha object for this interpolator
+ * @param target the Switch node affected by this interpolator
+ */
+ public SwitchValueInterpolator(Alpha alpha,
+ Switch target) {
+
+ super(alpha);
+
+ this.target = target;
+ firstSwitchIndex = 0;
+ childCount = target.numChildren();
+ lastSwitchIndex = childCount - 1;
+
+ }
+
+ /**
+ * Constructs a SwitchValueInterpolator behavior that varies its target
+ * Switch node's child index between the two values provided.
+ * @param alpha the alpha object for this interpolator
+ * @param target the Switch node affected by this interpolator
+ * @param firstChildIndex the index of first child in the Switch node to
+ * select
+ * @param lastChildIndex the index of last child in the Switch node to
+ * select
+ */
+ public SwitchValueInterpolator(Alpha alpha,
+ Switch target,
+ int firstChildIndex,
+ int lastChildIndex) {
+
+ super(alpha);
+
+ this.target = target;
+ firstSwitchIndex = firstChildIndex;
+ lastSwitchIndex = lastChildIndex;
+ computeChildCount();
+ }
+
+ /**
+ * This method sets the firstChildIndex for this interpolator.
+ * @param firstIndex the new index for the first child
+ */
+ public void setFirstChildIndex(int firstIndex) {
+ firstSwitchIndex = firstIndex;
+ computeChildCount();
+ }
+
+ /**
+ * This method retrieves this interpolator's firstChildIndex.
+ * @return the interpolator's firstChildIndex
+ */
+ public int getFirstChildIndex() {
+ return this.firstSwitchIndex;
+ }
+
+ /**
+ * This method sets the lastChildIndex for this interpolator.
+ * @param lastIndex the new index for the last child
+ */
+ public void setLastChildIndex(int lastIndex) {
+ lastSwitchIndex = lastIndex;
+ computeChildCount();
+ }
+
+ /**
+ * This method retrieves this interpolator's lastSwitchIndex.
+ * @return the interpolator's maximum scale value
+ */
+ public int getLastChildIndex() {
+ return this.lastSwitchIndex;
+ }
+
+ /**
+ * This method sets the target for this interpolator.
+ * @param target the target Switch node
+ */
+ public void setTarget(Switch target) {
+ this.target = target;
+ }
+
+ /**
+ * This method retrieves this interpolator's target Switch node
+ * reference.
+ * @return the interpolator's target Switch node
+ */
+ public Switch getTarget() {
+ return target;
+ }
+
+ // The SwitchValueInterpolator's initialize routine uses the default
+ // initialization routine.
+
+ /**
+ * This method is invoked by the behavior scheduler every frame.
+ * It maps the alpha value that corresponds to the current time
+ * into a child index value and updates the specified Switch node
+ * with this new child index value.
+ * @param criteria an enumeration of the criteria that triggered
+ * this stimulus
+ */
+ public void processStimulus(Enumeration criteria) {
+ // Handle stimulus
+ WakeupCriterion criterion = passiveWakeupCriterion;
+
+ if (alpha != null) {
+ float value = alpha.value();
+
+ if (value != prevAlphaValue) {
+ int child;
+
+ if (lastSwitchIndex > firstSwitchIndex) {
+ child = (int)(firstSwitchIndex +
+ (int)(value * (childCount-1) + 0.49999999999f));
+ } else {
+ child = (int)(firstSwitchIndex -
+ (int)(value * (childCount-1) + 0.49999999999f));
+ }
+ target.setWhichChild(child);
+ prevAlphaValue = value;
+ }
+ if (!alpha.finished() && !alpha.isPaused()) {
+ criterion = defaultWakeupCriterion;
+ }
+ }
+ wakeupOn(criterion);
+ }
+
+
+ /**
+ * calculate the number of the child to manage for this switch node
+ */
+ final private void computeChildCount() {
+ if (lastSwitchIndex >= firstSwitchIndex) {
+ childCount = lastSwitchIndex - firstSwitchIndex + 1;
+ } else {
+ childCount = firstSwitchIndex - lastSwitchIndex + 1;
+ }
+ }
+
+ /**
+ * 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) {
+ SwitchValueInterpolator svi = new SwitchValueInterpolator();
+ svi.duplicateNode(this, forceDuplicate);
+ return svi;
+ }
+
+
+ /**
+ * Copies all SwitchValueInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ SwitchValueInterpolator si =
+ (SwitchValueInterpolator) originalNode;
+
+ setFirstChildIndex(si.getFirstChildIndex());
+ setLastChildIndex(si.getLastChildIndex());
+ // this reference will be updated in updateNodeReferences()
+ setTarget(si.getTarget());
+ }
+
+ /**
+ * Callback used to allow a node to check if any nodes referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any node references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding Node in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * node is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ // check Switch
+ Node n = getTarget();
+
+ if (n != null) {
+ setTarget((Switch) referenceTable.getNewObjectReference(n));
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Table.java b/src/classes/share/javax/media/j3d/Table.java
new file mode 100644
index 0000000..432b83b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Table.java
@@ -0,0 +1,84 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Used by ImageComponent for data conversions
+ */
+
+class Table extends Object {
+
+ // 2 to 8 bit data conversion
+ static final int[] table2To8Bit = {0,85,170,255};
+
+ // 3 to 8 bit data conversion
+ static final int[] table3To8Bit = {0,36,73,109,146,182,219,255};
+
+ // 4 to 8 bit data conversion
+ static final int[] table4To8Bit = {
+ 0,17,34,51,68,85,102,119,136,153,170,187,204,221,238,255};
+
+ // 5 to 8 bit data conversion
+ static final int[] table5To8Bit = {
+ 0,8,16,25,33,41,49,58,66,74,82,90,99,107,115,123,132,140,148,156,
+ 165,173,181,189,197,206,214,222,230,239,247,255};
+
+ // 8 to 4 bit data conversion
+ static final int[] table8To4Bit = {
+ 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
+ 11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,
+ 12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,
+ 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,
+ 15,15};
+
+ // 8 to 5 bit data conversion
+ static int[] table8To5Bit = {
+ 0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,
+ 3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,
+ 7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,
+ 11,11,11,11,11,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,14,
+ 14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,
+ 17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,
+ 19,19,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22,
+ 22,22,22,22,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,
+ 25,25,25,25,25,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,28,
+ 28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,
+ 31,31,31,31,31};
+
+ // 8 to 3 bit data conversion
+ static int[] table8To3Bit = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7};
+
+ // 8 to 2 bit data conversion
+ static int[] table8To2Bit = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3};
+}
diff --git a/src/classes/share/javax/media/j3d/Targets.java b/src/classes/share/javax/media/j3d/Targets.java
new file mode 100644
index 0000000..1e6c13a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Targets.java
@@ -0,0 +1,198 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+class Targets {
+
+ static final int MAX_NODELIST = 7;
+
+ static final int GEO_TARGETS = 0; // For geometryAtoms.
+ static final int ENV_TARGETS = 1; // For enviroment nodes.
+ static final int BEH_TARGETS = 2; // For behavior nodes.
+ static final int SND_TARGETS = 3; // For sound nodes.
+ static final int VPF_TARGETS = 4; // For viewPlatform nodes.
+ static final int BLN_TARGETS = 5; // For boundingLeaf nodes.
+ static final int GRP_TARGETS = 6; // For group nodes.
+
+ ArrayList[] targetList = new ArrayList[MAX_NODELIST];
+
+ void addNode(NnuId node, int targetType) {
+ if(targetList[targetType] == null)
+ targetList[targetType] = new ArrayList(1);
+
+ targetList[targetType].add(node);
+ }
+
+ void addNodeArray(NnuId[] nodeArr, int targetType) {
+ if(targetList[targetType] == null)
+ targetList[targetType] = new ArrayList(1);
+
+ targetList[targetType].add(nodeArr);
+ }
+
+
+ void removeNode(int index, int targetType) {
+ if(targetList[targetType] != null) {
+ targetList[targetType].remove(index);
+ }
+ }
+
+
+ void addNodes(ArrayList nodeList, int targetType) {
+ if(targetList[targetType] == null)
+ targetList[targetType] = new ArrayList(1);
+
+ targetList[targetType].addAll(nodeList);
+ }
+
+
+ void clearNodes() {
+ for(int i=0; i<MAX_NODELIST; i++) {
+ if (targetList[i] != null) {
+ targetList[i].clear();
+ }
+ }
+ }
+
+ CachedTargets snapShotInit() {
+
+ CachedTargets cachedTargets = new CachedTargets();
+
+
+ for(int i=0; i<MAX_NODELIST; i++) {
+ if(targetList[i] != null) {
+ int size = targetList[i].size();
+ NnuId[] nArr = new NnuId[size];
+ targetList[i].toArray(nArr);
+ cachedTargets.targetArr[i] = nArr;
+ // System.out.println("Before sort : ");
+ // NnuIdManager.printIds(cachedTargets.targetArr[i]);
+ NnuIdManager.sort((NnuId[])cachedTargets.targetArr[i]);
+ // System.out.println("After sort : ");
+ // NnuIdManager.printIds(cachedTargets.targetArr[i]);
+ } else {
+ cachedTargets.targetArr[i] = null;
+ }
+ }
+
+ clearNodes();
+
+ return cachedTargets;
+ }
+
+
+ CachedTargets snapShotAdd(CachedTargets cachedTargets) {
+
+ int i, size;
+
+ CachedTargets newCachedTargets = new CachedTargets();
+
+ for(i=0; i<MAX_NODELIST; i++) {
+ if((targetList[i] != null) && (cachedTargets.targetArr[i] == null)) {
+ size = targetList[i].size();
+ NnuId[] nArr = new NnuId[size];
+ targetList[i].toArray(nArr);
+ newCachedTargets.targetArr[i] = nArr;
+ NnuIdManager.sort(newCachedTargets.targetArr[i]);
+
+ }
+ else if((targetList[i] != null) && (cachedTargets.targetArr[i] != null)) {
+
+ size = targetList[i].size();
+ NnuId[] targetArr = new NnuId[size];
+ targetList[i].toArray(targetArr);
+ NnuIdManager.sort(targetArr);
+ newCachedTargets.targetArr[i] =
+ NnuIdManager.merge(cachedTargets.targetArr[i], targetArr);
+
+ }
+ else if((targetList[i] == null) && (cachedTargets.targetArr[i] != null)) {
+ newCachedTargets.targetArr[i] = cachedTargets.targetArr[i];
+ }
+ }
+
+ clearNodes();
+
+ return newCachedTargets;
+
+ }
+
+
+ CachedTargets snapShotRemove(CachedTargets cachedTargets) {
+
+ int i, size;
+ NnuId[] targetArr;
+
+
+ CachedTargets newCachedTargets = new CachedTargets();
+
+ for(i=0; i<MAX_NODELIST; i++) {
+
+ if((targetList[i] != null) && (cachedTargets.targetArr[i] != null)) {
+ size = targetList[i].size();
+ targetArr = new NnuId[size];
+ targetList[i].toArray(targetArr);
+ NnuIdManager.sort(targetArr);
+ newCachedTargets.targetArr[i] =
+ NnuIdManager.delete(cachedTargets.targetArr[i], targetArr);
+
+ }
+ else if((targetList[i] == null) && (cachedTargets.targetArr[i] != null)) {
+ newCachedTargets.targetArr[i] = cachedTargets.targetArr[i];
+
+ }
+ else if((targetList[i] != null) && (cachedTargets.targetArr[i] == null)) {
+ System.out.println("You can't remove something that isn't there");
+ }
+
+ }
+
+ clearNodes();
+
+ return newCachedTargets;
+
+ }
+
+ boolean isEmpty() {
+ boolean empty = true;
+
+ for (int i=0; i < MAX_NODELIST; i++) {
+ if (targetList[i] != null) {
+ empty = false;
+ break;
+ }
+ }
+ return empty;
+ }
+
+ void addCachedTargets(CachedTargets cachedTargets) {
+ for(int i=0; i<MAX_NODELIST; i++) {
+ if (cachedTargets.targetArr[i] != null ) {
+ addNodeArray(cachedTargets.targetArr[i], i);
+ }
+ }
+ }
+
+ void dump() {
+ for(int i=0; i<Targets.MAX_NODELIST; i++) {
+ if (targetList[i] != null) {
+ System.out.println(" " + CachedTargets.typeString[i]);
+ for(int j=0; j<targetList[i].size(); j++) {
+ System.out.println(" " + targetList[i].get(j));
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TargetsInterface.java b/src/classes/share/javax/media/j3d/TargetsInterface.java
new file mode 100644
index 0000000..34d2f8e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TargetsInterface.java
@@ -0,0 +1,35 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.ArrayList;
+
+
+interface TargetsInterface {
+
+ static final int TRANSFORM_TARGETS = 0;
+ static final int SWITCH_TARGETS = 1;
+
+ // used by Switch, TransformGroup and SharedGroup
+ abstract CachedTargets getCachedTargets(int type, int index, int child);
+ abstract void resetCachedTargets(int type, CachedTargets[] newCt, int child);
+ // used by TransformGroup and SharedGroup
+ abstract int getTargetThreads(int type);
+ abstract void updateCachedTargets(int type, CachedTargets[] newCt);
+ abstract void computeTargetThreads(int type, CachedTargets[] newCt);
+ abstract void updateTargetThreads(int type, CachedTargets[] newCt);
+ abstract void propagateTargetThreads(int type, int childTargetThreads);
+ abstract void copyCachedTargets(int type, CachedTargets[] newCt);
+
+ // used by Switch and SharedGroup
+ abstract ArrayList getTargetsData(int type, int index);
+}
diff --git a/src/classes/share/javax/media/j3d/TexCoordGeneration.java b/src/classes/share/javax/media/j3d/TexCoordGeneration.java
new file mode 100644
index 0000000..59ac29d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TexCoordGeneration.java
@@ -0,0 +1,644 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Vector4f;
+
+/**
+ * The TexCoordGeneration object contains all parameters needed for
+ * automatic texture coordinate generation. It is included as part
+ * of an Appearance component object.
+ * <p>
+ * Texture coordinates determine which texel in the texture map is
+ * assigned to a given vertex. Texture coordinates are interpolated
+ * between vertices, similarly to how colors are interpolated between
+ * two vertices of lines and polygons.
+ * <p>
+ * Texture coordinates consist of two, three or four coordinates.
+ * These coordinates
+ * are referred to as the <i>S</i>, <i>T</i>, <i>R</i>, and <i>Q</i>
+ * coordinates.
+ * 2D textures use the <i>S</i> and <i>T</i> coordinates. 3D textures
+ * use the <i>S</i>, <i>T</i> and <i>R</i> coordinates. The <i>Q</i>
+ * coordinate, similar to the <i>w</i> coordinate of the <i>(x, y, z, w)</i>
+ * object coordinates, is used to create homogeneous coordinates.
+ * <p>
+ * Rather than the programmer having to explicitly assign texture
+ * coordinates, Java 3D can automatically generate the texture
+ * coordinates to achieve texture mapping onto contours.
+ * The TexCoordGeneration attributes specify the functions for automatically
+ * generating texture coordinates. The texture attributes that can be
+ * defined are:
+ * <p><ul>
+ * <li>Texture format - defines whether the generated texture
+ * coordinates are 2D, 3D, or 4D:<p>
+ * <ul>
+ * <li>TEXTURE_COORDINATE_2 - generates 2D texture coordinates
+ * (S and T).<p>
+ * <li>TEXTURE_COORDINATE_3 - generates 3D texture coordinates
+ * (S, T, and R).<p>
+ * <li>TEXTURE_COORDINATE_4 - generates 4D texture coordinates
+ * (S, T, R, and Q).<p>
+ * </ul>
+ * <li>Texture generation mode - defines how the texture coordinates
+ * are generated:<p>
+ * <ul>
+ * <li>OBJECT_LINEAR - texture coordinates are generated as a linear
+ * function in object coordinates. The function used is:<p>
+ * <ul>
+ * <code>g = p<sub>1</sub>x<sub>o</sub> + p<sub>2</sub>y<sub>o</sub> + p<sub>3</sub>z<sub>o</sub> + p<sub>4</sub>w<sub>o</sub></code>
+ * <p>
+ * where<br>
+ * <ul><code>g</code> is the value computed for the coordinate.<br>
+ * <code>p<sub>1</sub></code>, <code>p<sub>2</sub></code>,
+ * <code>p<sub>3</sub></code>, and <code>p<sub>4</sub></code>
+ * are the plane equation coefficients (described below).<br>
+ * x<sub>o</sub>, y<sub>o</sub>, z<sub>o</sub>, and w<sub>o</sub> are
+ * the object coordinates of the vertex.<p>
+ * </ul></ul>
+ * <li>EYE_LINEAR - texture coordinates are generated as a linear
+ * function in eye coordinates. The function used is:<p>
+ * <ul>
+ * <code>g = p<sub>1</sub>'x<sub>e</sub> + p<sub>2</sub>'y<sub>e</sub> + p<sub>3</sub>'z<sub>e</sub> + p<sub>4</sub>'w<sub>e</sub></code>
+ * <p>
+ * where<br>
+ * <ul><code>x<sub>e</sub></code>, <code>y<sub>e</sub></code>,
+ * <code>z<sub>e</sub></code>, and w<sub>e</sub></code> are the eye
+ * coordinates of the vertex.<br>
+ * <code>p<sub>1</sub>'</code>, <code>p<sub>2</sub>'</code>,
+ * <code>p<sub>3</sub>'</code>, and <code>p<sub>4</sub>'</code>
+ * are the plane equation coefficients transformed into eye
+ * coordinates.<p>
+ * </ul></ul>
+ *
+ * <li>SPHERE_MAP - texture coordinates are generated using
+ * spherical reflection mapping in eye coordinates. Used to simulate
+ * the reflected image of a spherical environment onto a polygon.<p>
+ *
+ * <li>NORMAL_MAP - texture coordinates are generated to match
+ * vertices' normals in eye coordinates. This is only available if
+ * TextureCubeMap is available.
+ * </li><p>
+ *
+ * <li>REFLECTION_MAP - texture coordinates are generated to match
+ * vertices' reflection vectors in eye coordinates. This is only available
+ * if TextureCubeMap is available.
+ * </li><p>
+ * </ul>
+ * <li>Plane equation coefficients - defines the coefficients for the
+ * plane equations used to generate the coordinates in the
+ * OBJECT_LINEAR and EYE_LINEAR texture generation modes.
+ * The coefficients define a reference plane in either object coordinates
+ * or in eye coordinates, depending on the texture generation mode.
+ * <p>
+ * The equation coefficients are set by the <code>setPlaneS</code>,
+ * <code>setPlaneT</code>, <code>setPlaneR</code>, and <code>setPlaneQ</code>
+ * methods for each of the S, T, R, and Q coordinate functions, respectively.
+ * By default the equation coefficients are set as follows:<p>
+ * <ul>
+ * plane S = (1.0, 0.0, 0.0, 0.0)<br>
+ * plane T = (0.0, 1.0, 0.0, 0.0)<br>
+ * plane R = (0.0, 0.0, 0.0, 0.0)<br>
+ * plane Q = (0.0, 0.0, 0.0, 0.0)<p>
+ * </ul></ul>
+ * Texture coordinate generation is enabled or disabled by the
+ * <code>setEnable</code> method. When enabled, the specified
+ * texture coordinate is computed according to the generating function
+ * associated with the coordinate. When disabled, subsequent vertices
+ * take the specified texture coordinate from the current set of
+ * texture coordinates.<p>
+ *
+ * @see Canvas3D#queryProperties
+ */
+public class TexCoordGeneration extends NodeComponent {
+
+ /**
+ * Specifies that this TexCoordGeneration object allows reading its
+ * enable flag.
+ */
+ public static final int
+ ALLOW_ENABLE_READ = CapabilityBits.TEX_COORD_GENERATION_ALLOW_ENABLE_READ;
+
+ /**
+ * Specifies that this TexCoordGeneration object allows writing its
+ * enable flag.
+ */
+ public static final int
+ ALLOW_ENABLE_WRITE = CapabilityBits.TEX_COORD_GENERATION_ALLOW_ENABLE_WRITE;
+
+ /**
+ * Specifies that this TexCoordGeneration object allows reading its
+ * format information.
+ */
+ public static final int
+ ALLOW_FORMAT_READ = CapabilityBits.TEX_COORD_GENERATION_ALLOW_FORMAT_READ;
+
+ /**
+ * Specifies that this TexCoordGeneration object allows reading its
+ * mode information.
+ */
+ public static final int
+ ALLOW_MODE_READ = CapabilityBits.TEX_COORD_GENERATION_ALLOW_MODE_READ;
+
+ /**
+ * Specifies that this TexCoordGeneration object allows reading its
+ * planeS, planeR, and planeT component information.
+ */
+ public static final int
+ ALLOW_PLANE_READ = CapabilityBits.TEX_COORD_GENERATION_ALLOW_PLANE_READ;
+
+ /**
+ * Specifies that this TexCoordGeneration object allows writing its
+ * planeS, planeR, and planeT component information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_PLANE_WRITE =
+ CapabilityBits.TEX_COORD_GENERATION_ALLOW_PLANE_WRITE;
+
+ /**
+ * Generates texture coordinates as a linear function in
+ * object coordinates.
+ *
+ * @see #setGenMode
+ */
+ public static final int OBJECT_LINEAR = 0;
+ /**
+ * Generates texture coordinates as a linear function in
+ * eye coordinates.
+ *
+ * @see #setGenMode
+ */
+ public static final int EYE_LINEAR = 1;
+ /**
+ * Generates texture coordinates using a spherical reflection
+ * mapping in eye coordinates.
+ *
+ * @see #setGenMode
+ */
+ public static final int SPHERE_MAP = 2;
+ /**
+ * Generates texture coordinates that match vertices' normals in
+ * eye coordinates.
+ *
+ * @see #setGenMode
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int NORMAL_MAP = 3;
+ /**
+ * Generates texture coordinates that match vertices' reflection
+ * vectors in eye coordinates.
+ *
+ * @see #setGenMode
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int REFLECTION_MAP = 4;
+
+ // Definitions for format
+ /**
+ * Generates 2D texture coordinates (S and T).
+ *
+ * @see #setFormat
+ */
+ public static final int TEXTURE_COORDINATE_2 = 0;
+ /**
+ * Generates 3D texture coordinates (S, T, and R).
+ *
+ * @see #setFormat
+ */
+ public static final int TEXTURE_COORDINATE_3 = 1;
+ /**
+ * Generates 4D texture coordinates (S, T, R, and Q).
+ *
+ * @see #setFormat
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int TEXTURE_COORDINATE_4 = 2;
+
+ /**
+ * Constructs a TexCoordGeneration object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * enable flag : true<br>
+ * texture generation mode : OBJECT_LINEAR<br>
+ * format : TEXTURE_COORDINATE_2<br>
+ * plane S : (1,0,0,0)<br>
+ * plane T : (0,1,0,0)<br>
+ * plane R : (0,0,0,0)<br>
+ * plane Q : (0,0,0,0)<br>
+ * </ul>
+ */
+ public TexCoordGeneration() {
+ // Just use the defaults
+ }
+
+ /**
+ * Constructs a TexCoordGeneration object with the specified genMode and
+ * format.
+ * Defaults will be used for the rest of the state variables.
+ * @param genMode texture generation mode, one of: OBJECT_LINEAR,
+ * EYE_LINEAR, SPHERE_MAP, NORMAL_MAP, or REFLECTION_MAP
+ * @param format texture format, one of: TEXTURE_COORDINATE_2,
+ * TEXTURE_COORDINATE_3, or TEXTURE_COORDINATE_4
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public TexCoordGeneration(int genMode, int format) {
+ ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+ ((TexCoordGenerationRetained)this.retained).initFormat(format);
+ }
+
+ /**
+ * Constructs a TexCoordGeneration object with the specified genMode,
+ * format, and the S coordinate plane equation.
+ * Defaults will be used for the rest of the state variables.
+ * @param genMode texture generation mode, one of: OBJECT_LINEAR,
+ * EYE_LINEAR, SPHERE_MAP, NORMAL_MAP, or REFLECTION_MAP
+ * @param format texture format, one of: TEXTURE_COORDINATE_2,
+ * TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4
+ * @param planeS plane equation for the S coordinate
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public TexCoordGeneration(int genMode, int format, Vector4f planeS) {
+ ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+ ((TexCoordGenerationRetained)this.retained).initFormat(format);
+ ((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
+ }
+
+ /**
+ * Constructs a TexCoordGeneration object with the specified genMode,
+ * format, and the S and T coordinate plane equations.
+ * Defaults will be used for the rest of the state variables.
+ * @param genMode texture generation mode, one of: OBJECT_LINEAR,
+ * EYE_LINEAR, SPHERE_MAP, NORMAL_MAP, or REFLECTION_MAP
+ * @param format texture format, one of: TEXTURE_COORDINATE_2,
+ * TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4
+ * @param planeS plane equation for the S coordinate
+ * @param planeT plane equation for the T coordinate
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public TexCoordGeneration(int genMode, int format, Vector4f planeS,
+ Vector4f planeT) {
+ ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+ ((TexCoordGenerationRetained)this.retained).initFormat(format);
+ ((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
+ ((TexCoordGenerationRetained)this.retained).initPlaneT(planeT);
+ }
+
+ /**
+ * Constructs a TexCoordGeneration object with the specified genMode,
+ * format, and the S, T, and R coordinate plane equations.
+ * @param genMode texture generation mode, one of: OBJECT_LINEAR,
+ * EYE_LINEAR, SPHERE_MAP, NORMAL_MAP, or REFLECTION_MAP
+ * @param format texture format, one of: TEXTURE_COORDINATE_2,
+ * TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4
+ * @param planeS plane equation for the S coordinate
+ * @param planeT plane equation for the T coordinate
+ * @param planeR plane equation for the R coordinate
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public TexCoordGeneration(int genMode, int format, Vector4f planeS,
+ Vector4f planeT, Vector4f planeR) {
+ ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+ ((TexCoordGenerationRetained)this.retained).initFormat(format);
+ ((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
+ ((TexCoordGenerationRetained)this.retained).initPlaneT(planeT);
+ ((TexCoordGenerationRetained)this.retained).initPlaneR(planeR);
+ }
+
+ /**
+ * Constructs a TexCoordGeneration object with the specified genMode,
+ * format, and the S, T, R, and Q coordinate plane equations.
+ * @param genMode texture generation mode, one of: OBJECT_LINEAR,
+ * EYE_LINEAR, SPHERE_MAP, NORMAL_MAP, or REFLECTION_MAP
+ * @param format texture format, one of: TEXTURE_COORDINATE_2,
+ * TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4
+ * @param planeS plane equation for the S coordinate
+ * @param planeT plane equation for the T coordinate
+ * @param planeR plane equation for the R coordinate
+ * @param planeQ plane equation for the Q coordinate
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public TexCoordGeneration(int genMode, int format, Vector4f planeS,
+ Vector4f planeT, Vector4f planeR,
+ Vector4f planeQ) {
+ ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+ ((TexCoordGenerationRetained)this.retained).initFormat(format);
+ ((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
+ ((TexCoordGenerationRetained)this.retained).initPlaneT(planeT);
+ ((TexCoordGenerationRetained)this.retained).initPlaneR(planeR);
+ ((TexCoordGenerationRetained)this.retained).initPlaneQ(planeQ);
+ }
+
+ /**
+ * Enables or disables texture coordinate generation for this
+ * appearance component object.
+ * @param state true or false to enable or disable texture coordinate
+ * generation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setEnable(boolean state) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ENABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration0"));
+ if (isLive())
+ ((TexCoordGenerationRetained)this.retained).setEnable(state);
+ else
+ ((TexCoordGenerationRetained)this.retained).initEnable(state);
+ }
+
+ /**
+ * Retrieves the state of the texCoordGeneration enable flag.
+ * @return true if texture coordinate generation is enabled,
+ * false if texture coordinate generation is disabled
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getEnable() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ENABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration1"));
+ return ((TexCoordGenerationRetained)this.retained).getEnable();
+ }
+ /**
+ * Sets the TexCoordGeneration format to the specified value.
+ * @param format texture format, one of: TEXTURE_COORDINATE_2,
+ * TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void setFormat(int format) {
+ checkForLiveOrCompiled();
+ ((TexCoordGenerationRetained)this.retained).initFormat(format);
+
+ }
+
+ /**
+ * Retrieves the current TexCoordGeneration format.
+ * @return the texture format
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getFormat() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_FORMAT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration2"));
+ return ((TexCoordGenerationRetained)this.retained).getFormat();
+ }
+
+ /**
+ * Sets the TexCoordGeneration generation mode to the specified value.
+ * @param genMode texture generation mode, one of: OBJECT_LINEAR,
+ * EYE_LINEAR, SPHERE_MAP, NORMAL_MAP, or REFLECTION_MAP.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @exception IllegalArgumentException if <code>genMode</code> is
+ * a value other than <code>OBJECT_LINEAR</code>, <code>EYE_LINEAR</code>,
+ * <code>SPHERE_MAP</code>, <code>NORMAL_MAP</code>, or
+ * <code>REFLECTION_MAP</code>.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public void setGenMode(int genMode) {
+ checkForLiveOrCompiled();
+
+ if ((genMode < OBJECT_LINEAR) || (genMode > REFLECTION_MAP)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TexCoordGeneration5"));
+ }
+ ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+ }
+
+ /**
+ * Retrieves the current TexCoordGeneration generation mode.
+ * @return the texture generation mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getGenMode() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration3"));
+ return ((TexCoordGenerationRetained)this.retained).getGenMode();
+ }
+
+ /**
+ * Sets the S coordinate plane equation. This plane equation
+ * is used to generate the S coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeS plane equation for the S coordinate
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPlaneS(Vector4f planeS) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration6"));
+
+ if (isLive())
+ ((TexCoordGenerationRetained)this.retained).setPlaneS(planeS);
+ else
+ ((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the S coordinate.
+ * @param planeS the S coordinate plane equation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getPlaneS(Vector4f planeS) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration4"));
+ ((TexCoordGenerationRetained)this.retained).getPlaneS(planeS);
+ }
+
+ /**
+ * Sets the T coordinate plane equation. This plane equation
+ * is used to generate the T coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeT plane equation for the T coordinate
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPlaneT(Vector4f planeT) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration6"));
+
+ if (isLive())
+ ((TexCoordGenerationRetained)this.retained).setPlaneT(planeT);
+ else
+ ((TexCoordGenerationRetained)this.retained).initPlaneT(planeT);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the T coordinate.
+ * @param planeT the T coordinate plane equation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getPlaneT(Vector4f planeT) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration4"));
+ ((TexCoordGenerationRetained)this.retained).getPlaneT(planeT);
+ }
+
+ /**
+ * Sets the R coordinate plane equation. This plane equation
+ * is used to generate the R coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeR plane equation for the R coordinate
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPlaneR(Vector4f planeR) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration6"));
+
+ if (isLive())
+ ((TexCoordGenerationRetained)this.retained).setPlaneR(planeR);
+ else
+ ((TexCoordGenerationRetained)this.retained).initPlaneR(planeR);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the R coordinate.
+ * @param planeR the R coordinate plane equation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getPlaneR(Vector4f planeR) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration4"));
+ ((TexCoordGenerationRetained)this.retained).getPlaneR(planeR);
+ }
+
+ /**
+ * Sets the Q coordinate plane equation. This plane equation
+ * is used to generate the Q coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeQ plane equation for the Q coordinate
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void setPlaneQ(Vector4f planeQ) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration6"));
+
+ if (isLive())
+ ((TexCoordGenerationRetained)this.retained).setPlaneQ(planeQ);
+ else
+ ((TexCoordGenerationRetained)this.retained).initPlaneQ(planeQ);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the Q coordinate.
+ * @param planeQ the Q coordinate plane equation
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getPlaneQ(Vector4f planeQ) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PLANE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TexCoordGeneration4"));
+ ((TexCoordGenerationRetained)this.retained).getPlaneQ(planeQ);
+ }
+
+ /**
+ * Creates a retained mode TexCoordGenerationRetained object that this
+ * TexCoordGeneration component object will point to.
+ */
+ void createRetained() {
+ this.retained = new TexCoordGenerationRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ TexCoordGeneration tga = new TexCoordGeneration();
+ tga.duplicateNodeComponent(this);
+ return tga;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ TexCoordGenerationRetained tex = (TexCoordGenerationRetained)
+ originalNodeComponent.retained;
+ TexCoordGenerationRetained rt = (TexCoordGenerationRetained) retained;
+
+ Vector4f v = new Vector4f();
+
+ rt.initGenMode(tex.getGenMode());
+ tex.getPlaneS(v);
+ rt.initPlaneS(v);
+ tex.getPlaneT(v);
+ rt.initPlaneT(v);
+ tex.getPlaneR(v);
+ rt.initPlaneR(v);
+ tex.getPlaneQ(v);
+ rt.initPlaneQ(v);
+ rt.initFormat(tex.getFormat());
+ rt.initEnable(tex.getEnable());
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TexCoordGenerationRetained.java b/src/classes/share/javax/media/j3d/TexCoordGenerationRetained.java
new file mode 100644
index 0000000..7f5acb7
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TexCoordGenerationRetained.java
@@ -0,0 +1,423 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Vector4f;
+import java.util.ArrayList;
+
+/**
+ * The TexCoordGeneration object contains all parameters needed for texture
+ * coordinate generation. It is included as part of an Appearance
+ * component object.
+ */
+class TexCoordGenerationRetained extends NodeComponentRetained {
+
+ // A list of pre-defined bits to indicate which component
+ // in this TexCoordGeneration object changed.
+ private static final int ENABLE_CHANGED = 0x01;
+ private static final int PLANE_S_CHANGED = 0x02;
+ private static final int PLANE_T_CHANGED = 0x04;
+ private static final int PLANE_R_CHANGED = 0x08;
+ private static final int PLANE_Q_CHANGED = 0x10;
+
+ //
+ // State variables
+ //
+ int genMode = TexCoordGeneration.OBJECT_LINEAR;
+ int format = TexCoordGeneration.TEXTURE_COORDINATE_2;
+
+ Vector4f planeS = new Vector4f(1.0f, 0.0f, 0.0f, 0.0f);
+ Vector4f planeT = new Vector4f(0.0f, 1.0f, 0.0f, 0.0f);
+ Vector4f planeR = new Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
+ Vector4f planeQ = new Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
+
+ /**
+ * Flag to enable/disable Texture coordinate generation.
+ */
+ boolean enable = true;
+
+ // true when mirror texCoord component set
+ boolean mirrorCompDirty = false;
+
+ /**
+ * Enables or disables texture coordinate generation for this
+ * appearance component object.
+ * @param state true or false to enable or disable texture coordinate
+ * generation
+ */
+ final void initEnable(boolean state) {
+ enable = state;
+ }
+ /**
+ * Enables or disables texture coordinate generation for this
+ * appearance component object and sends a message notifying
+ * the interested structures of the change.
+ * @param state true or false to enable or disable texture coordinate
+ * generation
+ */
+ final void setEnable(boolean state) {
+ initEnable(state);
+ sendMessage(ENABLE_CHANGED, (state ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Retrieves the state of the texCoordGeneration enable flag.
+ * @return true if texture coordinate generation is enabled,
+ * false if texture coordinate generation is disabled
+ */
+ final boolean getEnable() {
+ return enable;
+ }
+ /**
+ * Sets the TexCoordGeneration format to the specified value.
+ * @param format texture format, one of: TEXTURE_COORDINATE_2
+ * or TEXTURE_COORDINATE_3
+ */
+ final void initFormat(int format) {
+ this.format = format;
+ }
+
+ /**
+ * Retrieves the current TexCoordGeneration format.
+ * @return the texture format
+ */
+ final int getFormat() {
+ return format;
+ }
+
+ /**
+ * Sets the TexCoordGeneration generation mode to the specified value.
+ * @param genMode texture generation mode, one of: OBJECT_LINEAR,
+ * EYE_LINEAR, or SPHERE_MAP
+ */
+ final void initGenMode(int genMode) {
+ this.genMode = genMode;
+ }
+
+ /**
+ * Retrieves the current TexCoordGeneration generation mode.
+ * @return the texture generation mode
+ */
+ final int getGenMode() {
+ return genMode;
+ }
+
+ /**
+ * Sets the S coordinate plane equation. This plane equation
+ * is used to generate the S coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeS plane equation for the S coordinate
+ */
+ final void setPlaneS(Vector4f planeS) {
+ initPlaneS(planeS);
+ sendMessage(PLANE_S_CHANGED, new Vector4f(planeS));
+ }
+
+ /**
+ * Sets the S coordinate plane equation. This plane equation
+ * is used to generate the S coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeS plane equation for the S coordinate
+ */
+ final void initPlaneS(Vector4f planeS) {
+ this.planeS.set(planeS);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the S coordinate.
+ * @param planeS the S coordinate plane equation
+ */
+ final void getPlaneS(Vector4f planeS) {
+ planeS.set(this.planeS);
+ }
+
+ /**
+ * Sets the T coordinate plane equation. This plane equation
+ * is used to generate the T coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeT plane equation for the T coordinate
+ */
+ final void setPlaneT(Vector4f planeT) {
+ initPlaneT(planeT);
+ sendMessage(PLANE_T_CHANGED, new Vector4f(planeT));
+ }
+
+ /**
+ * Sets the T coordinate plane equation. This plane equation
+ * is used to generate the T coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeT plane equation for the T coordinate
+ */
+ final void initPlaneT(Vector4f planeT) {
+ this.planeT.set(planeT);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the T coordinate.
+ * @param planeT the T coordinate plane equation
+ */
+ final void getPlaneT(Vector4f planeT) {
+ planeT.set(this.planeT);
+ }
+
+ /**
+ * Sets the R coordinate plane equation. This plane equation
+ * is used to generate the R coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeR plane equation for the R coordinate
+ */
+ final void setPlaneR(Vector4f planeR) {
+ initPlaneR(planeR);
+ sendMessage(PLANE_R_CHANGED, new Vector4f(planeR));
+ }
+
+ /**
+ * Sets the R coordinate plane equation. This plane equation
+ * is used to generate the R coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeR plane equation for the R coordinate
+ */
+ final void initPlaneR(Vector4f planeR) {
+ this.planeR.set(planeR);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the R coordinate.
+ * @param planeR the R coordinate plane equation
+ */
+ final void getPlaneR(Vector4f planeR) {
+ planeR.set(this.planeR);
+ }
+
+ /**
+ * Sets the Q coordinate plane equation. This plane equation
+ * is used to generate the Q coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeQ plane equation for the Q coordinate
+ */
+ final void setPlaneQ(Vector4f planeQ) {
+ initPlaneQ(planeQ);
+ sendMessage(PLANE_Q_CHANGED, new Vector4f(planeQ));
+ }
+
+ /**
+ * Sets the Q coordinate plane equation. This plane equation
+ * is used to generate the Q coordinate in OBJECT_LINEAR and EYE_LINEAR
+ * texture generation modes.
+ * @param planeQ plane equation for the Q coordinate
+ */
+ final void initPlaneQ(Vector4f planeQ) {
+ this.planeQ.set(planeQ);
+ }
+
+ /**
+ * Retrieves a copy of the plane equation used to
+ * generate the Q coordinate.
+ * @param planeQ the Q coordinate plane equation
+ */
+ final void getPlaneQ(Vector4f planeQ) {
+ planeQ.set(this.planeQ);
+ }
+
+
+
+ /**
+ * Creates a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror= this;
+ } else {
+ TexCoordGenerationRetained mirrorTg = new TexCoordGenerationRetained();
+ mirrorTg.set(this);
+ mirrorTg.source = source;
+ mirror = mirrorTg;
+ }
+ } else {
+ ((TexCoordGenerationRetained) mirror).set(this);
+ }
+ }
+
+ /**
+ * These two methods update the native context,
+ * trans contains eyeTovworld transform in d3d
+ * trans contains vworldToEye transform in ogl
+ */
+ native void updateNative(long ctx,
+ boolean enable, int genMode, int format,
+ float planeSx, float planeSy, float planeSz, float planeSw,
+ float planeTx, float planeTy, float planeTz, float planeTw,
+ float planeRx, float planeRy, float planeRz, float planeRw,
+ float planeQx, float planeQy, float planeQz, float planeQw,
+ double[] trans);
+
+
+ void updateNative(Canvas3D cv) {
+ int gMode = genMode;
+ Transform3D trans = null;
+ Transform3D m = cv.vworldToEc;
+
+ if (((cv.textureExtendedFeatures & Canvas3D.TEXTURE_CUBE_MAP) == 0) &&
+ ((genMode == TexCoordGeneration.NORMAL_MAP) ||
+ (genMode == TexCoordGeneration.REFLECTION_MAP))) {
+ gMode = TexCoordGeneration.SPHERE_MAP;
+ }
+
+ if (VirtualUniverse.mc.isD3D() &&
+ (gMode == TexCoordGeneration.EYE_LINEAR)) {
+ trans = VirtualUniverse.mc.getTransform3D(cv.vworldToEc);
+ trans.invert();
+ m = trans;
+ }
+
+ updateNative(cv.ctx,
+ enable, gMode, format, planeS.x, planeS.y, planeS.z,
+ planeS.w, planeT.x, planeT.y, planeT.z, planeT.w,
+ planeR.x, planeR.y, planeR.z, planeR.w,
+ planeQ.x, planeQ.y, planeQ.z, planeQ.w,
+ m.mat);
+
+ if (trans != null) {
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ trans);
+ }
+ }
+
+ /**
+ * Initializes a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ ((TexCoordGenerationRetained)mirror).set(this);
+ }
+
+ /** Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ TexCoordGenerationRetained mirrorTc = (TexCoordGenerationRetained) mirror;
+
+ mirrorTc.mirrorCompDirty = true;
+
+ if ((component & ENABLE_CHANGED) != 0) {
+ mirrorTc.enable = ((Boolean)value).booleanValue();
+ }
+ else if ((component & PLANE_S_CHANGED) != 0) {
+ mirrorTc.planeS = (Vector4f)value;
+ }
+ else if ((component & PLANE_T_CHANGED) != 0) {
+ mirrorTc.planeT = (Vector4f)value;
+ }
+ else if ((component & PLANE_R_CHANGED) != 0) {
+ mirrorTc.planeR = (Vector4f)value;
+ }
+ else if ((component & PLANE_Q_CHANGED) != 0) {
+ mirrorTc.planeQ = (Vector4f)value;
+ }
+ }
+
+
+ boolean equivalent(TexCoordGenerationRetained tr) {
+
+ if (tr == null) {
+ return (false);
+
+ } else if ((this.changedFrequent != 0) || (tr.changedFrequent != 0)) {
+ return (this == tr);
+ }
+
+ return ((tr.genMode == genMode) &&
+ (tr.format == format) &&
+ (tr.enable == enable) &&
+ tr.planeS.equals(planeS) &&
+ tr.planeT.equals(planeT) &&
+ tr.planeR.equals(planeR));
+ }
+
+ protected Object clone() {
+ TexCoordGenerationRetained tr = (TexCoordGenerationRetained)super.clone();
+ tr.planeS = new Vector4f(planeS);
+ tr.planeT = new Vector4f(planeT);
+ tr.planeR = new Vector4f(planeR);
+ // other attributes is copied in super.clone()
+ return tr;
+
+ }
+
+ protected void set(TexCoordGenerationRetained tr) {
+ super.set(tr);
+ genMode = tr.genMode;
+ format = tr.format;
+ enable = tr.enable;
+ planeS.set(tr.planeS);
+ planeT.set(tr.planeT);
+ planeR.set(tr.planeR);
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.TEXCOORDGENERATION_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.TEXCOORDGENERATION_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ void handleFrequencyChange(int bit) {
+ switch (bit) {
+ case TexCoordGeneration.ALLOW_ENABLE_WRITE:
+ case TexCoordGeneration.ALLOW_PLANE_WRITE: {
+ setFrequencyChangeMask(bit, bit);
+ }
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Text3D.java b/src/classes/share/javax/media/j3d/Text3D.java
new file mode 100644
index 0000000..5c3e4b6
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Text3D.java
@@ -0,0 +1,619 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Point3f;
+
+/**
+ * A Text3D object is a text string that has been converted to 3D
+ * geometry. The Font3D object determines the appearance of the
+ * Text3D NodeComponent object. Each Text3D object has the following
+ * parameters:<P>
+ * <UL>
+ * <LI>Font3D object - describes the font style of the text string,
+ * such as the font family (Helvetica, Courier, etc.), style (Italic,
+ * bold, etc.), and point size. The size of the resulting characters will
+ * be equal to the point size. For example, a 12 point font will result in
+ * a Font3D with characters 12 meters tall. </LI><P>
+ * <LI>Text string - the text string to be written.</LI><P>
+ * <LI>Position - determines the initial placement of the Text3D string
+ * in three-space.</LI><P>
+ * <LI>Alignment - specifies how glyphs in the string are placed in
+ * relation to the position parameter. Valid values are:
+ * <UL>
+ * <LI> ALIGN_CENTER - the center of the string is placed on the
+ * <code>position</code> point.</LI>
+ * <LI> ALIGN_FIRST - the first character of the string is placed on
+ * the <code>position</code> point.</LI>
+ * <LI> ALIGN_LAST - the last character of the string is placed on the
+ * <code>position</code> point.</LI>
+ * </UL><P>
+ * <LI>Path - specifies how succeeding glyphs in the string are placed
+ * in relation to the previous glyph. Valid values are:</LI><P>
+ * <UL>
+ * <LI> PATH_LEFT - succeeding glyphs are placed to the left of the
+ * current glyph.</LI>
+ * <LI> PATH_RIGHT - succeeding glyphs are placed to the right of the
+ * current glyph.</LI>
+ * <LI> PATH_UP - succeeding glyphs are placed above the current glyph.</LI>
+ * <LI> PATH_DOWN - succeeding glyphs are placed below the current glyph.</LI>
+ * </UL><P>
+ * <LI>Character spacing - the space between characters. This spacing is
+ * in addition to the regular spacing between glyphs as defined in the
+ * Font object.</LI></UL><P>
+ *
+ * @see Font3D
+ */
+public class Text3D extends Geometry {
+
+ /**
+ * Specifies that this Text3D object allows
+ * reading the Font3D component information.
+ *
+ * @see Font3D
+ */
+ public static final int
+ ALLOW_FONT3D_READ = CapabilityBits.TEXT3D_ALLOW_FONT3D_READ;
+
+ /**
+ * Specifies that this Text3D object allows
+ * writing the Font3D component information.
+ *
+ * @see Font3D
+ */
+ public static final int
+ ALLOW_FONT3D_WRITE = CapabilityBits.TEXT3D_ALLOW_FONT3D_WRITE;
+
+ /**
+ * Specifies that this Text3D object allows
+ * reading the String object.
+ */
+ public static final int
+ ALLOW_STRING_READ = CapabilityBits.TEXT3D_ALLOW_STRING_READ;
+
+ /**
+ * Specifies that this Text3D object allows
+ * writing the String object.
+ */
+ public static final int
+ ALLOW_STRING_WRITE = CapabilityBits.TEXT3D_ALLOW_STRING_WRITE;
+
+ /**
+ * Specifies that this Text3D object allows
+ * reading the text position value.
+ */
+ public static final int
+ ALLOW_POSITION_READ = CapabilityBits.TEXT3D_ALLOW_POSITION_READ;
+
+ /**
+ * Specifies that this Text3D object allows
+ * writing the text position value.
+ */
+ public static final int
+ ALLOW_POSITION_WRITE = CapabilityBits.TEXT3D_ALLOW_POSITION_WRITE;
+
+ /**
+ * Specifies that this Text3D object allows
+ * reading the text alignment value.
+ */
+ public static final int
+ ALLOW_ALIGNMENT_READ = CapabilityBits.TEXT3D_ALLOW_ALIGNMENT_READ;
+
+ /**
+ * Specifies that this Text3D object allows
+ * writing the text alignment value.
+ */
+ public static final int
+ ALLOW_ALIGNMENT_WRITE = CapabilityBits.TEXT3D_ALLOW_ALIGNMENT_WRITE;
+
+ /**
+ * Specifies that this Text3D object allows
+ * reading the text path value.
+ */
+ public static final int
+ ALLOW_PATH_READ = CapabilityBits.TEXT3D_ALLOW_PATH_READ;
+
+ /**
+ * Specifies that this Text3D object allows
+ * writing the text path value.
+ */
+ public static final int
+ ALLOW_PATH_WRITE = CapabilityBits.TEXT3D_ALLOW_PATH_WRITE;
+
+ /**
+ * Specifies that this Text3D object allows
+ * reading the text character spacing value.
+ */
+ public static final int
+ ALLOW_CHARACTER_SPACING_READ = CapabilityBits.TEXT3D_ALLOW_CHARACTER_SPACING_READ;
+
+ /**
+ * Specifies that this Text3D object allows
+ * writing the text character spacing value.
+ */
+ public static final int
+ ALLOW_CHARACTER_SPACING_WRITE = CapabilityBits.TEXT3D_ALLOW_CHARACTER_SPACING_WRITE;
+
+ /**
+ * Specifies that this Text3D object allows
+ * reading the text string bounding box value
+ */
+ public static final int
+ ALLOW_BOUNDING_BOX_READ = CapabilityBits.TEXT3D_ALLOW_BOUNDING_BOX_READ;
+
+ /**
+ * <code>alignment</code>: the center of the string is placed on the
+ * <code>position</code> point.
+ *
+ * @see #getAlignment
+ */
+ public static final int ALIGN_CENTER = 0;
+
+ /**
+ * <code>alignment</code>: the first character of the string is placed
+ * on the <code>position</code> point.
+ *
+ * @see #getAlignment
+ */
+ public static final int ALIGN_FIRST = 1;
+
+ /**
+ * <code>alignment</code>: the last character of the string is placed
+ * on the <code>position</code> point.
+ *
+ * @see #getAlignment
+ */
+ public static final int ALIGN_LAST = 2;
+
+ /**
+ * <code>path</code>: succeeding glyphs are placed to the left of
+ * the current glyph.
+ *
+ * @see #getPath
+ */
+ public static final int PATH_LEFT = 0;
+ /**
+ * <code>path</code>: succeeding glyphs are placed to the left of
+ * the current glyph.
+ *
+ * @see #getPath
+ */
+ public static final int PATH_RIGHT = 1;
+
+ /**
+ * <code>path</code>: succeeding glyphs are placed above the
+ * current glyph.
+ *
+ * @see #getPath
+ */
+ public static final int PATH_UP = 2;
+
+ /**
+ * <code>path</code>: succeeding glyphs are placed below the
+ * current glyph.
+ *
+ * @see #getPath
+ */
+ public static final int PATH_DOWN = 3;
+
+ /**
+ * Constructs a Text3D object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * font 3D : null<br>
+ * string : null<br>
+ * position : (0,0,0)<br>
+ * alignment : ALIGN_FIRST<br>
+ * path : PATH_RIGHT<br>
+ * character spacing : 0.0<br>
+ * </ul>
+ */
+ public Text3D() {
+ }
+
+ /**
+ * Creates a Text3D object with the given Font3D object.
+ *
+ * @see Font3D
+ */
+ public Text3D(Font3D font3D) {
+ ((Text3DRetained)this.retained).setFont3D(font3D);
+ }
+
+ /**
+ * Creates a Text3D object given a Font3D object and a string. The
+ * string is converted into 3D glyphs. The first glyph from the
+ * string is placed at (0.0, 0.0, 0.0) and succeeding glyphs are
+ * placed to the right of the initial glyph.
+ *
+ * @see Font3D
+ */
+ public Text3D(Font3D font3D, String string) {
+ ((Text3DRetained)this.retained).setFont3D(font3D);
+ ((Text3DRetained)this.retained).setString(string);
+ }
+
+ /**
+ * Creates a Text3D object given a Font3D, a string and position. The
+ * string is converted into 3D glyphs. The first glyph from the
+ * string is placed at position <code>position</code> and succeeding
+ * glyphs are placed to the right of the initial glyph.
+ *
+ * @see Font3D
+ */
+ public Text3D(Font3D font3D, String string, Point3f position) {
+ ((Text3DRetained)this.retained).setFont3D(font3D);
+ ((Text3DRetained)this.retained).setString(string);
+ ((Text3DRetained)this.retained).setPosition(position);
+ }
+
+ /**
+ * Creates a Text3D object given a Font3D, string, position, alignment
+ * and path along which string is to be placed. The
+ * string is converted into 3D glyphs. The placement of the glyphs
+ * with respect to the <code>position</code> position depends on
+ * the alignment parameter and the path parameter.
+ *
+ * @see Font3D
+ */
+ public Text3D(Font3D font3D, String string, Point3f position,
+ int alignment, int path) {
+ ((Text3DRetained)this.retained).setFont3D(font3D);
+ ((Text3DRetained)this.retained).setString(string);
+ ((Text3DRetained)this.retained).setPosition(position);
+ ((Text3DRetained)this.retained).setAlignment(alignment);
+ ((Text3DRetained)this.retained).setPath(path);
+ }
+
+ /**
+ * Creates the retained mode Text3DRetained object that this
+ * Text3D component object will point to.
+ */
+ void createRetained() {
+ this.retained = new Text3DRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Returns the Font3D objects used by this Text3D NodeComponent object.
+ *
+ * @return the Font3D object of this Text3D node - null if no Font3D
+ * has been associated with this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Font3D getFont3D() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_FONT3D_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D0"));
+ return ((Text3DRetained)this.retained).getFont3D();
+
+ }
+
+ /**
+ * Sets the Font3D object used by this Text3D NodeComponent object.
+ *
+ * @param font3d the Font3D object to associate with this Text3D node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setFont3D(Font3D font3d) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_FONT3D_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D1"));
+ ((Text3DRetained)this.retained).setFont3D(font3d);
+
+ }
+
+ /**
+ * Copies the character string used in the construction of the
+ * Text3D node into the supplied parameter.
+ *
+ * @return a copy of the String object in this Text3D node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public String getString() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_STRING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D2"));
+ return ((Text3DRetained)this.retained).getString();
+ }
+
+ /**
+ * Copies the character string from the supplied parameter into the
+ * Text3D node.
+ *
+ * @param string the String object to recieve the Text3D node's string.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setString(String string) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_STRING_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D3"));
+ ((Text3DRetained)this.retained).setString(string);
+ }
+
+ /**
+ * Copies the node's <code>position</code> field into the supplied
+ * parameter. The <code>position</code> is used to determine the
+ * initial placement of the Text3D string. The position, combined with
+ * the path and alignment control how the text is displayed.
+ *
+ * @param position the point to position the text.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getAlignment
+ * @see #getPath
+ */
+ public void getPosition(Point3f position) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D4"));
+ ((Text3DRetained)this.retained).getPosition(position);
+ }
+
+ /**
+ * Sets the node's <code>position</code> field to the supplied
+ * parameter. The <code>position</code> is used to determine the
+ * initial placement of the Text3D string. The position, combined with
+ * the path and alignment control how the text is displayed.
+ *
+ * @param position the point to position the text.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getAlignment
+ * @see #getPath
+ */
+ public void setPosition(Point3f position) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POSITION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D5"));
+ ((Text3DRetained)this.retained).setPosition(position);
+ }
+
+ /**
+ * Retrieves the text alignment policy for this Text3D NodeComponent
+ * object. The <code>alignment</code> is used to specify how
+ * glyphs in the string are placed in relation to the
+ * <code>position</code> field. Valid values for this field
+ * are:
+ * <UL>
+ * <LI> ALIGN_CENTER - the center of the string is placed on the
+ * <code>position</code> point.
+ * <LI> ALIGN_FIRST - the first character of the string is placed on
+ * the <code>position</code> point.
+ * <LI> ALIGN_LAST - the last character of the string is placed on the
+ * <code>position</code> point.
+ * </UL>
+ * The default value of this field is <code>ALIGN_FIRST</code>.
+ *
+ * @return the current alingment policy for this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getPosition
+ */
+ public int getAlignment() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ALIGNMENT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D6"));
+ return ((Text3DRetained)this.retained).getAlignment();
+ }
+
+ /**
+ * Sets the text alignment policy for this Text3D NodeComponent
+ * object. The <code>alignment</code> is used to specify how
+ * glyphs in the string are placed in relation to the
+ * <code>position</code> field. Valid values for this field
+ * are:
+ * <UL>
+ * <LI> ALIGN_CENTER - the center of the string is placed on the
+ * <code>position</code> point.
+ * <LI> ALIGN_FIRST - the first character of the string is placed on
+ * the <code>position</code> point.
+ * <LI> ALIGN_LAST - the last character of the string is placed on the
+ * <code>position</code> point.
+ * </UL>
+ * The default value of this field is <code>ALIGN_FIRST</code>.
+ *
+ * @param alignment specifies how glyphs in the string are placed
+ * in relation to the position field
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getPosition
+ */
+ public void setAlignment(int alignment) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_ALIGNMENT_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D7"));
+ ((Text3DRetained)this.retained).setAlignment(alignment);
+ }
+
+ /**
+ * Retrieves the node's <code>path</code> field. This field
+ * is used to specify how succeeding
+ * glyphs in the string are placed in relation to the previous glyph.
+ * Valid values for this field are:
+ * <UL>
+ * <LI> PATH_LEFT: - succeeding glyphs are placed to the left of the
+ * current glyph.
+ * <LI> PATH_RIGHT: - succeeding glyphs are placed to the right of the
+ * current glyph.
+ * <LI> PATH_UP: - succeeding glyphs are placed above the current glyph.
+ * <LI> PATH_DOWN: - succeeding glyphs are placed below the current glyph.
+ * </UL>
+ * The default value of this field is <code>PATH_RIGHT</code>.
+ *
+ * @return the current alingment policy for this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getPath() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PATH_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D8"));
+ return ((Text3DRetained)this.retained).getPath();
+ }
+
+ /**
+ * Sets the node's <code>path</code> field. This field
+ * is used to specify how succeeding
+ * glyphs in the string are placed in relation to the previous glyph.
+ * Valid values for this field are:
+ * <UL>
+ * <LI> PATH_LEFT - succeeding glyphs are placed to the left of the
+ * current glyph.
+ * <LI> PATH_RIGHT - succeeding glyphs are placed to the right of the
+ * current glyph.
+ * <LI> PATH_UP - succeeding glyphs are placed above the current glyph.
+ * <LI> PATH_DOWN - succeeding glyphs are placed below the current glyph.
+ * </UL>
+ * The default value of this field is <code>PATH_RIGHT</code>.
+ *
+ * @param path the value to set the path to
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setPath(int path) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_PATH_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D9"));
+ ((Text3DRetained)this.retained).setPath(path);
+ }
+
+ /**
+ * Retrieves the 3D bounding box that encloses this Text3D object.
+ *
+ * @param bounds the object to copy the bounding information to.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see BoundingBox
+ */
+ public void getBoundingBox(BoundingBox bounds) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_BOUNDING_BOX_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D10"));
+ ((Text3DRetained)this.retained).getBoundingBox(bounds);
+ }
+
+ /**
+ * Retrieves the character spacing used to construct the Text3D string.
+ * This spacing is in addition to the regular spacing between glyphs as
+ * defined in the Font object. 1.0 in this space is measured as the
+ * width of the largest glyph in the 2D Font. The default value is
+ * 0.0.
+ *
+ * @return the current character spacing value
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getCharacterSpacing() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHARACTER_SPACING_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D11"));
+ return ((Text3DRetained)this.retained).getCharacterSpacing();
+ }
+
+ /**
+ * Sets the character spacing used when constructing the Text3D string.
+ * This spacing is in addition to the regular spacing between glyphs as
+ * defined in the Font object. 1.0 in this space is measured as the
+ * width of the largest glyph in the 2D Font. The default value is
+ * 0.0.
+ *
+ * @param characterSpacing the new character spacing value
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setCharacterSpacing(float characterSpacing) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_CHARACTER_SPACING_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Text3D12"));
+ ((Text3DRetained)this.retained).setCharacterSpacing(characterSpacing);
+ }
+
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ Text3D t = new Text3D();
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ Text3DRetained text = (Text3DRetained) originalNodeComponent.retained;
+ Text3DRetained rt = (Text3DRetained) retained;
+
+ Font3D font3D = text.getFont3D();
+ if (font3D != null) {
+ rt.setFont3D(font3D);
+ }
+
+ String s = text.getString();
+ if (s != null) {
+ rt.setString(s);
+ }
+
+ Point3f p = new Point3f();
+ text.getPosition(p);
+ rt.setPosition(p);
+ rt.setAlignment(text.getAlignment());
+ rt.setPath(text.getPath());
+ rt.setCharacterSpacing(text.getCharacterSpacing());
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Text3DRenderMethod.java b/src/classes/share/javax/media/j3d/Text3DRenderMethod.java
new file mode 100644
index 0000000..8ff96a3
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Text3DRenderMethod.java
@@ -0,0 +1,109 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The RenderMethod interface is used to create various ways to render
+ * different geometries.
+ */
+
+class Text3DRenderMethod implements RenderMethod {
+
+ /**
+ * The actual rendering code for this RenderMethod
+ */
+ public boolean render(RenderMolecule rm, Canvas3D cv, int pass,
+ RenderAtomListInfo ra, int dirtyBits) {
+ boolean isNonUniformScale;
+ Transform3D trans = null;
+
+ GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry();
+ geo.setVertexFormat((rm.useAlpha && ((geo.vertexFormat &
+ GeometryArray.COLOR) != 0)),
+ rm.textureBin.attributeBin.ignoreVertexColors, cv.ctx);
+
+ if (rm.doInfinite) {
+ cv.updateState(pass, dirtyBits);
+ while (ra != null) {
+ trans = ra.infLocalToVworld;
+ isNonUniformScale = !trans.isCongruent();
+ cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat, trans);
+
+ ra.geometry().execute(cv, ra.renderAtom, isNonUniformScale,
+ (rm.useAlpha && ra.geometry().noAlpha),
+ rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.ignoreVertexColors,
+ pass);
+ ra = ra.next;
+ }
+ return true;
+ }
+
+ boolean isVisible = false; // True if any of the RAs is visible.
+ while (ra != null) {
+ if (cv.ra == ra.renderAtom) {
+ if (cv.raIsVisible) {
+ cv.updateState(pass, dirtyBits);
+ trans = ra.localToVworld;
+ isNonUniformScale = !trans.isCongruent();
+
+ cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat, trans);
+ ra.geometry().execute(cv, ra.renderAtom, isNonUniformScale,
+ (rm.useAlpha && ra.geometry().noAlpha),
+ rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.
+ ignoreVertexColors,
+ pass);
+ isVisible = true;
+ }
+ }
+ else {
+ if (ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) {
+ cv.updateState(pass, dirtyBits);
+ cv.raIsVisible = true;
+ trans = ra.localToVworld;
+ isNonUniformScale = !trans.isCongruent();
+
+ cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat, trans);
+ ra.geometry().execute(cv, ra.renderAtom, isNonUniformScale,
+ (rm.useAlpha && ra.geometry().noAlpha),
+ rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.
+ ignoreVertexColors,
+ pass);
+ isVisible = true;
+ }
+ else {
+ cv.raIsVisible = false;
+ }
+ cv.ra = ra.renderAtom;
+ }
+
+ ra = ra.next;
+
+ }
+
+ geo.disableGlobalAlpha(cv.ctx,
+ (rm.useAlpha && ((geo.vertexFormat &
+ GeometryArray.COLOR) != 0)),
+ rm.textureBin.attributeBin.ignoreVertexColors);
+
+ return isVisible;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Text3DRetained.java b/src/classes/share/javax/media/j3d/Text3DRetained.java
new file mode 100644
index 0000000..81631b6
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Text3DRetained.java
@@ -0,0 +1,985 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.awt.font.*;
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+
+/**
+ * Implements Text3D class.
+ */
+class Text3DRetained extends GeometryRetained {
+ /**
+ * Packaged scope variables needed for implementation
+ */
+ Font3D font3D = null;
+ String string = null;
+ Point3f position = new Point3f(0.0f, 0.0f, 0.0f);
+ int alignment = Text3D.ALIGN_FIRST, path = Text3D.PATH_RIGHT;
+ float charSpacing = 0.0f;
+ int numChars = 0;
+ static final int targetThreads = (J3dThread.UPDATE_TRANSFORM |
+ J3dThread.UPDATE_GEOMETRY |
+ J3dThread.UPDATE_RENDER);
+ /**
+ * The temporary transforms for this Text3D
+ */
+ Transform3D[] charTransforms = new Transform3D[0];
+
+ /**
+ * A cached list of geometry arrays for the current settings
+ */
+ GeometryArrayRetained[] geometryList = new GeometryArrayRetained[0];
+ GlyphVector[] glyphVecs = new GlyphVector[0];
+
+ /**
+ * Bounding box data for this text string.
+ */
+ Point3d lower = new Point3d();
+ Point3d upper = new Point3d();
+
+
+ /**
+ * An Array list used for messages
+ */
+ ArrayList newGeometryAtomList = new ArrayList();
+ ArrayList oldGeometryAtomList = new ArrayList();
+
+
+ /**
+ * temporary model view matrix for immediate mode only
+ */
+ Transform3D vpcToEc;
+ Transform3D drawTransform;
+
+
+ Text3DRetained(){
+ this.geoType = GEO_TYPE_TEXT3D;
+ }
+
+
+ synchronized void computeBoundingBox() {
+ Point3d l = new Point3d();
+ Point3d u = new Point3d();
+ Vector3f location = new Vector3f(this.position);
+ int i, k=0, numTotal=0;
+ double width = 0, height = 0;
+ Rectangle2D bounds;
+
+ //Reset bounds data
+ l.set(location);
+ u.set(location);
+
+ if (numChars != 0) {
+ // Set loop counters based on path type
+ if (path == Text3D.PATH_RIGHT || path == Text3D.PATH_UP) {
+ k = 0;
+ numTotal = numChars + 1;
+ } else if (path == Text3D.PATH_LEFT || path == Text3D.PATH_DOWN) {
+ k = 1;
+ numTotal = numChars;
+ // Reset bounds to bounding box if first character
+ bounds = glyphVecs[0].getVisualBounds();
+ u.x += bounds.getWidth();
+ u.y += bounds.getHeight();
+ }
+
+ for (i=1; i<numTotal; i++, k++) {
+ width = glyphVecs[k].getLogicalBounds().getWidth();
+ bounds = glyphVecs[k].getVisualBounds();
+ // 'switch' could be outside loop with little hacking,
+ width += charSpacing;
+ height = bounds.getHeight();
+
+ switch (this.path) {
+ case Text3D.PATH_RIGHT:
+ u.x += (width);
+ if (u.y < (height + location.y)) {
+ u.y = location.y + height;
+ }
+ break;
+ case Text3D.PATH_LEFT:
+ l.x -= (width);
+ if (u.y < ( height + location.y)) {
+ u.y = location.y + height;
+ }
+ break;
+ case Text3D.PATH_UP:
+ u.y += height;
+ if (u.x < (bounds.getWidth() + location.x)) {
+ u.x = location.x + bounds.getWidth();
+ }
+ break;
+ case Text3D.PATH_DOWN:
+ l.y -= height;
+ if (u.x < (bounds.getWidth() + location.x)) {
+ u.x = location.x + bounds.getWidth();
+ }
+ break;
+ }
+ }
+
+ // Handle string alignment. ALIGN_FIRST is handled by default
+ if (alignment != Text3D.ALIGN_FIRST) {
+ double cx = (u.x - l.x);
+ double cy = (u.y - l.y);
+
+ if (alignment == Text3D.ALIGN_CENTER) {
+ cx *= .5;
+ cy *= .5;
+ }
+ switch (path) {
+ case Text3D.PATH_RIGHT:
+ l.x -= cx;
+ u.x -= cx;
+ break;
+ case Text3D.PATH_LEFT:
+ l.x += cx;
+ u.x += cx;
+ break;
+ case Text3D.PATH_UP:
+ l.y -= cy;
+ u.y -= cy;
+ break;
+ case Text3D.PATH_DOWN:
+ l.y += cy;
+ u.y += cy;
+ break;
+
+ }
+ }
+ }
+
+ l.z = 0.0f;
+ if ((font3D == null) || (font3D.fontExtrusion == null)) {
+ u.z = l.z;
+ } else {
+ u.z = l.z + font3D.fontExtrusion.length;
+ }
+ }
+
+ void update() {}
+
+
+ /**
+ * Returns the Font3D objects used by this Text3D NodeComponent object.
+ *
+ * @return the Font3D object of this Text3D node - null if no Font3D
+ * has been associated with this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final Font3D getFont3D() {
+ return this.font3D;
+ }
+
+ /**
+ * Sets the Font3D object used by this Text3D NodeComponent object.
+ *
+ * @param font3d the Font3D object to associate with this Text3D node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final void setFont3D(Font3D font3d) {
+ geomLock.getLock();
+ this.font3D = font3d;
+ updateCharacterData();
+ geomLock.unLock();
+ sendDataChangedMessage();
+ }
+
+ /**
+ * Copies the character string used in the construction of the
+ * Text3D node into the supplied parameter.
+ *
+ * @return a copy of the String object in this Text3D node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final String getString() {
+ return this.string;
+ }
+
+ /**
+ * Copies the character string from the supplied parameter into Tex3D
+ * node.
+ *
+ * @param string the String object to recieve the Text3D node's string.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final void setString(String string) {
+ geomLock.getLock();
+ this.string = string;
+ if (string == null) {
+ numChars = 0;
+ } else {
+ numChars = string.length();
+ }
+ updateCharacterData();
+ geomLock.unLock();
+ sendDataChangedMessage();
+ }
+
+ /**
+ * Copies the node's <code>position</code> field into the supplied
+ * parameter. The <code>position</code> is used to determine the
+ * initial placement of the Text3D string. The position, combined with
+ * the path and alignment control how the text is displayed.
+ *
+ * @param position the point to position the text.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getAlignment
+ * @see #getPath
+ */
+ final void getPosition(Point3f position) {
+ position.set(this.position);
+ }
+
+ /**
+ * Sets the node's <code>position</code> field to the supplied
+ * parameter. The <code>position</code> is used to determine the
+ * initial placement of the Text3D string. The position, combined with
+ * the path and alignment control how the text is displayed.
+ *
+ * @param position the point to position the text.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getAlignment
+ * @see #getPath
+ */
+ final void setPosition(Point3f position) {
+ geomLock.getLock();
+ this.position.set(position);
+ updateTransformData();
+ geomLock.unLock();
+ sendTransformChangedMessage();
+ }
+
+ /**
+ * Retrieves the text alignment policy for this Text3D NodeComponent
+ * object. The <code>alignment</code> is used to specify how
+ * glyphs in the string are placed in relation to the
+ * <code>position</code> field. Valid values for this field
+ * are:
+ * <UL>
+ * <LI> ALIGN_CENTER - the center of the string is placed on the
+ * <code>position</code> point.
+ * <LI> ALIGN_FIRST - the first character of the string is placed on
+ * the <code>position</code> point.
+ * <LI> ALIGN_LAST - the last character of the string is placed on the
+ * <code>position</code> point.
+ * </UL>
+ * The default value of this field is <code>ALIGN_FIRST</code>.
+ *
+ * @return the current alingment policy for this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getPosition
+ */
+ final int getAlignment() {
+ return alignment;
+ }
+
+ /**
+ * Sets the text alignment policy for this Text3D NodeComponent
+ * object. The <code>alignment</code> is used to specify how
+ * glyphs in the string are placed in relation to the
+ * <code>position</code> field. Valid values for this field
+ * are:
+ * <UL>
+ * <LI> ALIGN_CENTER - the center of the string is placed on the
+ * <code>position</code> point.
+ * <LI> ALIGN_FIRST - the first character of the string is placed on
+ * the <code>position</code> point.
+ * <LI> ALIGN_LAST - the last character of the string is placed on the
+ * <code>position</code> point.
+ * </UL>
+ * The default value of this field is <code>ALIGN_FIRST</code>.
+ *
+ * @return the current alingment policy for this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see #getPosition
+ */
+ final void setAlignment(int alignment) {
+ geomLock.getLock();
+ this.alignment = alignment;
+ updateTransformData();
+ geomLock.unLock();
+ sendTransformChangedMessage();
+ }
+
+ /**
+ * Retrieves the node's <code>path</code> field. This field
+ * is used to specify how succeeding
+ * glyphs in the string are placed in relation to the previous glyph.
+ * Valid values for this field are:
+ * <UL>
+ * <LI> PATH_LEFT: - succeeding glyphs are placed to the left of the
+ * current glyph.
+ * <LI> PATH_RIGHT: - succeeding glyphs are placed to the right of the
+ * current glyph.
+ * <LI> PATH_UP: - succeeding glyphs are placed above the current glyph.
+ * <LI> PATH_DOWN: - succeeding glyphs are placed below the current glyph.
+ * </UL>
+ * The default value of this field is <code>PATH_RIGHT</code>.
+ *
+ * @return the current alingment policy for this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final int getPath() {
+ return this.path;
+ }
+
+ /**
+ * Sets the node's <code>path</code> field. This field
+ * is used to specify how succeeding
+ * glyphs in the string are placed in relation to the previous glyph.
+ * Valid values for this field are:
+ * <UL>
+ * <LI> PATH_LEFT - succeeding glyphs are placed to the left of the
+ * current glyph.
+ * <LI> PATH_RIGHT - succeeding glyphs are placed to the right of the
+ * current glyph.
+ * <LI> PATH_UP - succeeding glyphs are placed above the current glyph.
+ * <LI> PATH_DOWN - succeeding glyphs are placed below the current glyph.
+ * </UL>
+ * The default value of this field is <code>PATH_RIGHT</code>.
+ *
+ * @param path the value to set the path to.
+ *
+ * @return the current alingment policy for this node.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final void setPath(int path) {
+ this.path = path;
+ updateTransformData();
+ sendTransformChangedMessage();
+ }
+
+ /**
+ * Retrieves the 3D bounding box that encloses this Text3D object.
+ *
+ * @param bounds the object to copy the bounding information to.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see BoundingBox
+ */
+ final void getBoundingBox(BoundingBox bounds) {
+ synchronized (this) {
+ bounds.setLower(lower);
+ bounds.setUpper(upper);
+ }
+ }
+
+ /**
+ * Retrieves the character spacing used to construct the Text3D string.
+ * This spacing is in addition to the regular spacing between glyphs as
+ * defined in the Font object. 1.0 in this space is measured as the
+ * width of the largest glyph in the 2D Font. The default value is
+ * 0.0.
+ *
+ * @return the current character spacing value
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final float getCharacterSpacing() {
+ return charSpacing;
+ }
+
+ /**
+ * Sets the character spacing used hwne constructing the Text3D string.
+ * This spacing is in addition to the regular spacing between glyphs as
+ * defined in the Font object. 1.0 in this space is measured as the
+ * width of the largest glyph in the 2D Font. The default value is
+ * 0.0.
+ *
+ * @param characterSpacing the new character spacing value
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ final void setCharacterSpacing(float characterSpacing) {
+ geomLock.getLock();
+ this.charSpacing = characterSpacing;
+ updateTransformData();
+ geomLock.unLock();
+ sendTransformChangedMessage();
+ }
+
+
+ final void sendDataChangedMessage() {
+ J3dMessage[] m;
+ int i, j, k, kk, numMessages;
+ int gSize;
+ ArrayList shapeList, gaList;
+ Shape3DRetained s;
+ GeometryAtom[] newGeometryAtoms;
+ ArrayList tiArrList = new ArrayList();
+ ArrayList newCtArrArrList = new ArrayList();
+
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+ synchronized (universeList) {
+ numMessages = universeList.size();
+ m = new J3dMessage[numMessages];
+ for (i=0; i<numMessages; i++) {
+ m[i] = VirtualUniverse.mc.getMessage();
+ m[i].type = J3dMessage.TEXT3D_DATA_CHANGED;
+ m[i].threads = targetThreads;
+ shapeList = (ArrayList)userLists.get(i);
+ newGeometryAtomList.clear();
+ oldGeometryAtomList.clear();
+
+ for (j=0; j<shapeList.size(); j++) {
+ s = (Shape3DRetained)shapeList.get(j);
+ if (s.boundsAutoCompute) {
+ // update combine bounds of mirrorShape3Ds. So we need to
+ // use its bounds and not localBounds.
+ // bounds is actually a reference to
+ // mirrorShape3D.source.localBounds.
+ // TODO : Should only need to update distinct localBounds.
+ s.getCombineBounds((BoundingBox)s.bounds);
+ }
+
+ gSize = s.geometryList.size();
+
+ GeometryAtom oldGA = Shape3DRetained.getGeomAtom(s);
+ GeometryAtom newGA = new GeometryAtom();
+
+ int geometryCnt = 0;
+ for(k = 0; k<gSize; k++) {
+ GeometryRetained geomRetained =
+ (GeometryRetained) s.geometryList.get(k);
+ if(geomRetained != null) {
+ Text3DRetained tempT3d = (Text3DRetained)geomRetained;
+ geometryCnt += tempT3d.numChars;
+ }
+ else {
+ // Slightly wasteful, but not quite worth to optimize yet.
+ geometryCnt++;
+ }
+ }
+
+ newGA.geometryArray = new GeometryRetained[geometryCnt];
+ newGA.lastLocalTransformArray = new Transform3D[geometryCnt];
+ // Reset geometryCnt;
+ geometryCnt = 0;
+
+ newGA.locale = s.locale;
+ newGA.visible = s.visible;
+ newGA.source = s;
+ int gaCnt=0;
+ GeometryRetained geometry = null;
+ for(; gaCnt<gSize; gaCnt++) {
+ geometry = (GeometryRetained) s.geometryList.get(gaCnt);
+ if(geometry != null) {
+ newGA.geoType = geometry.geoType;
+ newGA.alphaEditable = s.isAlphaEditable(geometry);
+ break;
+ }
+ }
+
+ for(; gaCnt<gSize; gaCnt++) {
+ geometry = (GeometryRetained) s.geometryList.get(gaCnt);
+ if(geometry == null) {
+ newGA.geometryArray[gaCnt] = null;
+ }
+ else {
+ Text3DRetained t = (Text3DRetained)geometry;
+ GeometryRetained geo;
+ for (k=0; k<t.numChars; k++, geometryCnt++) {
+ geo = t.geometryList[k];
+ if (geo != null) {
+ newGA.geometryArray[geometryCnt] = geo;
+ newGA.lastLocalTransformArray[geometryCnt] =
+ t.charTransforms[k];
+
+ } else {
+ newGA.geometryArray[geometryCnt] = null;
+ newGA.lastLocalTransformArray[geometryCnt] = null;
+ }
+
+ }
+
+ }
+ }
+
+ oldGeometryAtomList.add(oldGA);
+ newGeometryAtomList.add(newGA);
+ Shape3DRetained.setGeomAtom(s, newGA);
+ }
+
+ Object[] oldGAArray = oldGeometryAtomList.toArray();
+ Object[] newGAArray = newGeometryAtomList.toArray();
+ ArrayList uniqueList = getUniqueSource(shapeList);
+ int numSrc = uniqueList.size();
+ int numMS3D;
+ Shape3DRetained ms, src;
+
+ for (j=0; j<numSrc; j++) {
+ CachedTargets[] newCtArr = null;
+ src = (Shape3DRetained)uniqueList.get(j);
+ numMS3D = src.mirrorShape3D.size();
+
+ TargetsInterface ti = ((GroupRetained)src.
+ parent).getClosestTargetsInterface(
+ TargetsInterface.TRANSFORM_TARGETS);
+
+ if (ti != null) {
+ CachedTargets ct;
+ newCtArr = new CachedTargets[numMS3D];
+
+ for (k=0; k<numMS3D; k++) {
+ ms = (Shape3DRetained)src.mirrorShape3D.get(k);
+
+ GeometryAtom ga =
+ Shape3DRetained.getGeomAtom(ms);
+ for(kk=0; kk<newGAArray.length; kk++) {
+ if(ga == newGAArray[kk]) {
+ break;
+ }
+ }
+
+ if(kk==newGAArray.length) {
+ System.out.println("Text3DRetained : Problem !!! Can't find matching geomAtom");
+ }
+
+ ct = ti.getCachedTargets(TargetsInterface.
+ TRANSFORM_TARGETS, k, -1);
+ if (ct != null) {
+ newCtArr[k] = new CachedTargets();
+ newCtArr[k].copy(ct);
+ newCtArr[k].replace((NnuId)oldGAArray[kk],
+ (NnuId)newGAArray[kk],
+ Targets.GEO_TARGETS);
+ } else {
+ newCtArr[k] = null;
+ }
+
+ }
+
+ ti.resetCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, newCtArr, -1);
+
+ tiArrList.add(ti);
+ newCtArrArrList.add(newCtArr);
+
+ }
+
+ }
+
+ m[i].args[0] = oldGAArray;
+ m[i].args[1] = newGAArray;
+ m[i].universe = (VirtualUniverse)universeList.get(i);
+
+ if(tiArrList.size() > 0) {
+ m[i].args[2] = tiArrList.toArray();
+ m[i].args[3] = newCtArrArrList.toArray();
+ }
+
+ tiArrList.clear();
+ newCtArrArrList.clear();
+
+ }
+ VirtualUniverse.mc.processMessage(m);
+ }
+
+ }
+ }
+ }
+
+
+ final void sendTransformChangedMessage() {
+ J3dMessage[] m;
+ int i, j, numMessages, sCnt;
+ ArrayList shapeList;
+ ArrayList gaList = new ArrayList();
+ Shape3DRetained s;
+ GeometryRetained geomR;
+ synchronized(liveStateLock) {
+ if (source.isLive()) {
+ synchronized (universeList) {
+ numMessages = universeList.size();
+ m = new J3dMessage[numMessages];
+ for (i=0; i<numMessages; i++) {
+ m[i] = VirtualUniverse.mc.getMessage();
+ m[i].type = J3dMessage.TEXT3D_TRANSFORM_CHANGED;
+ m[i].threads = targetThreads;
+ shapeList = (ArrayList)userLists.get(i);
+ // gaList = new GeometryAtom[shapeList.size() * numChars];
+ for (j=0; j<shapeList.size(); j++) {
+ s = (Shape3DRetained)shapeList.get(j);
+
+ // Find the right geometry.
+ for(sCnt=0; sCnt<s.geometryList.size(); sCnt++) {
+ geomR = (GeometryRetained) s.geometryList.get(sCnt);
+ if(geomR == this) {
+ break;
+ }
+ }
+
+ if(sCnt < s.geometryList.size())
+ gaList.add(Shape3DRetained.getGeomAtom(s));
+
+ }
+ m[i].args[0] = gaList.toArray();
+ m[i].args[1] = charTransforms;
+ m[i].universe = (VirtualUniverse)universeList.get(i);
+ }
+ VirtualUniverse.mc.processMessage(m);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update internal reprsentation of tranform matrices and geometry.
+ * This method will be called whenever string or font3D change.
+ */
+ final void updateCharacterData() {
+ char c[] = new char[1];
+
+ if (geometryList.length != numChars) {
+ geometryList = new GeometryArrayRetained[numChars];
+ glyphVecs = new GlyphVector[numChars];
+ }
+
+ if (font3D != null) {
+ for (int i=0; i<numChars; i++) {
+ c[0] = string.charAt(i);
+ glyphVecs[i] = font3D.font.createGlyphVector(font3D.frc, c);
+ geometryList[i] = font3D.triangulateGlyphs(glyphVecs[i], c[0]);
+ }
+ }
+
+ updateTransformData();
+ }
+
+ /**
+ * Update per character transform based on Text3D location,
+ * per character size and path.
+ *
+ * WARNING: Caller of this method must make sure SceneGraph is live,
+ * else exceptions may be thrown.
+ */
+ final void updateTransformData(){
+ int i, k=0, numTotal=0;
+ double width = 0, height = 0;
+ Vector3f location = new Vector3f(this.position);
+ Rectangle2D bounds;
+
+ //Reset bounds data
+ lower.set(location);
+ upper.set(location);
+
+ charTransforms = new Transform3D[numChars];
+ for (i=0; i<numChars; i++) {
+ charTransforms[i] = VirtualUniverse.mc.getTransform3D(null);
+ }
+
+ if (numChars != 0) {
+ charTransforms[0].set(location);
+
+ // Set loop counters based on path type
+ if (path == Text3D.PATH_RIGHT || path == Text3D.PATH_UP) {
+ k = 0;
+ numTotal = numChars + 1;
+ } else if (path == Text3D.PATH_LEFT || path == Text3D.PATH_DOWN) {
+ k = 1;
+ numTotal = numChars;
+ // Reset bounds to bounding box if first character
+ bounds = glyphVecs[0].getVisualBounds();
+ upper.x += bounds.getWidth();
+ upper.y += bounds.getHeight();
+ }
+
+ for (i=1; i<numTotal; i++, k++) {
+ width = glyphVecs[k].getLogicalBounds().getWidth();
+ bounds = glyphVecs[k].getVisualBounds();
+ // 'switch' could be outside loop with little hacking,
+ width += charSpacing;
+ height = bounds.getHeight();
+
+ switch (this.path) {
+ case Text3D.PATH_RIGHT:
+ location.x += width;
+ upper.x += (width);
+ if (upper.y < (height + location.y)) {
+ upper.y = location.y + height;
+ }
+ break;
+ case Text3D.PATH_LEFT:
+ location.x -= width;
+ lower.x -= (width);
+ if (upper.y < ( height + location.y)) {
+ upper.y = location.y + height;
+ }
+ break;
+ case Text3D.PATH_UP:
+ location.y += height;
+ upper.y += height;
+ if (upper.x < (bounds.getWidth() + location.x)) {
+ upper.x = location.x + bounds.getWidth();
+ }
+ break;
+ case Text3D.PATH_DOWN:
+ location.y -= height;
+ lower.y -= height;
+ if (upper.x < (bounds.getWidth() + location.x)) {
+ upper.x = location.x + bounds.getWidth();
+ }
+ break;
+ }
+ if (i < numChars) {
+ charTransforms[i].set(location);
+ }
+ }
+
+ // Handle string alignment. ALIGN_FIRST is handled by default
+ if (alignment != Text3D.ALIGN_FIRST) {
+ double cx = (upper.x - lower.x);
+ double cy = (upper.y - lower.y);
+
+ if (alignment == Text3D.ALIGN_CENTER) {
+ cx *= .5;
+ cy *= .5;
+ }
+ switch (path) {
+ case Text3D.PATH_RIGHT:
+ for (i=0;i < numChars;i++) {
+ charTransforms[i].mat[3] -= cx;
+ }
+ lower.x -= cx;
+ upper.x -= cx;
+ break;
+ case Text3D.PATH_LEFT:
+ for (i=0;i < numChars;i++) {
+ charTransforms[i].mat[3] += cx;
+ }
+ lower.x += cx;
+ upper.x += cx;
+ break;
+
+ case Text3D.PATH_UP:
+ for (i=0;i < numChars;i++) {
+ charTransforms[i].mat[7] -=cy;
+ }
+ lower.y -= cy;
+ upper.y -= cy;
+ break;
+ case Text3D.PATH_DOWN:
+ for (i=0;i < numChars;i++) {
+ charTransforms[i].mat[7] +=cy;
+ }
+ lower.y += cy;
+ upper.y += cy;
+ break;
+
+ }
+ }
+ }
+
+ lower.z = 0.0f;
+ if ((font3D == null) || (font3D.fontExtrusion == null)) {
+ upper.z = lower.z;
+ } else {
+ upper.z = lower.z + font3D.fontExtrusion.length;
+ }
+
+ // update geoBounds
+ getBoundingBox(geoBounds);
+ }
+
+
+ /**
+ * This method is called when the SceneGraph becomes live. All characters
+ * used by this.string are tesselated in this method, to avoid wait during
+ * traversal and rendering.
+ */
+ void setLive(boolean inBackgroundGroup, int refCount) {
+ // Tesselate all character data and update character transforms
+ updateCharacterData();
+ super.doSetLive(inBackgroundGroup, refCount);
+ super.markAsLive();
+ }
+
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Transform3D tempT3D = VirtualUniverse.mc.getTransform3D(null);
+ GeometryArrayRetained geo = null;
+ int sIndex = -1;
+ PickShape newPS;
+ double x = 0, y = 0, z = 0;
+ double minDist = Double.MAX_VALUE;
+
+ for (int i=0; i < numChars; i++) {
+ geo= geometryList[i];
+ if (geo != null) {
+ tempT3D.invert(charTransforms[i]);
+ newPS = pickShape.transform(tempT3D);
+ if (geo.intersect(newPS, dist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (dist[0] < minDist) {
+ sIndex = i;
+ minDist = dist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ }
+
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, tempT3D);
+
+ if (sIndex >= 0) {
+ // We need to transform iPnt to the vworld to compute the actual distance.
+ // In this method we'll transform iPnt by its char. offset. Shape3D will
+ // do the localToVworld transform.
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ dist[0] = minDist;
+ charTransforms[sIndex].transform(iPnt);
+ return true;
+ }
+ return false;
+ }
+
+ boolean intersect(Point3d[] pnts) {
+ Transform3D tempT3D = VirtualUniverse.mc.getTransform3D(null);
+ GeometryArrayRetained ga;
+ boolean isIntersect = false;
+ Point3d transPnts[] = new Point3d[pnts.length];
+ for (int j=pnts.length-1; j >= 0; j--) {
+ transPnts[j] = new Point3d();
+ }
+
+ for (int i=numChars-1; i >= 0; i--) {
+ ga = geometryList[i];
+ if ( ga != null) {
+ tempT3D.invert(charTransforms[i]);
+ for (int j=pnts.length-1; j >= 0; j--) {
+ tempT3D.transform(pnts[j], transPnts[j]);
+ }
+ if (ga.intersect(transPnts)) {
+ isIntersect = true;
+ break;
+ }
+ }
+ }
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, tempT3D);
+ return isIntersect;
+ }
+
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) {
+ GeometryArrayRetained ga;
+
+ for (int i=numChars-1; i >=0; i--) {
+ ga = geometryList[i];
+ if ((ga != null) && ga.intersect(thisToOtherVworld, geom)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ boolean intersect(Bounds targetBound) {
+ GeometryArrayRetained ga;
+
+ for (int i=numChars-1; i >=0; i--) {
+ ga = geometryList[i];
+ if ((ga != null) && ga.intersect(targetBound)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ void setModelViewMatrix(Transform3D vpcToEc, Transform3D drawTransform) {
+ this.vpcToEc = vpcToEc;
+ this.drawTransform = drawTransform;
+ }
+
+
+ void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
+ boolean updateAlpha, float alpha, boolean multiScreen,
+ int screen,
+ boolean ignoreVertexColors, int pass) {
+
+ Transform3D trans = VirtualUniverse.mc.getTransform3D(null);
+
+ for (int i = 0; i < geometryList.length; i++) {
+ trans.set(drawTransform);
+ trans.mul(charTransforms[i]);
+ cv.setModelViewMatrix(cv.ctx, vpcToEc.mat, trans);
+ geometryList[i].execute(cv, ra, isNonUniformScale, updateAlpha, alpha,
+ multiScreen, screen, ignoreVertexColors,
+ pass);
+ }
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, trans);
+ }
+
+ int getClassType() {
+ return TEXT3D_TYPE;
+ }
+
+
+ ArrayList getUniqueSource(ArrayList shapeList) {
+ ArrayList uniqueList = new ArrayList();
+ int size = shapeList.size();
+ Object src;
+ int i, index;
+
+ for (i=0; i<size; i++) {
+ src = ((Shape3DRetained)shapeList.get(i)).sourceNode;
+ index = uniqueList.indexOf(src);
+ if (index == -1) {
+ uniqueList.add(src);
+ }
+ }
+ return uniqueList;
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/Texture.java b/src/classes/share/javax/media/j3d/Texture.java
new file mode 100644
index 0000000..12b7e9c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Texture.java
@@ -0,0 +1,1749 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Hashtable;
+
+/**
+ * The Texture object is a component object of an Appearance object
+ * that defines the texture properties used when texture mapping is
+ * enabled. The Texture object is an abstract class and all texture
+ * objects must be created as either a Texture2D object or a
+ * Texture3D object.
+ * <P>
+ * Each Texture object has the following properties:<P>
+ * <UL>
+ * <LI>Boundary color - the texture boundary color. The texture
+ * boundary color is used when the boundaryModeS and boundaryModeT
+ * parameters are set to CLAMP or CLAMP_TO_BOUNDARY and if the texture
+ * boundary is not specified. </LI><P>
+ * <LI>Boundary Width - the texture boundary width. If the texture boundary
+ * width is > 0, then all images for all mipmap levels will include boundary
+ * texels. The actual texture image for level 0, for example, will be of
+ * dimension (width + 2*boundaryWidth) * (height + 2*boundaryWidth).
+ * The boundary texels will be used when linear filtering is to be applied.
+ * </LI><p>
+ * <LI>Boundary ModeS and Boundary ModeT - the boundary mode for the
+ * S and T coordinates, respectively. The boundary modes are as
+ * follows:</LI><P>
+ * <UL>
+ * <LI>CLAMP - clamps texture coordinates to be in the range [0,1].
+ * Texture boundary texels or the constant boundary color if boundary width
+ * is 0 will be used for U,V values that fall outside this range.</LI><P>
+ * <LI>WRAP - repeats the texture by wrapping texture coordinates
+ * that are outside the range [0,1]. Only the fractional portion
+ * of the texture coordinates is used. The integer portion is
+ * discarded</LI><P>
+ * <LI>CLAMP_TO_EDGE - clamps texture coordinates such that filtering
+ * will not sample a texture boundary texel. Texels at the edge of the
+ * texture will be used instead.</LI><P>
+ * <LI>CLAMP_TO_BOUNDARY - clamps texture coordinates such that filtering
+ * will sample only texture boundary texels, that is, it will never
+ * get some samples from the boundary and some from the edge. This
+ * will ensure clean unfiltered boundaries. If the texture does not
+ * have a boundary, that is the boundary width is equal to 0, then the
+ * constant boundary color will be used.</LI></P>
+ * </UL>
+ * <LI>Image - an image or an array of images for all the mipmap
+ * levels. If only one image is provided, the MIPmap mode must be
+ * set to BASE_LEVEL.</LI><P>
+ * <LI>Magnification filter - the magnification filter function.
+ * Used when the pixel being rendered maps to an area less than or
+ * equal to one texel. The magnification filter functions are as
+ * follows:</LI><P>
+ * <UL>
+ * <LI>FASTEST - uses the fastest available method for processing
+ * geometry.</LI><P>
+ * <LI>NICEST - uses the nicest available method for processing
+ * geometry.</LI><P>
+ * <LI>BASE_LEVEL_POINT - selects the nearest texel in the base level
+ * texture image.</LI><P>
+ * <LI>BASE_LEVEL_LINEAR - performs a bilinear interpolation on the four
+ * nearest texels in the base level texture image. The texture value T' is
+ * computed as follows:</LI><P>
+ * <UL>
+ * i<sub>0</sub> = trunc(u - 0.5)<P>
+ * j<sub>0</sub> = trunc(v - 0.5)<P>
+ * i<sub>1</sub> = i<sub>0</sub> + 1<P>
+ * j<sub>1</sub> = j<sub>0</sub> + 1<P>
+ * a = frac(u - 0.5)<P>
+ * b = frac(v - 0.5)<P>
+ * T' = (1-a)*(1-b)*T<sub>i<sub>0</sub>j<sub>0</sub></sub> +
+ * a*(1-b)*T<sub>i<sub>1</sub>j<sub>0</sub></sub> +
+ * (1-a)*b*T<sub>i<sub>0</sub>j<sub>1</sub></sub> +
+ * a*b*T<sub>i<sub>1</sub>j<sub>1</sub></sub><P>
+ * </UL>
+ * <LI>LINEAR_SHARPEN - sharpens the resulting image by extrapolating
+ * from the base level plus one image to the base level image of this
+ * texture object.</LI><P>
+ * <LI>LINEAR_SHARPEN_RGB - performs linear sharpen filter for the rgb
+ * components only. The alpha component is computed using BASE_LEVEL_LINEAR
+ * filter.</LI><P>
+ * <LI>LINEAR_SHARPEN_ALPHA - performs linear sharpen filter for the alpha
+ * component only. The rgb components are computed using BASE_LEVEL_LINEAR
+ * filter.</LI><P>
+ * <LI>FILTER4 - applies an application-supplied weight function
+ * on the nearest 4x4 texels in the base level texture image. The
+ * texture value T' is computed as follows:</LI><P>
+ * <UL>
+ * <table cellspacing=10>
+ * <td>i<sub>1</sub> = trunc(u - 0.5)</td>
+ * <td>i<sub>2</sub> = i<sub>1</sub> + 1</td>
+ * <td>i<sub>3</sub> = i<sub>2</sub> + 1</td>
+ * <td>i<sub>0</sub> = i<sub>1</sub> - 1</td>
+ * <tr>
+ * <td>j<sub>1</sub> = trunc(v - 0.5)</td>
+ * <td>j<sub>3</sub> = j<sub>2</sub> + 1</td>
+ * <td>j<sub>2</sub> = j<sub>1</sub> + 1</td>
+ * <td>j<sub>0</sub> = j<sub>1</sub> - 1</td>
+ * <tr>
+ * <td>a = frac(u - 0.5)</td>
+ * <tr>
+ * <td>b = frac(v - 0.5)</td>
+ * </table>
+ * f(x) : filter4 function where 0<=x<=2<P>
+ * T' = f(1+a) * f(1+b) * T<sub>i<sub>0</sub>j<sub>0</sub></sub> +
+ * f(a) * f(1+b) * T<sub>i<sub>1</sub>j<sub>0</sub></sub> +
+ * f(1-a) * f(1+b) * T<sub>i<sub>2</sub>j<sub>0</sub></sub> +
+ * f(2-a) * f(1+b) * T<sub>i<sub>3</sub>j<sub>0</sub></sub> + <br>
+ * f(1+a) * f(b) * T<sub>i<sub>0</sub>j<sub>1</sub></sub> +
+ * f(a) * f(b) * T<sub>i<sub>1</sub>j<sub>1</sub></sub> +
+ * f(1-a) * f(b) * T<sub>i<sub>2</sub>j<sub>1</sub></sub> +
+ * f(2-a) * f(b) * T<sub>i<sub>3</sub>j<sub>1</sub></sub> + <br>
+ * f(1+a) * f(1-b) * T<sub>i<sub>0</sub>j<sub>2</sub></sub> +
+ * f(a) * f(1-b) * T<sub>i<sub>1</sub>j<sub>2</sub></sub> +
+ * f(1-a) * f(1-b) * T<sub>i<sub>2</sub>j<sub>2</sub></sub> +
+ * f(2-a) * f(1-b) * T<sub>i<sub>3</sub>j<sub>2</sub></sub> + <br>
+ * f(1+a) * f(2-b) * T<sub>i<sub>0</sub>j<sub>3</sub></sub> +
+ * f(a) * f(2-b) * T<sub>i<sub>1</sub>j<sub>3</sub></sub> +
+ * f(1-a) * f(2-b) * T<sub>i<sub>2</sub>j<sub>3</sub></sub> +
+ * f(2-a) * f(2-b) * T<sub>i<sub>3</sub>j<sub>3</sub></sub> <P>
+ * </UL>
+ * </UL>
+ * <LI>Minification filter - the minification filter function. Used
+ * when the pixel being rendered maps to an area greater than one
+ * texel. The minifaction filter functions are as follows:</LI><P>
+ * <UL>
+ * <LI>FASTEST - uses the fastest available method for processing
+ * geometry.</LI><P>
+ * <LI>NICEST - uses the nicest available method for processing
+ * geometry.</LI><P>
+ * <LI>BASE_LEVEL_POINT - selects the nearest level in the base level
+ * texture map.</LI><P>
+ *<LI>BASE_LEVEL_LINEAR - performs a bilinear interpolation on the four
+ * nearest texels in the base level texture map.</LI><P>
+ * <LI>MULTI_LEVEL_POINT - selects the nearest texel in the nearest
+ * mipmap.</LI><P>
+ * <LI>MULTI_LEVEL_LINEAR - performs trilinear interpolation of texels
+ * between four texels each from the two nearest mipmap levels.</LI><P>
+ * <LI>FILTER4 - applies an application-supplied weight function
+ * on the nearest 4x4 texels in the base level texture image.</LI><P>
+ * </UL>
+ * <LI>MIPmap mode - the mode used for texture mapping for this
+ * object. The mode is one of the following:</LI><P>
+ * <UL>
+ * <LI>BASE_LEVEL - indicates that this Texture object only has a
+ * base-level image. If multiple levels are needed, they will be
+ * implicitly computed.</LI><P>
+ * <LI>MULTI_LEVEL_MIPMAP - indicates that this Texture object has
+ * multiple images. If MIPmap mode is set
+ * to MULTI_LEVEL_MIPMAP, images for Base Level through Max Level
+ * must be set.</LI><P>
+ * </UL>
+ * <LI>Format - the data format. The format is one of the
+ * following:</LI><P>
+ * <UL>
+ * <LI>INTENSITY - the texture image contains only texture
+ * values.</LI><P>
+ * <LI>LUMINANCE - the texture image contains only
+ * luminance values.</LI><P>
+ * <LI>ALPHA - the texture image contains only alpha
+ * values.</LI><P>
+ * <LI>LUMINANCE_ALPHA - the texture image contains
+ * both luminance and alpha values.</LI><P>
+ * <LI>RGB - the texture image contains red, green,
+ * and blue values.</LI><P>
+ * <LI>RGBA - the texture image contains red, green, blue, and alpha
+ * values.</LI><P></UL>
+ * <LI>Base Level - specifies the mipmap level to be used when filter
+ * specifies BASE_LEVEL_POINT or BASE_LEVEL_LINEAR.</LI><P>
+ * <LI>Maximum Level - specifies the maximum level of image that needs to be
+ * defined for this texture to be valid. Note, for this texture to be valid,
+ * images for Base Level through Maximum Level have to be defined.</LI><P>
+ * <LI>Minimum LOD - specifies the minimum of the LOD range. LOD smaller
+ * than this value will be clamped to this value.</LI><P>
+ * <LI>Maximum LOD - specifies the maximum of the LOD range. LOD larger
+ * than this value will be clamped to this value.</LI><P>
+ * <LI>LOD offset - specifies the offset to be used in the LOD calculation
+ * to compensate for under or over sampled texture images.</LI></P>
+ * <LI>Anisotropic Mode - defines how anisotropic filter is applied for
+ * this texture object. The anisotropic modes are as follows:</LI><P>
+ * <UL>
+ * <LI>ANISOTROPIC_NONE - no anisotropic filtering.</LI><P>
+ * <LI>ANISOTROPIC_SINGLE_VALUE - applies the degree of anisotropic filter
+ * in both the minification and magnification filters.</LI><P>
+ * </UL>
+ * <LI>Anisotropic Filter Degree - controls the degree of anisotropy. This
+ * property applies to both minification and magnification filtering.
+ * If it is equal to 1.0, then an isotropic filtering as specified in the
+ * minification or magnification filter will be used. If it is greater
+ * than 1.0, and the anisotropic mode is equal to ANISOTROPIC_SINGLE_VALUE,
+ * then
+ * the degree of anisotropy will also be applied in the filtering.</LI><P>
+ * <LI>Sharpen Texture Function - specifies the function of level-of-detail
+ * used in combining the texture value computed from the base level image
+ * and the texture value computed from the base level plus one image. The
+ * final texture value is computed as follows: </LI><P>
+ * <UL>
+ * T' = ((1 + SharpenFunc(LOD)) * T<sub>BaseLevel</sub>) - (SharpenFunc(LOD) * T<sub>BaseLevel+1</sub>) <P>
+ * </UL>
+ * <LI>Filter4 Function - specifies the function to be applied to the
+ * nearest 4x4 texels. This property includes samples of the filter
+ * function f(x), 0<=x<=2. The number of function values supplied
+ * has to be equal to 2<sup>m</sup> + 1 for some integer value of m
+ * greater than or equal to 4. </LI><P>
+ * </UL>
+ *
+ * @see Canvas3D#queryProperties
+ */
+public abstract class Texture extends NodeComponent {
+ /**
+ * Specifies that this Texture object allows reading its
+ * enable flag.
+ */
+ public static final int
+ ALLOW_ENABLE_READ = CapabilityBits.TEXTURE_ALLOW_ENABLE_READ;
+
+ /**
+ * Specifies that this Texture object allows writing its
+ * enable flag.
+ */
+ public static final int
+ ALLOW_ENABLE_WRITE = CapabilityBits.TEXTURE_ALLOW_ENABLE_WRITE;
+
+ /**
+ * Specifies that this Texture object allows reading its
+ * boundary mode information.
+ */
+ public static final int
+ ALLOW_BOUNDARY_MODE_READ = CapabilityBits.TEXTURE_ALLOW_BOUNDARY_MODE_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its
+ * filter information.
+ */
+ public static final int
+ ALLOW_FILTER_READ = CapabilityBits.TEXTURE_ALLOW_FILTER_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its
+ * image component information.
+ */
+ public static final int
+ ALLOW_IMAGE_READ = CapabilityBits.TEXTURE_ALLOW_IMAGE_READ;
+
+ /**
+ * Specifies that this Texture object allows writing its
+ * image component information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int
+ ALLOW_IMAGE_WRITE = CapabilityBits.TEXTURE_ALLOW_IMAGE_WRITE;
+
+ /**
+ * Specifies that this Texture object allows reading its
+ * format information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int
+ ALLOW_FORMAT_READ = CapabilityBits.TEXTURE_ALLOW_FORMAT_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its
+ * size information (e.g., width, height, number of mipmap levels,
+ * boundary width).
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int
+ ALLOW_SIZE_READ = CapabilityBits.TEXTURE_ALLOW_SIZE_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its
+ * mipmap mode information.
+ */
+ public static final int
+ ALLOW_MIPMAP_MODE_READ = CapabilityBits.TEXTURE_ALLOW_MIPMAP_MODE_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its
+ * boundary color information.
+ */
+ public static final int
+ ALLOW_BOUNDARY_COLOR_READ = CapabilityBits.TEXTURE_ALLOW_BOUNDARY_COLOR_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its LOD range
+ * information (e.g., base level, maximum level, minimum lod,
+ * maximum lod, lod offset)
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_LOD_RANGE_READ = CapabilityBits.TEXTURE_ALLOW_LOD_RANGE_READ;
+
+ /**
+ * Specifies that this Texture object allows writing its LOD range
+ * information (e.g., base level, maximum level, minimum lod,
+ * maximum lod, lod offset)
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_LOD_RANGE_WRITE = CapabilityBits.TEXTURE_ALLOW_LOD_RANGE_WRITE;
+
+
+ /**
+ * Specifies that this Texture object allows reading its anistropic
+ * filter information (e.g., anisotropic mode, anisotropic filter)
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_ANISOTROPIC_FILTER_READ = CapabilityBits.TEXTURE_ALLOW_ANISOTROPIC_FILTER_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its sharpen
+ * texture function information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_SHARPEN_TEXTURE_READ = CapabilityBits.TEXTURE_ALLOW_SHARPEN_TEXTURE_READ;
+
+ /**
+ * Specifies that this Texture object allows reading its filter4
+ * function information.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_FILTER4_READ = CapabilityBits.TEXTURE_ALLOW_FILTER4_READ;
+
+
+ /**
+ * Uses the fastest available method for processing geometry.
+ * This value can be used as a parameter to setMinFilter and
+ * setMagFilter.
+ * @see #setMinFilter
+ * @see #setMagFilter
+ */
+ public static final int FASTEST = 0;
+ /**
+ * Uses the nicest available method for processing geometry.
+ * This value can be used as a parameter to setMinFilter and
+ * setMagFilter.
+ * @see #setMinFilter
+ * @see #setMagFilter
+ */
+ public static final int NICEST = 1;
+
+ /**
+ * Select the nearest texel in level 0 texture map.
+ * Maps to NEAREST.
+ * @see #setMinFilter
+ * @see #setMagFilter
+ */
+ public static final int BASE_LEVEL_POINT = 2;
+
+ /**
+ * Performs bilinear interpolation on the four nearest texels
+ * in level 0 texture map.
+ * Maps to LINEAR.
+ * @see #setMinFilter
+ * @see #setMagFilter
+ */
+ public static final int BASE_LEVEL_LINEAR = 3;
+
+ /**
+ * Selects the nearest texel in the nearest mipmap.
+ * Maps to NEAREST_MIPMAP_NEAREST.
+ * @see #setMinFilter
+ */
+ public static final int MULTI_LEVEL_POINT = 4;
+
+ /**
+ * Performs tri-linear interpolation of texels between four
+ * texels each from two nearest mipmap levels.
+ * Maps to LINEAR_MIPMAP_LINEAR, but an implementation can
+ * fall back to LINEAR_MIPMAP_NEAREST or NEAREST_MIPMAP_LINEAR.
+ * @see #setMinFilter
+ */
+ public static final int MULTI_LEVEL_LINEAR = 5;
+
+ // NOTE: values 6, 7, and 8 are reserved for the LINEAR_DETAIL*
+ // filter modes in Texture2D
+
+ /**
+ * Sharpens the resulting image by extrapolating
+ * from the base level plus one image to the base level image of this
+ * texture object.
+ *
+ * @since Java 3D 1.3
+ * @see #setMagFilter
+ */
+ public static final int LINEAR_SHARPEN = 9;
+
+ /**
+ * Performs linear sharpen filter for the rgb
+ * components only. The alpha component is computed using
+ * BASE_LEVEL_LINEAR filter.
+ *
+ * @since Java 3D 1.3
+ * @see #setMagFilter
+ */
+ public static final int LINEAR_SHARPEN_RGB = 10;
+
+ /**
+ * Performs linear sharpen filter for the alpha
+ * component only. The rgb components are computed using
+ * BASE_LEVEL_LINEAR filter.
+ *
+ * @since Java 3D 1.3
+ * @see #setMagFilter
+ */
+ public static final int LINEAR_SHARPEN_ALPHA = 11;
+
+ /**
+ * Applies an application-supplied weight function
+ * on the nearest 4x4 texels in the base level texture image.
+ *
+ * @since Java 3D 1.3
+ * @see #setMinFilter
+ * @see #setMagFilter
+ */
+ public static final int FILTER4 = 12;
+
+ // Texture boundary mode parameter values
+ /**
+ * Clamps texture coordinates to be in the range [0, 1].
+ * Texture boundary texels or the constant boundary color if boundary
+ * width is 0 will be used for U,V values that fall
+ * outside this range.
+ */
+ public static final int CLAMP = 2;
+ /**
+ * Repeats the texture by wrapping texture coordinates that are outside
+ * the range [0,1]. Only the fractional portion of the texture
+ * coordinates is used; the integer portion is discarded.
+ */
+ public static final int WRAP = 3;
+ /**
+ * Clamps texture coordinates such that filtering
+ * will not sample a texture boundary texel. Texels at the edge of the
+ * texture will be used instead.
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int CLAMP_TO_EDGE = 4;
+ /**
+ * Clamps texture coordinates such that filtering
+ * will sample only texture boundary texels. If the texture does not
+ * have a boundary, that is the boundary width is equal to 0, then the
+ * constant boundary color will be used.</LI></P>
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int CLAMP_TO_BOUNDARY = 5;
+
+
+ /**
+ * Indicates that Texture object only has one level. If multiple
+ * levels are needed, they will be implicitly computed.
+ */
+ public static final int BASE_LEVEL = 1;
+
+ /**
+ * Indicates that this Texture object has multiple images, one for
+ * each mipmap level. In this mode, there are
+ * <code>log<sub><font size=-2>2</font></sub>(max(width,height))+1</code>
+ * separate images.
+ */
+ public static final int MULTI_LEVEL_MIPMAP = 2;
+
+ // Texture format parameter values
+
+ /**
+ * Specifies Texture contains only Intensity values.
+ */
+ public static final int INTENSITY = 1;
+
+ /**
+ * Specifies Texture contains only luminance values.
+ */
+ public static final int LUMINANCE = 2;
+
+ /**
+ * Specifies Texture contains only Alpha values.
+ */
+ public static final int ALPHA = 3;
+
+ /**
+ * Specifies Texture contains Luminance and Alpha values.
+ */
+ public static final int LUMINANCE_ALPHA = 4;
+
+ /**
+ * Specifies Texture contains Red, Green and Blue color values.
+ */
+ public static final int RGB = 5;
+
+ /**
+ * Specifies Texture contains Red, Green, Blue color values
+ * and Alpha value.
+ */
+ public static final int RGBA = 6;
+
+ /**
+ * No anisotropic filter.
+ *
+ * @since Java 3D 1.3
+ * @see #setAnisotropicFilterMode
+ */
+ public static final int ANISOTROPIC_NONE = 0;
+
+ /**
+ * Uses the degree of anisotropy in both the minification and
+ * magnification filters.
+ *
+ * @since Java 3D 1.3
+ * @see #setAnisotropicFilterMode
+ */
+ public static final int ANISOTROPIC_SINGLE_VALUE = 1;
+
+ /**
+ * Constructs a Texture object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * enable flag : true<br>
+ * width : 0<br>
+ * height : 0<br>
+ * mipmap mode : BASE_LEVEL<br>
+ * format : RGB<br>
+ * boundary mode S : WRAP<br>
+ * boundary mode T : WRAP<br>
+ * min filter : BASE_LEVEL_POINT<br>
+ * mag filter : BASE_LEVEL_POINT<br>
+ * boundary color : black (0,0,0,0)<br>
+ * boundary width : 0<br>
+ * array of images : null<br>
+ * baseLevel : 0<br>
+ * maximumLevel : <code>log<sub><font size=-2>2</font></sub>(max(width,height))</code><br>
+ * minimumLOD : -1000.0<br>
+ * maximumLOD : 1000.0<br>
+ * lod offset : (0, 0, 0)<br>
+ * anisotropic mode : ANISOTROPIC_NONE<br>
+ * anisotropic filter : 1.0<br>
+ * sharpen texture func: null<br>
+ * filter4 func: null<br>
+ * </ul>
+ * <p>
+ * Note that the default constructor creates a texture object with
+ * a width and height of 0 and is, therefore, not useful.
+ */
+ public Texture() {
+ // Just use default values
+ }
+
+ /**
+ * Constructs an empty Texture object with specified mipMapMode,
+ * format, width and height. Defaults are used for all other
+ * parameters. If <code>mipMapMode</code> is set to
+ * <code>BASE_LEVEL</code>, then the image at level 0 must be set
+ * by the application (using either the <code>setImage</code> or
+ * <code>setImages</code> method). If <code>mipMapMode</code> is
+ * set to <code>MULTI_LEVEL_MIPMAP</code>, then images for levels
+ * Base Level through Maximum Level must be set.
+ *
+ * @param mipMapMode type of mipmap for this Texture: one of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA
+ * @param width width of image at level 0. Must be power of 2.
+ * @param height height of image at level 0. Must be power of 2.
+ * @exception IllegalArgumentException if width or height are not a
+ * power of 2, or if an invalid format or mipMapMode is specified.
+ */
+ public Texture(int mipMapMode,
+ int format,
+ int width,
+ int height) {
+
+ if ((mipMapMode != BASE_LEVEL) && (mipMapMode != MULTI_LEVEL_MIPMAP))
+ throw new IllegalArgumentException(J3dI18N.getString("Texture0"));
+
+ if ((format != INTENSITY) && (format != LUMINANCE) &&
+ (format != ALPHA) && (format != LUMINANCE_ALPHA) &&
+ (format != RGB) && (format != RGBA)) {
+ throw new IllegalArgumentException(J3dI18N.getString("Texture1"));
+ }
+
+ int widPower = getPowerOf2(width);
+ if (widPower == -1)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture2"));
+
+ int heiPower = getPowerOf2(height);
+ if (heiPower == -1)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture3"));
+
+ ((TextureRetained)this.retained).initialize(format, width, widPower,
+ height, heiPower, mipMapMode, 0);
+ }
+
+ /**
+ * Constructs an empty Texture object with specified mipMapMode,
+ * format, width, height, and boundaryWidth.
+ * Defaults are used for all other
+ * parameters. If <code>mipMapMode</code> is set to
+ * <code>BASE_LEVEL</code>, then the image at level 0 must be set
+ * by the application (using either the <code>setImage</code> or
+ * <code>setImages</code> method). If <code>mipMapMode</code> is
+ * set to <code>MULTI_LEVEL_MIPMAP</code>, then images for levels
+ * Base Level through Maximum Level must be set.
+ *
+ * @param mipMapMode type of mipmap for this Texture: one of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA
+ * @param width width of image at level 0. Must be power of 2. This
+ * does not include the width of the boundary.
+ * @param height height of image at level 0. Must be power of 2. This
+ * does not include the width of the boundary.
+ * @param boundaryWidth width of the boundary.
+ * @exception IllegalArgumentException if width or height are not a
+ * power of 2, if an invalid format or mipMapMode is specified, or
+ * if the boundaryWidth < 0
+ *
+ * @since Java 3D 1.3
+ */
+ public Texture(int mipMapMode,
+ int format,
+ int width,
+ int height,
+ int boundaryWidth) {
+
+ if ((mipMapMode != BASE_LEVEL) && (mipMapMode != MULTI_LEVEL_MIPMAP))
+ throw new IllegalArgumentException(J3dI18N.getString("Texture0"));
+
+ if ((format != INTENSITY) && (format != LUMINANCE) &&
+ (format != ALPHA) && (format != LUMINANCE_ALPHA) &&
+ (format != RGB) && (format != RGBA)) {
+ throw new IllegalArgumentException(J3dI18N.getString("Texture1"));
+ }
+
+ int widPower = getPowerOf2(width);
+ if (widPower == -1)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture2"));
+
+ int heiPower = getPowerOf2(height);
+ if (heiPower == -1)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture3"));
+
+ if (boundaryWidth < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture30"));
+
+ ((TextureRetained)this.retained).initialize(format, width, widPower,
+ height, heiPower, mipMapMode, boundaryWidth);
+ }
+
+ /**
+ * Sets the boundary mode for the S coordinate in this texture object.
+ * @param boundaryModeS the boundary mode for the S coordinate.
+ * One of: CLAMP, WRAP, CLAMP_TO_EDGE, or CLAMP_TO_BOUNDARY.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if <code>boundaryModeS</code>
+ * is a value other than <code>CLAMP</code>, <code>WRAP</code>,
+ * <code>CLAMP_TO_EDGE</code>, or <code>CLAMP_TO_BOUNDARY</code>.
+ */
+ public void setBoundaryModeS(int boundaryModeS) {
+ checkForLiveOrCompiled();
+ switch (boundaryModeS) {
+ case Texture.CLAMP:
+ case Texture.WRAP:
+ case Texture.CLAMP_TO_EDGE:
+ case Texture.CLAMP_TO_BOUNDARY:
+ break;
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("Texture31"));
+ }
+ ((TextureRetained)this.retained).initBoundaryModeS(boundaryModeS);
+ }
+
+ /**
+ * Retrieves the boundary mode for the S coordinate.
+ * @return the current boundary mode for the S coordinate.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getBoundaryModeS() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_BOUNDARY_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture4"));
+ return ((TextureRetained)this.retained).getBoundaryModeS();
+ }
+
+ /**
+ * Sets the boundary mode for the T coordinate in this texture object.
+ * @param boundaryModeT the boundary mode for the T coordinate.
+ * One of: CLAMP, WRAP, CLAMP_TO_EDGE, or CLAMP_TO_BOUNDARY.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if <code>boundaryModeT</code>
+ * is a value other than <code>CLAMP</code>, <code>WRAP</code>,
+ * <code>CLAMP_TO_EDGE</code>, or <code>CLAMP_TO_BOUNDARY</code>.
+ */
+ public void setBoundaryModeT(int boundaryModeT) {
+ checkForLiveOrCompiled();
+ switch (boundaryModeT) {
+ case Texture.CLAMP:
+ case Texture.WRAP:
+ case Texture.CLAMP_TO_EDGE:
+ case Texture.CLAMP_TO_BOUNDARY:
+ break;
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("Texture31"));
+ }
+ ((TextureRetained)this.retained).initBoundaryModeT(boundaryModeT);
+ }
+
+ /**
+ * Retrieves the boundary mode for the T coordinate.
+ * @return the current boundary mode for the T coordinate.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getBoundaryModeT() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_BOUNDARY_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture4"));
+ return ((TextureRetained)this.retained).getBoundaryModeT();
+ }
+
+ /**
+ * Sets the minification filter function. This
+ * function is used when the pixel being rendered maps to an area
+ * greater than one texel.
+ * @param minFilter the minification filter. One of:
+ * FASTEST, NICEST, BASE_LEVEL_POINT, BASE_LEVEL_LINEAR,
+ * MULTI_LEVEL_POINT, MULTI_LEVEL_LINEAR, or FILTER4
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if <code>minFilter</code>
+ * is a value other than <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>BASE_LEVEL_POINT</code>, <code>BASE_LEVEL_LINEAR</code>,
+ * <code>MULTI_LEVEL_POINT</code>, <code>MULTI_LEVEL_LINEAR</code>, or
+ * <code>FILTER4</code>.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public void setMinFilter(int minFilter) {
+ checkForLiveOrCompiled();
+
+ switch (minFilter) {
+ case FASTEST:
+ case NICEST:
+ case BASE_LEVEL_POINT:
+ case BASE_LEVEL_LINEAR:
+ case MULTI_LEVEL_POINT:
+ case MULTI_LEVEL_LINEAR:
+ case FILTER4:
+ break;
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("Texture28"));
+ }
+
+ ((TextureRetained)this.retained).initMinFilter(minFilter);
+ }
+
+ /**
+ * Retrieves the minification filter.
+ * @return the current minification filter function.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getMinFilter() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_FILTER_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture6"));
+ return ((TextureRetained)this.retained).getMinFilter();
+ }
+
+ /**
+ * Sets the magnification filter function. This
+ * function is used when the pixel being rendered maps to an area
+ * less than or equal to one texel.
+ * @param magFilter the magnification filter, one of:
+ * FASTEST, NICEST, BASE_LEVEL_POINT, BASE_LEVEL_LINEAR,
+ * LINEAR_SHARPEN, LINEAR_SHARPEN_RGB, LINEAR_SHARPEN_ALPHA, or FILTER4.
+ *
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if <code>magFilter</code>
+ * is a value other than <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>BASE_LEVEL_POINT</code>, <code>BASE_LEVEL_LINEAR</code>,
+ * <code>LINEAR_SHARPEN</code>, <code>LINEAR_SHARPEN_RGB</code>,
+ * <code>LINEAR_SHARPEN_ALPHA</code>, or
+ * <code>FILTER4</code>.
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public void setMagFilter(int magFilter) {
+ checkForLiveOrCompiled();
+
+ switch (magFilter) {
+ case FASTEST:
+ case NICEST:
+ case BASE_LEVEL_POINT:
+ case BASE_LEVEL_LINEAR:
+ case LINEAR_SHARPEN:
+ case LINEAR_SHARPEN_RGB:
+ case LINEAR_SHARPEN_ALPHA:
+ case FILTER4:
+ break;
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("Texture29"));
+ }
+
+ ((TextureRetained)this.retained).initMagFilter(magFilter);
+ }
+
+ /**
+ * Retrieves the magnification filter.
+ * @return the current magnification filter function.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getMagFilter() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_FILTER_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture6"));
+ return ((TextureRetained)this.retained).getMagFilter();
+ }
+
+ /**
+ * Sets the image for a specified mipmap level.
+ * @param level mipmap level to set: 0 is the base level
+ * @param image ImageComponent object containing the texture image
+ * for the specified mipmap level
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalArgumentException if an ImageComponent3D is
+ * used in a Texture2D object; if an ImageComponent2D is used in a
+ * Texture3D object; or if this object is part of a live
+ * scene graph and the image being set at this level is not the
+ * same size (width, height, depth) as the old image at this
+ * level.
+ */
+ public void setImage(int level, ImageComponent image) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture15"));
+ }
+
+ if (isLive())
+ ((TextureRetained)this.retained).setImage(level, image);
+ else
+ ((TextureRetained)this.retained).initImage(level, image);
+ }
+
+ /**
+ * Retrieves the image for a specified mipmap level.
+ * @param level mipmap level to get: 0 is the base level
+ * @return the ImageComponent object containing the texture image at
+ * the specified mipmap level.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public ImageComponent getImage(int level) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture9"));
+ }
+
+ return ((TextureRetained)this.retained).getImage(level);
+ }
+
+ /**
+ * Sets the array of images for all mipmap levels.
+ * @param images array of ImageComponent objects
+ * containing the texture images for all mipmap levels
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @exception IllegalArgumentException if an ImageComponent3D is
+ * used in a Texture2D object; if an ImageComponent2D is used in a
+ * Texture3D object; if <code>images.length</code> is not equal to
+ * the total number of mipmap levels; or if this object is part of
+ * a live scene graph and the size of each dimension (width,
+ * height, depth) of the image at a given level in the
+ * <code>images</code> array is not half the dimension of the
+ * previous level.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setImages(ImageComponent[] images) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture15"));
+ }
+
+ if (images == null)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture20"));
+
+ if (isLive())
+ ((TextureRetained)this.retained).setImages(images);
+ else
+ ((TextureRetained)this.retained).initImages(images);
+ }
+
+ /**
+ * Retrieves the array of images for all mipmap levels.
+ * @return the array of ImageComponent objects for this Texture.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public ImageComponent[] getImages() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture9"));
+ }
+ return ((TextureRetained)this.retained).getImages();
+ }
+
+ /**
+ * Retrieves the format of this Texture object.
+ * @return the format of this Texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getFormat() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_FORMAT_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture19"));
+ }
+ return ((TextureRetained)this.retained).getFormat();
+ }
+
+ /**
+ * Retrieves the width of this Texture object.
+ * @return the width of this Texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getWidth() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture16"));
+ }
+ return ((TextureRetained)this.retained).getWidth();
+ }
+
+ /**
+ * Retrieves the height of this Texture object.
+ * @return the height of this Texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getHeight() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture17"));
+ }
+ return ((TextureRetained)this.retained).getHeight();
+ }
+
+ /**
+ * Retrieves the width of the boundary of this Texture object.
+ * @return the width of the boundary of this Texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getBoundaryWidth() {
+
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture17"));
+ }
+
+ return ((TextureRetained)this.retained).getBoundaryWidth();
+ }
+
+ /**
+ * Retrieves the number of mipmap levels needed for this Texture object.
+ * @return (maximum Level - base Level + 1)
+ * if <code>mipMapMode</code> is
+ * <code>MULTI_LEVEL_MIPMAP</code>; otherwise it returns 1.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int numMipMapLevels() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture18"));
+ }
+ return ((TextureRetained)this.retained).numMipMapLevels();
+ }
+
+ /**
+ * Sets mipmap mode for texture mapping for this texture object.
+ * @param mipMapMode the new mipmap mode for this object. One of:
+ * BASE_LEVEL or MULTI_LEVEL_MIPMAP.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if <code>mipMapMode</code>
+ * is a value other than <code>BASE_LEVEL</code> or
+ * <code>MULTI_LEVEL_MIPMAP</code>.
+ */
+ public void setMipMapMode(int mipMapMode) {
+ checkForLiveOrCompiled();
+ ((TextureRetained)this.retained).initMipMapMode(mipMapMode);
+ }
+
+ /**
+ * Retrieves current mipmap mode.
+ * @return current mipmap mode of this texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getMipMapMode() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_MIPMAP_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture10"));
+ return ((TextureRetained)this.retained).getMipMapMode();
+ }
+
+ /**
+ * Enables or disables texture mapping for this
+ * appearance component object.
+ * @param state true or false to enable or disable texture mapping
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setEnable(boolean state) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_ENABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture11"));
+ }
+ if (isLive())
+ ((TextureRetained)this.retained).setEnable(state);
+ else
+ ((TextureRetained)this.retained).initEnable(state);
+
+ }
+
+ /**
+ * Retrieves the state of the texture enable flag.
+ * @return true if texture mapping is enabled,
+ * false if texture mapping is disabled
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public boolean getEnable() {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_ENABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture12"));
+ }
+ return ((TextureRetained)this.retained).getEnable();
+ }
+
+ // Internal j3d usage method
+ // Returns n if num is 2**n
+ // Returns -1 if num is 0 or negative or if
+ // num is NOT power of 2.
+ // NOTE: ********** Assumes 32 bit integer******************
+ static int getPowerOf2(int num) {
+
+ int i, tmp;
+ // Can only handle positive numbers, return error.
+ if (num < 1) return -1;
+
+ for (i=0, tmp = num; i < 32;i++) {
+ // Check if leftmost bit is 1
+ if ((tmp & 0x80000000) != 0) {
+ //Check if any other bit is 1
+ if ((tmp & 0x7fffffff) == 0)
+ return 31-i;//valid power of 2 integer
+ else
+ return -1;//invalid non-power-of-2 integer
+ }
+ tmp <<= 1;
+ }
+ //Can't reach here because we have already checked for 0
+ return -1;
+ }
+
+ /**
+ * Sets the texture boundary color for this texture object. The
+ * texture boundary color is used when boundaryModeS or boundaryModeT
+ * is set to CLAMP or CLAMP_TO_BOUNDARY and if texture boundary is not
+ * specified.
+ * @param boundaryColor the new texture boundary color.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void setBoundaryColor(Color4f boundaryColor) {
+ checkForLiveOrCompiled();
+ ((TextureRetained)this.retained).initBoundaryColor(boundaryColor);
+ }
+
+ /**
+ * Sets the texture boundary color for this texture object. The
+ * texture boundary color is used when boundaryModeS or boundaryModeT
+ * is set to CLAMP or CLAMP_TO_BOUNDARY and if texture boundary is not
+ * specified.
+ * @param r the red component of the color.
+ * @param g the green component of the color.
+ * @param b the blue component of the color.
+ * @param a the alpha component of the color.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void setBoundaryColor(float r, float g, float b, float a) {
+ checkForLiveOrCompiled();
+ ((TextureRetained)this.retained).initBoundaryColor(r, g, b, a);
+ }
+
+ /**
+ * Retrieves the texture boundary color for this texture object.
+ * @param boundaryColor the vector that will receive the
+ * current texture boundary color.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getBoundaryColor(Color4f boundaryColor) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_BOUNDARY_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture13"));
+ }
+ ((TextureRetained)this.retained).getBoundaryColor(boundaryColor);
+ }
+
+ /**
+ * Specifies the base level for this texture object.
+ * @param baseLevel index of the lowest defined mipmap level.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if specified baseLevel < 0, or
+ * if baseLevel > maximumLevel
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setBaseLevel(int baseLevel) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture32"));
+ }
+ }
+
+ if (isLive()) {
+ ((TextureRetained)this.retained).setBaseLevel(baseLevel);
+ } else {
+ ((TextureRetained)this.retained).initBaseLevel(baseLevel);
+ }
+ }
+
+ /**
+ * Retrieves the base level for this texture object.
+ * @return base level for this texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getBaseLevel() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture34"));
+ }
+ }
+ return ((TextureRetained)this.retained).getBaseLevel();
+ }
+
+ /**
+ * Specifies the maximum level for this texture object.
+ * @param maximumLevel index of the highest defined mipmap level.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if specified
+ * maximumLevel < baseLevel, or
+ * if maximumLevel > <code>log<sub><font size=-2>2</font></sub>(max(width,height))</code>
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setMaximumLevel(int maximumLevel) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture33"));
+ }
+ }
+
+ if (isLive()) {
+ ((TextureRetained)this.retained).setMaximumLevel(maximumLevel);
+ } else {
+ ((TextureRetained)this.retained).initMaximumLevel(maximumLevel);
+ }
+ }
+
+ /**
+ * Retrieves the maximum level for this texture object.
+ * @return maximum level for this texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getMaximumLevel() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture35"));
+ }
+ }
+ return ((TextureRetained)this.retained).getMaximumLevel();
+ }
+
+ /**
+ * Specifies the minimum level-of-detail for this texture object.
+ * @param minimumLod the minimum level-of-detail.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if specified lod > maximum lod
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setMinimumLOD(float minimumLod) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture38"));
+ }
+ }
+
+ if (isLive()) {
+ ((TextureRetained)this.retained).setMinimumLOD(minimumLod);
+ } else {
+ ((TextureRetained)this.retained).initMinimumLOD(minimumLod);
+ }
+ }
+
+ /**
+ * Retrieves the minimum level-of-detail for this texture object.
+ * @return the minimum level-of-detail
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public float getMinimumLOD() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture40"));
+ }
+ }
+ return ((TextureRetained)this.retained).getMinimumLOD();
+ }
+
+ /**
+ * Specifies the maximum level-of-detail for this texture object.
+ * @param maximumLod the maximum level-of-detail.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if specified lod < minimum lod
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setMaximumLOD(float maximumLod) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture39"));
+ }
+ }
+
+ if (isLive()) {
+ ((TextureRetained)this.retained).setMaximumLOD(maximumLod);
+ } else {
+ ((TextureRetained)this.retained).initMaximumLOD(maximumLod);
+ }
+ }
+
+ /**
+ * Retrieves the maximum level-of-detail for this texture object.
+ * @return the maximum level-of-detail
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public float getMaximumLOD() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture41"));
+ }
+ }
+ return ((TextureRetained)this.retained).getMaximumLOD();
+ }
+
+ /**
+ * Specifies the LOD offset for this texture object.
+ * @param s the s component of the LOD offset
+ * @param t the t component of the LOD offset
+ * @param r the r component of the LOD offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setLodOffset(float s, float t, float r) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture44"));
+ }
+ }
+
+ if (isLive()) {
+ ((TextureRetained)this.retained).setLodOffset(s, t, r);
+ } else {
+ ((TextureRetained)this.retained).initLodOffset(s, t, r);
+ }
+ }
+
+ /**
+ * Specifies the LOD offset for this texture object.
+ * @param offset the LOD offset
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setLodOffset(Tuple3f offset) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture44"));
+ }
+ }
+
+ if (isLive()) {
+ ((TextureRetained)this.retained).setLodOffset(
+ offset.x, offset.y, offset.z);
+ } else {
+ ((TextureRetained)this.retained).initLodOffset(
+ offset.x, offset.y, offset.z);
+ }
+ }
+
+ /**
+ * Retrieves the LOD offset for this texture object.
+ * @param offset the vector that will receive the
+ * current LOD offset.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getLodOffset(Tuple3f offset) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_LOD_RANGE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture45"));
+ }
+ }
+ ((TextureRetained)this.retained).getLodOffset(offset);
+ }
+
+ /**
+ * Specifies the anisotropic filter mode for this texture object.
+ * @param mode the anisotropic filter mode. One of
+ * ANISOTROPIC_NONE or ANISOTROPIC_SINGLE_VALUE.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if
+ * <code>mode</code> is a value other than
+ * <code>ANISOTROPIC_NONE</code> or <code>ANISOTROPIC_SINGLE_VALUE</code>
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setAnisotropicFilterMode(int mode) {
+ checkForLiveOrCompiled();
+ if ((mode != ANISOTROPIC_NONE) &&
+ (mode != ANISOTROPIC_SINGLE_VALUE)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture25"));
+ }
+ ((TextureRetained)this.retained).initAnisotropicFilterMode(mode);
+ }
+
+ /**
+ * Retrieves the anisotropic filter mode for this texture object.
+ * @return the currrent anisotropic filter mode of this texture object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getAnisotropicFilterMode() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_ANISOTROPIC_FILTER_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture26"));
+ }
+ }
+ return ((TextureRetained)this.retained).getAnisotropicFilterMode();
+ }
+
+ /**
+ * Specifies the degree of anisotropy to be
+ * used when the anisotropic filter mode specifies
+ * ANISOTROPIC_SINGLE_VALUE.
+ * @param degree degree of anisotropy
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if
+ * <code>degree</code> < 1.0 or
+ * <code>degree</code> > the maximum degree of anisotropy.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setAnisotropicFilterDegree(float degree) {
+ checkForLiveOrCompiled();
+ if (degree < 1.0) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture27"));
+ }
+ ((TextureRetained)this.retained).initAnisotropicFilterDegree(degree);
+ }
+
+ /**
+ * Retrieves the anisotropic filter degree for this texture object.
+ * @return the current degree of anisotropy of this texture object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public float getAnisotropicFilterDegree() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_ANISOTROPIC_FILTER_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture26"));
+ }
+ }
+ return ((TextureRetained)this.retained).getAnisotropicFilterDegree();
+ }
+
+ /**
+ * sets the sharpen texture LOD function for this texture object.
+ * @param lod array containing the level-of-detail values.
+ * @param pts array containing the function values for the corresponding
+ * level-of-detail values.
+ *
+ * @exception IllegalStateException if the length of <code>lod</code>
+ * does not match the length of <code>pts</code>
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setSharpenTextureFunc(float[] lod, float[] pts) {
+ checkForLiveOrCompiled();
+ if (((lod != null) && (pts != null) && (lod.length == pts.length)) ||
+ ((lod == null) && (pts == null))) {
+ ((TextureRetained)this.retained).initSharpenTextureFunc(lod, pts);
+ } else {
+ throw new IllegalStateException(
+ J3dI18N.getString("Texture22"));
+ }
+ }
+
+ /**
+ * sets the sharpen texture LOD function for this texture object.
+ * The Point2f x,y values are defined as follows: x is the lod value,
+ * y is the corresponding function value.
+ *
+ * @param pts array of Point2f containing the lod as well as the
+ * corresponding function value.
+ *
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setSharpenTextureFunc(Point2f[] pts) {
+ checkForLiveOrCompiled();
+ ((TextureRetained)this.retained).initSharpenTextureFunc(pts);
+ }
+
+ /**
+ * Gets the number of points in the sharpen texture LOD function for this
+ * texture object.
+ *
+ * @return the number of points in the sharpen texture LOD function.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getSharpenTextureFuncPointsCount() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_SHARPEN_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture21"));
+ }
+ }
+ return ((TextureRetained)this.retained).getSharpenTextureFuncPointsCount();
+ }
+
+ /**
+ * Copies the array of sharpen texture LOD function points into the
+ * specified arrays. The arrays must be large enough to hold all the
+ * points.
+ *
+ * @param lod the array to receive the level-of-detail values.
+ * @param pts the array to receive the function values for the
+ * corresponding level-of-detail values.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getSharpenTextureFunc(float[] lod, float[] pts) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_SHARPEN_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture21"));
+ }
+ }
+ ((TextureRetained)this.retained).getSharpenTextureFunc(
+ lod, pts);
+ }
+
+ /**
+ * Copies the array of sharpen texture LOD function points including
+ * the lod values and the corresponding function values into the
+ * specified array. The array must be large enough to hold all the points.
+ * The individual array elements must be allocated by the caller as well.
+ *
+ * @param pts the array to receive the sharpen texture LOD function points
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getSharpenTextureFunc(Point2f[] pts) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_SHARPEN_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture21"));
+ }
+ }
+ ((TextureRetained)this.retained).getSharpenTextureFunc(pts);
+ }
+
+ /**
+ * sets the filter4 function for this texture object.
+ * @param weights array containing samples of the filter4 function.
+ *
+ * @exception IllegalArgumentException if the length of
+ * <code>weight</code> < 4
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setFilter4Func(float[] weights) {
+ checkForLiveOrCompiled();
+ if ((weights == null) || (weights.length < 4)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture24"));
+ } else {
+ ((TextureRetained)this.retained).initFilter4Func(weights);
+ }
+ }
+
+ /**
+ * Retrieves the number of filter4 function values for this
+ * texture object.
+ *
+ * @return the number of filter4 function values
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getFilter4FuncPointsCount() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_FILTER4_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture23"));
+ }
+ }
+ return (((TextureRetained)this.retained).getFilter4FuncPointsCount());
+ }
+
+ /**
+ * Copies the array of filter4 function values into the specified
+ * array. The array must be large enough to hold all the values.
+ *
+ * @param weights the array to receive the function values.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getFilter4Func(float[] weights) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_FILTER4_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture23"));
+ }
+ }
+ ((TextureRetained)this.retained).getFilter4Func(weights);
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code>
+ * into the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ Hashtable hashtable = originalNodeComponent.nodeHashtable;
+
+ TextureRetained tex = (TextureRetained) originalNodeComponent.retained;
+ TextureRetained rt = (TextureRetained) retained;
+
+ rt.initBoundaryModeS(tex.getBoundaryModeS());
+ rt.initBoundaryModeT(tex.getBoundaryModeT());
+ rt.initMinFilter(tex.getMinFilter());
+ rt.initMagFilter(tex.getMagFilter());
+ rt.initMipMapMode(tex.getMipMapMode());
+ rt.initEnable(tex.getEnable());
+ rt.initAnisotropicFilterMode(tex.getAnisotropicFilterMode());
+ rt.initAnisotropicFilterDegree(tex.getAnisotropicFilterDegree());
+ rt.initSharpenTextureFunc(tex.getSharpenTextureFunc());
+ rt.initFilter4Func(tex.getFilter4Func());
+
+ rt.initBaseLevel(tex.getBaseLevel());
+ rt.initMaximumLevel(tex.getMaximumLevel());
+ rt.initMinimumLOD(tex.getMinimumLOD());
+ rt.initMaximumLOD(tex.getMaximumLOD());
+
+ Point3f offset = new Point3f();
+ tex.getLodOffset(offset);
+ rt.initLodOffset(offset.x, offset.y, offset.z);
+
+ Color4f c = new Color4f();
+ tex.getBoundaryColor(c);
+ rt.initBoundaryColor(c);
+
+ // No API available to get the current level
+ for (int i=tex.maxLevels-1; i>=0; i-- ) {
+ ImageComponent image = (ImageComponent)
+ getNodeComponent(tex.getImage(i),
+ forceDuplicate,
+ hashtable);
+ if (image != null) {
+ rt.initImage(i, image);
+ }
+ }
+ // TODO: clone new v1.2 attributes
+ }
+
+ /**
+ * This function is called from getNodeComponent() to see if any of
+ * the sub-NodeComponents duplicateOnCloneTree flag is true.
+ * If it is the case, current NodeComponent needs to
+ * duplicate also even though current duplicateOnCloneTree flag is false.
+ * This should be overwrite by NodeComponent which contains sub-NodeComponent.
+ */
+ boolean duplicateChild() {
+ if (getDuplicateOnCloneTree())
+ return true;
+
+ int level = ((TextureRetained) this.retained).maxLevels;
+ TextureRetained rt = (TextureRetained) retained;
+
+ for (int i=0; i < level; i++) {
+ ImageComponent img = rt.getImage(i);
+ if ((img != null) && img.getDuplicateOnCloneTree())
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/Texture2D.java b/src/classes/share/javax/media/j3d/Texture2D.java
new file mode 100644
index 0000000..e3e2015
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Texture2D.java
@@ -0,0 +1,554 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.*;
+
+
+
+/**
+ * Texture2D is a subclass of Texture class. It extends Texture
+ * class by adding a constructor and a mutator method for
+ * setting a 2D texture image.
+ * <P>
+ * Each Texture2D object has the following properties:<P>
+ * <UL>
+ * <LI>Magnification filter - the magnification filter function.
+ * Used when the pixel being rendered maps to an area less than or
+ * equal to one texel. In addition to the magnification filter functions
+ * defined in the base Texture class, the following values are
+ * supported:</LI><P>
+ * <UL>
+ * <LI>LINEAR_DETAIL - performs linear sampling in both the base level
+ * texture image and the detail texture image, and combines the two
+ * texture values according to the detail texture mode.</LI><P>
+ * <LI>LINEAR_DETAIL_RGB - performs linear detail for the rgb
+ * components only. The alpha component is computed using BASE_LEVEL_LINEAR
+ * filter.</LI><P>
+ * <LI>LINEAR_DETAIL_ALPHA - performs linear detail for the alpha
+ * component only. The rgb components are computed using BASE_LEVEL_LINEAR
+ * filter.</LI><P>
+ * </UL>
+ * <LI>Detail Texture Image - Detail texture image to be used when the texture
+ * magnification filter mode specifies LINEAR_DETAIL, LINEAR_DETAIL_ALPHA, or
+ * LINEAR_DETAIL_RGB; if the detail texture images is null, then
+ * the texture magnification filter mode will fall back to BASE_LEVEL_LINEAR.
+ * </LI><P>
+ * <LI>Detail Texture Mode - specifies how the texture image is combined
+ * with the detail image. The detail texture modes are as follows: </LI><P>
+ * <UL>
+ * <LI>DETAIL_ADD</LI><P>
+ * <UL>
+ * T' = T<sub>texture</sub> + DetailFunc(LOD) * (2 * T<sub>detail</sub> - 1)<P>
+ * </UL>
+ * <LI>DETAIL_MODULATE</LI><P>
+ * <UL>
+ * T' = T<sub>texture</sub> * (1 + DetailFunc(LOD) * (2 * T<sub>detail</sub> - 1))<P>
+ * </UL>
+ * </UL>
+ * where T<sub>texture</sub> is the texture value computed from the base level
+ * texture image, and T<sub>detail</sub> is the texture value computed from the
+ * detail texture image.<P>
+ * <LI>Detail Texture Function - specifies the function of level-of-detail
+ * used in combining the detail texture with the base level texture of this object.</LI><P>
+ * <LI>Detail Texture Level - specifies the number of levels that
+ * separate the base level image of this texture object and the detail
+ * texture image. This value is used in the linear filter
+ * calculation of the detail texture image. Note, detail texture will only
+ * be applied to the level 0 of the texture image. Hence, for detail texture
+ * to work, base level has to be set to 0.</LI><P>
+ * </UL>
+ *
+ * @see Canvas3D#queryProperties
+ */
+public class Texture2D extends Texture {
+
+ /**
+ * Specifies that this Texture object allows reading its detail
+ * texture information (e.g., detail texture image, detail texture mode,
+ * detail texture function, detail texture function points count,
+ * detail texture level)
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int
+ ALLOW_DETAIL_TEXTURE_READ = CapabilityBits.TEXTURE2D_ALLOW_DETAIL_TEXTURE_READ;
+
+ /**
+ * Performs linear sampling in both the base level
+ * texture image and the detail texture image, and combines the two
+ * texture values according to the detail texture mode.
+ *
+ * @since Java 3D 1.3
+ * @see #setMagFilter
+ */
+ public static final int LINEAR_DETAIL = 6;
+
+ /**
+ * Performs linear detail for the rgb
+ * components only. The alpha component is computed using
+ * BASE_LEVEL_LINEAR filter.
+ *
+ * @since Java 3D 1.3
+ * @see #setMagFilter
+ */
+ public static final int LINEAR_DETAIL_RGB = 7;
+
+ /**
+ * Performs linear detail for the alpha
+ * component only. The rgb components are computed using
+ * BASE_LEVEL_LINEAR filter.
+ *
+ * @since Java 3D 1.3
+ * @see #setMagFilter
+ */
+ public static final int LINEAR_DETAIL_ALPHA = 8;
+
+ /**
+ * Adds the detail texture image to the level 0 image of this texture
+ * object
+ *
+ * @since Java 3D 1.3
+ * @see #setDetailTextureMode
+ */
+ public static final int DETAIL_ADD = 0;
+
+ /**
+ * Modulates the detail texture image with the level 0 image of this
+ * texture object
+ *
+ * @since Java 3D 1.3
+ * @see #setDetailTextureMode
+ */
+ public static final int DETAIL_MODULATE = 1;
+
+
+
+ /**
+ * Constructs a texture object using default values.
+ *
+ * The default values are as follows:
+ * <ul>
+ * detail texture image: null<br>
+ * detail texture mode: DETAIL_MODULATE<br>
+ * detail texture func: null<br>
+ * detail texture level: 2<br>
+ * </ul>
+ * <p>
+ * Note that the default constructor creates a texture object with
+ * a width and height of 0 and is, therefore, not useful.
+ */
+ public Texture2D() {
+ super();
+ }
+
+ /**
+ * Constructs an empty Texture2D object with specified mipmapMode
+ * format, width and height. Image at base level must be set by
+ * the application using 'setImage' method. If mipmapMode is
+ * set to MULTI_LEVEL_MIPMAP, images for base level through maximum level
+ * must be set.
+ * @param mipMapMode type of mipmap for this Texture: One of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP.
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA.
+ * @param width width of image at level 0. Must be power of 2.
+ * @param height height of image at level 0. Must be power of 2.
+ * @exception IllegalArgumentException if width or height are NOT
+ * power of 2 OR invalid format/mipmapMode is specified.
+ */
+ public Texture2D(
+ int mipMapMode,
+ int format,
+ int width,
+ int height){
+
+ super(mipMapMode, format, width, height);
+ }
+
+
+ /**
+ * Constructs an empty Texture2D object with specified mipMapMode,
+ * format, width, height, and boundaryWidth.
+ * Defaults are used for all other
+ * parameters. If <code>mipMapMode</code> is set to
+ * <code>BASE_LEVEL</code>, then the image at level 0 must be set
+ * by the application (using either the <code>setImage</code> or
+ * <code>setImages</code> method). If <code>mipMapMode</code> is
+ * set to <code>MULTI_LEVEL_MIPMAP</code>, then images for levels
+ * Base Level through Maximum Level must be set.
+ *
+ * @param mipMapMode type of mipmap for this Texture: one of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA
+ * @param width width of image at level 0. Must be power of 2. This
+ * does not include the width of the boundary.
+ * @param height height of image at level 0. Must be power of 2. This
+ * does not include the width of the boundary.
+ * @param boundaryWidth width of the boundary.
+ * @exception IllegalArgumentException if width or height are not a
+ * power of 2, if an invalid format or mipMapMode is specified, or
+ * if the boundaryWidth < 0
+ *
+ * @since Java 3D 1.3
+ */
+ public Texture2D(int mipMapMode,
+ int format,
+ int width,
+ int height,
+ int boundaryWidth) {
+
+ super(mipMapMode, format, width, height, boundaryWidth);
+ }
+
+ /**
+ * Sets the magnification filter function. This
+ * function is used when the pixel being rendered maps to an area
+ * less than or equal to one texel.
+ * @param magFilter the magnification filter, one of:
+ * FASTEST, NICEST, BASE_LEVEL_POINT, BASE_LEVEL_LINEAR,
+ * LINEAR_DETAIL, LINEAR_DETAIL_RGB, LINEAR_DETAIL_ALPHA,
+ * LINEAR_SHARPEN, LINEAR_SHARPEN_RGB, LINEAR_SHARPEN_ALPHA, or FILTER4.
+ *
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if <code>minFilter</code>
+ * is a value other than <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>BASE_LEVEL_POINT</code>, <code>BASE_LEVEL_LINEAR</code>,
+ * <code>LINEAR_DETAIL</code>, <code>LINEAR_DETAIL_RGB</code>,
+ * <code>LINEAR_DETAIL_ALPHA</code>,
+ * <code>LINEAR_SHARPEN</code>, <code>LINEAR_SHARPEN_RGB</code>,
+ * <code>LINEAR_SHARPEN_ALPHA</code>, or
+ * <code>FILTER4</code>.
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setMagFilter(int magFilter) {
+ checkForLiveOrCompiled();
+
+ switch (magFilter) {
+ case FASTEST:
+ case NICEST:
+ case BASE_LEVEL_POINT:
+ case BASE_LEVEL_LINEAR:
+ case LINEAR_DETAIL:
+ case LINEAR_DETAIL_RGB:
+ case LINEAR_DETAIL_ALPHA:
+ case LINEAR_SHARPEN:
+ case LINEAR_SHARPEN_RGB:
+ case LINEAR_SHARPEN_ALPHA:
+ case FILTER4:
+ break;
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("Texture29"));
+ }
+
+ ((Texture2DRetained)this.retained).initMagFilter(magFilter);
+ }
+
+ /**
+ * Sets the detail texture image for this texture object.
+ * @param detailTexture ImageComponent2D object containing the
+ * detail texture image.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setDetailImage(ImageComponent2D detailTexture) {
+ checkForLiveOrCompiled();
+ ((Texture2DRetained)this.retained).initDetailImage(detailTexture);
+ }
+
+ /**
+ * Retrieves the detail texture image for this texture object.
+ * @return ImageComponent2D object containing the detail texture image.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public ImageComponent2D getDetailImage() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_DETAIL_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture2D0"));
+ }
+ }
+ return ((Texture2DRetained)this.retained).getDetailImage();
+ }
+
+ /**
+ * Sets the detail texture mode for this texture object.
+ * @param mode detail texture mode. One of: DETAIL_ADD or DETAIL_MODULATE
+ *
+ * @exception IllegalArgumentException if
+ * <code>mode</code> is a value other than
+ * <code>DETAIL_ADD</code>, or <code>DETAIL_MODULATE</code>
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setDetailTextureMode(int mode) {
+ checkForLiveOrCompiled();
+ if ((mode != DETAIL_ADD) && (mode != DETAIL_MODULATE)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture2D1"));
+ }
+ ((Texture2DRetained)this.retained).initDetailTextureMode(mode);
+ }
+
+ /**
+ * Retrieves the detail texture mode for this texture object.
+ * @return the detail texture mode.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getDetailTextureMode() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_DETAIL_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture2D0"));
+ }
+ }
+ return ((Texture2DRetained)this.retained).getDetailTextureMode();
+ }
+
+ /**
+ * Sets the detail texture level for this texture object.
+ * @param level the detail texture level.
+ *
+ * @exception IllegalArgumentException if <code>level</code> < 0
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setDetailTextureLevel(int level) {
+ checkForLiveOrCompiled();
+ if (level < 0) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture2D2"));
+ }
+ ((Texture2DRetained)this.retained).initDetailTextureLevel(level);
+ }
+
+ /**
+ * Retrieves the detail texture level for this texture object.
+ * @return the detail texture level.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getDetailTextureLevel() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_DETAIL_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture2D0"));
+ }
+ }
+ return ((Texture2DRetained)this.retained).getDetailTextureLevel();
+ }
+
+ /**
+ * sets the detail texture LOD function for this texture object.
+ * @param lod array containing the level-of-detail values.
+ * @param pts array containing the function values for the corresponding
+ * level-of-detail values.
+ *
+ * @exception IllegalStateException if the length of <code>lod</code>
+ * does not match the length of <code>pts</code>
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setDetailTextureFunc(float[] lod, float[] pts) {
+ checkForLiveOrCompiled();
+ if (((lod != null) && (pts != null) && (lod.length == pts.length)) ||
+ ((lod == null) && (pts == null))) {
+ ((Texture2DRetained)this.retained).initDetailTextureFunc(lod, pts);
+ } else {
+ throw new IllegalStateException(J3dI18N.getString("Texture2D3"));
+ }
+ }
+
+ /**
+ * sets the detail texture LOD function for this texture object.
+ * The Point2f x,y values are defined as follows: x is the lod value,
+ * y is the corresponding function value.
+ *
+ * @param pts array of Point2f containing the lod as well as the
+ * corresponding function value.
+ *
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+ public void setDetailTextureFunc(Point2f[] pts) {
+ checkForLiveOrCompiled();
+ ((Texture2DRetained)this.retained).initDetailTextureFunc(pts);
+ }
+
+ /**
+ * Gets the number of points in the detail texture LOD function for this
+ * texture object.
+ *
+ * @return the number of points in the detail texture LOD function.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getDetailTextureFuncPointsCount() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_DETAIL_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture2D0"));
+ }
+ }
+ return ((Texture2DRetained)this.retained).getDetailTextureFuncPointsCount();
+ }
+
+ /**
+ * Copies the array of detail texture LOD function points into the
+ * specified arrays. The arrays must be large enough to hold all the
+ * points.
+ *
+ * @param lod the array to receive the level-of-detail values.
+ * @param pts the array to receive the function values for the
+ * corresponding level-of-detail values.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getDetailTextureFunc(float[] lod, float[] pts) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_DETAIL_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture2D0"));
+ }
+ }
+ ((Texture2DRetained)this.retained).getDetailTextureFunc(lod, pts);
+ }
+
+ /**
+ * Copies the array of detail texture LOD function points including
+ * the lod values and the corresponding function values into the
+ * specified array. The array must be large enough to hold all the points.
+ * The individual array elements must be allocated by the caller as well.
+ *
+ * @param pts the array to receive the detail texture LOD function points
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void getDetailTextureFunc(Point2f[] pts) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_DETAIL_TEXTURE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("Texture2D0"));
+ }
+ }
+ ((Texture2DRetained)this.retained).getDetailTextureFunc(pts);
+ }
+
+
+ /**
+ * Creates a retained mode Texture2DRetained object that this
+ * Texture2D component object will point to.
+ */
+ void createRetained() {
+ this.retained = new Texture2DRetained();
+ this.retained.setSource(this);
+ }
+
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ Texture2DRetained t2d = (Texture2DRetained) retained;
+
+ Texture2D t = new Texture2D(t2d.getMipMapMode(), t2d.format,
+ t2d.width, t2d.height);
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+
+ /**
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @deprecated replaced with duplicateNodeComponent(
+ * NodeComponent originalNodeComponent, boolean forceDuplicate)
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+ checkDuplicateNodeComponent(originalNodeComponent);
+ }
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ Texture2DRetained tex = (Texture2DRetained)
+ originalNodeComponent.retained;
+ Texture2DRetained rt = (Texture2DRetained) retained;
+
+
+ rt.initDetailImage(tex.getDetailImage());
+ rt.initDetailTextureMode(tex.getDetailTextureMode());
+ rt.initDetailTextureLevel(tex.getDetailTextureLevel());
+ rt.initDetailTextureFunc(tex.getDetailTextureFunc());
+ }
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/Texture2DRetained.java b/src/classes/share/javax/media/j3d/Texture2DRetained.java
new file mode 100644
index 0000000..5eb60a4
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Texture2DRetained.java
@@ -0,0 +1,357 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import javax.vecmath.*;
+
+/**
+ * Texture2D is a subclass of Texture class. It extends Texture
+ * class by adding a constructor and a mutator method for
+ * setting a 2D texture image.
+ */
+class Texture2DRetained extends TextureRetained {
+
+ // currently detail image is only applicable to 2D texture
+
+ // detail texture info
+
+ int detailTextureId = -1;
+ ImageComponent2DRetained detailImage = null;
+ DetailTextureImage detailTexture = null;
+ int detailTextureMode = Texture2D.DETAIL_MODULATE;
+ int detailTextureLevel = 2;
+ int numDetailTextureFuncPts = 0;
+ float detailTextureFuncPts[] = null; // array of pairs of floats
+ // first value for LOD
+ // second value for the fcn value
+
+ /**
+ * Set detail texture image
+ */
+ final void initDetailImage(ImageComponent2D image) {
+ if (image == null) {
+ detailImage = null;
+ } else {
+ detailImage = (ImageComponent2DRetained)image.retained;
+ detailImage.setTextureRef();
+ }
+ }
+
+
+ /**
+ * Get detail texture image
+ */
+ final ImageComponent2D getDetailImage() {
+ if (detailImage != null) {
+ return (ImageComponent2D)detailImage.source;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Set detail texture mode
+ */
+ final void initDetailTextureMode(int mode) {
+ detailTextureMode = mode;
+ }
+
+
+ /**
+ * Get detail texture mode
+ */
+ final int getDetailTextureMode() {
+ return detailTextureMode;
+ }
+
+
+ /**
+ * Set detail texture level
+ */
+ final void initDetailTextureLevel(int level) {
+ detailTextureLevel = level;
+ }
+
+
+ /**
+ * Get detail texture level
+ */
+ final int getDetailTextureLevel() {
+ return detailTextureLevel;
+ }
+
+
+ /**
+ * Set detail texture function
+ */
+ final void initDetailTextureFunc(float[] lod, float[] pts) {
+ if (lod == null) { // pts will be null too.
+ detailTextureFuncPts = null;
+ numDetailTextureFuncPts = 0;
+ } else {
+ numDetailTextureFuncPts = lod.length;
+ if ((detailTextureFuncPts == null) ||
+ (detailTextureFuncPts.length != lod.length * 2)) {
+ detailTextureFuncPts = new float[lod.length * 2];
+ }
+ for (int i = 0, j = 0; i < lod.length; i++) {
+ detailTextureFuncPts[j++] = lod[i];
+ detailTextureFuncPts[j++] = pts[i];
+ }
+ }
+ }
+
+ final void initDetailTextureFunc(Point2f[] pts) {
+ if (pts == null) {
+ detailTextureFuncPts = null;
+ numDetailTextureFuncPts = 0;
+ } else {
+ numDetailTextureFuncPts = pts.length;
+ if ((detailTextureFuncPts == null) ||
+ (detailTextureFuncPts.length != pts.length * 2)) {
+ detailTextureFuncPts = new float[pts.length * 2];
+ }
+ for (int i = 0, j = 0; i < pts.length; i++) {
+ detailTextureFuncPts[j++] = pts[i].x;
+ detailTextureFuncPts[j++] = pts[i].y;
+ }
+ }
+ }
+
+ final void initDetailTextureFunc(float[] pts) {
+ if (pts == null) {
+ detailTextureFuncPts = null;
+ numDetailTextureFuncPts = 0;
+ } else {
+ numDetailTextureFuncPts = pts.length / 2;
+ if ((detailTextureFuncPts == null) ||
+ (detailTextureFuncPts.length != pts.length)) {
+ detailTextureFuncPts = new float[pts.length];
+ }
+ for (int i = 0; i < pts.length; i++) {
+ detailTextureFuncPts[i] = pts[i];
+ }
+ }
+ }
+
+ /**
+ * Get number of points in the detail texture LOD function
+ */
+ final int getDetailTextureFuncPointsCount() {
+ return numDetailTextureFuncPts;
+ }
+
+
+ /**
+ * Copies the array of detail texture LOD function points into the
+ * specified arrays
+ */
+ final void getDetailTextureFunc(float[] lod, float[] pts) {
+ if (detailTextureFuncPts != null) {
+ for (int i = 0, j = 0; i < numDetailTextureFuncPts; i++) {
+ lod[i] = detailTextureFuncPts[j++];
+ pts[i] = detailTextureFuncPts[j++];
+ }
+ }
+ }
+
+ final void getDetailTextureFunc(Point2f[] pts) {
+ if (detailTextureFuncPts != null) {
+ for (int i = 0, j = 0; i < numDetailTextureFuncPts; i++) {
+ pts[i].x = detailTextureFuncPts[j++];
+ pts[i].y = detailTextureFuncPts[j++];
+ }
+ }
+ }
+
+
+ /**
+ * internal method only -- returns the detail texture LOD function
+ */
+ final float[] getDetailTextureFunc() {
+ return detailTextureFuncPts;
+ }
+
+ synchronized void initMirrorObject() {
+
+ super.initMirrorObject();
+
+ Texture2DRetained mirrorTexture = (Texture2DRetained)mirror;
+
+ // detail texture info
+ mirrorTexture.detailImage = detailImage;
+ mirrorTexture.detailTextureMode = detailTextureMode;
+ mirrorTexture.detailTextureLevel = detailTextureLevel;
+ mirrorTexture.detailTexture = null;
+ mirrorTexture.numDetailTextureFuncPts = numDetailTextureFuncPts;
+
+ if (detailTextureFuncPts == null) {
+ mirrorTexture.detailTextureFuncPts = null;
+ } else {
+ if ((mirrorTexture.detailTextureFuncPts == null) ||
+ (mirrorTexture.detailTextureFuncPts.length !=
+ detailTextureFuncPts.length)) {
+ mirrorTexture.detailTextureFuncPts =
+ new float[detailTextureFuncPts.length];
+ }
+ for (int i = 0; i < detailTextureFuncPts.length; i++) {
+ mirrorTexture.detailTextureFuncPts[i] =
+ detailTextureFuncPts[i];
+ }
+
+ // add detail texture to the user list of the image
+ // only if detail texture is to be used
+ if ((mirrorTexture.detailImage != null) &&
+ (mirrorTexture.magFilter >= Texture2D.LINEAR_DETAIL) &&
+ (mirrorTexture.magFilter <= Texture2D.LINEAR_DETAIL_ALPHA)) {
+ mirrorTexture.detailImage.addUser(mirrorTexture);
+ }
+ }
+ }
+
+ void clearLive(int refCount) {
+ super.clearLive(refCount);
+
+ // remove detail texture from the user list of the image
+ if ((detailImage != null) &&
+ (magFilter >= Texture2D.LINEAR_DETAIL) &&
+ (magFilter <= Texture2D.LINEAR_DETAIL_ALPHA)) {
+ detailImage.removeUser(mirror);
+ }
+ }
+
+ // overload the incTextureBinRefCount method to take care
+ // of detail texture ref as well
+ // This method is called from RenderBin when a new TextureBin
+ // is created. And all the referenced textures in that TextureBin
+ // will be notified to increment the TextureBin reference count.
+
+ void incTextureBinRefCount(TextureBin tb) {
+ super.incTextureBinRefCount(tb);
+
+ // increment detail texture ref count
+
+ if ((detailImage != null) &&
+ (magFilter >= Texture2D.LINEAR_DETAIL) &&
+ (magFilter <= Texture2D.LINEAR_DETAIL_ALPHA)) {
+ if (detailTexture == null) {
+ detailTexture = detailImage.getDetailTexture();
+ }
+
+ detailTexture.incTextureBinRefCount(format, tb);
+ }
+ }
+
+ // This method is called from AttributeBin when a TextureBin
+ // is to be removed. And all the referenced textures in that TextureBin
+ // will be notified to decrement the TextureBin reference count.
+ // And if detail texture exists, we need to decrement the
+ // TextureBin reference count of the detail texture as well.
+
+ void decTextureBinRefCount(TextureBin tb) {
+ super.decTextureBinRefCount(tb);
+
+ // decrement detail texture ref count
+
+ if (detailTexture != null) {
+ detailTexture.decTextureBinRefCount(format, tb);
+ }
+ }
+
+
+
+ native void bindDetailTexture(long ctx, int objectId);
+
+ native void updateTextureImage(long ctx,
+ int numLevels,
+ int level,
+ int internalFormat, int format,
+ int width, int height,
+ int boundaryWidth, byte[] imageYup);
+
+ native void updateTextureSubImage(long ctx,
+ int level, int xoffset, int yoffset,
+ int internalFormat,int format,
+ int imgXOffset, int imgYOffset,
+ int tilew,
+ int width, int height,
+ byte[] image);
+
+ native void updateDetailTextureParameters(long ctx,
+ int detailTextureMode,
+ int detailTextureLevel,
+ int nPts, float[] pts);
+ // wrapper to the native call
+
+ void updateTextureImage(Canvas3D cv, int face,
+ int numLevels, int level,
+ int format, int storedFormat,
+ int width, int height,
+ int boundaryWidth,
+ byte[] data) {
+
+ updateTextureImage(cv.ctx, numLevels, level, format,
+ storedFormat, width, height,
+ boundaryWidth, data);
+ }
+
+
+
+ // wrapper to the native call
+
+ void updateTextureSubImage(Canvas3D cv, int face, int level,
+ int xoffset, int yoffset, int format,
+ int storedFormat, int imgXOffset,
+ int imgYOffset, int tileWidth,
+ int width, int height, byte[] data) {
+
+ updateTextureSubImage(cv.ctx, level, xoffset, yoffset, format,
+ storedFormat, imgXOffset, imgYOffset,
+ tileWidth, width, height, data);
+ }
+
+
+ void updateNative(Canvas3D cv) {
+
+ // update mipmap texture
+
+ super.updateNative(cv);
+
+
+ // update detail texture if exists
+
+ if (detailTexture != null) {
+ detailTexture.updateNative(cv, format);
+ }
+ }
+
+
+ // update texture parameters
+
+ void updateTextureFields(Canvas3D cv) {
+
+ super.updateTextureFields(cv);
+
+ // update detail texture parameters if applicable
+
+ if (detailTexture != null) {
+
+ updateDetailTextureParameters(cv.ctx, detailTextureMode,
+ detailTextureLevel, numDetailTextureFuncPts,
+ detailTextureFuncPts);
+ }
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/Texture3D.java b/src/classes/share/javax/media/j3d/Texture3D.java
new file mode 100644
index 0000000..6cceb1d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Texture3D.java
@@ -0,0 +1,220 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Texture3D is a subclass of Texture class. It extends Texture
+ * class by adding a third coordinate, constructor and a mutator
+ * method for setting a 3D texture image.
+ * If 3D texture mapping is not supported on a particular Canvas3D,
+ * 3D texture mapping is ignored for that canvas.
+ *
+ * @see Canvas3D#queryProperties
+ */
+
+public class Texture3D extends Texture {
+
+ /**
+ * Constructs a Texture3D object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * depth : 0<br>
+ * boundary mode R : WRAP<br>
+ * </ul>
+ * <p>
+ * Note that the default constructor creates a texture object with
+ * a width, height, and depth of 0 and is, therefore, not useful.
+ */
+ public Texture3D() {
+ super();
+ }
+
+ /**
+ * Constructs an empty Texture3D object with specified mipmapMode
+ * format, width, height, and depth. Image at base level must be set by
+ * the application using 'setImage' method. If mipmapMode is
+ * set to MULTI_LEVEL_MIPMAP, images for base level through
+ * maximum level must be set.
+ * @param mipmapMode type of mipmap for this Texture: One of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP.
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA.
+ * @param width width of image at level 0. Must be power of 2.
+ * @param height height of image at level 0. Must be power of 2.
+ * @param depth depth of image at level 0. Must be power of 2.
+ * @exception IllegalArgumentException if width or height are NOT
+ * power of 2 OR invalid format/mipmapMode is specified.
+ */
+ public Texture3D(int mipmapMode,
+ int format,
+ int width,
+ int height,
+ int depth) {
+
+ super(mipmapMode, format, width, height);
+ int depthPower = getPowerOf2(depth);
+ if (depthPower == -1)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture3D1"));
+
+ ((Texture3DRetained)this.retained).setDepth(depth);
+ }
+
+ /**
+ * Constructs an empty Texture3D object with specified mipmapMode
+ * format, width, height, depth, and boundaryWidth.
+ * Image at base level must be set by
+ * the application using 'setImage' method. If mipmapMode is
+ * set to MULTI_LEVEL_MIPMAP, images for base level through
+ * maximum level must be set.
+ * @param mipmapMode type of mipmap for this Texture: One of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP.
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA.
+ * @param width width of image at level 0. Must be power of 2.
+ * @param height height of image at level 0. Must be power of 2.
+ * @param depth depth of image at level 0. Must be power of 2.
+ * @param boundaryWidth width of the boundary.
+ * @exception IllegalArgumentException if width or height are NOT
+ * power of 2 OR invalid format/mipmapMode is specified, or
+ * if the boundaryWidth < 0
+ *
+ * @since Java 3D 1.3
+ */
+ public Texture3D(int mipmapMode,
+ int format,
+ int width,
+ int height,
+ int depth,
+ int boundaryWidth) {
+
+ super(mipmapMode, format, width, height, boundaryWidth);
+ int depthPower = getPowerOf2(depth);
+ if (depthPower == -1)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture3D1"));
+
+ ((Texture3DRetained)this.retained).setDepth(depth);
+ }
+
+ /**
+ * Sets the boundary mode for the R coordinate in this texture object.
+ * @param boundaryModeR the boundary mode for the R coordinate,
+ * one of: CLAMP, WRAP, CLAMP_TO_EDGE, or CLAMP_TO_BOUNDARY
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if <code>boundaryModeR</code>
+ * is a value other than <code>CLAMP</code>, <code>WRAP</code>,
+ * <code>CLAMP_TO_EDGE</code>, or <code>CLAMP_TO_BOUNDARY</code>.
+ */
+ public void setBoundaryModeR(int boundaryModeR) {
+ checkForLiveOrCompiled();
+ switch (boundaryModeR) {
+ case Texture.CLAMP:
+ case Texture.WRAP:
+ case Texture.CLAMP_TO_EDGE:
+ case Texture.CLAMP_TO_BOUNDARY:
+ break;
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("Texture31"));
+ }
+ ((Texture3DRetained)this.retained).initBoundaryModeR(boundaryModeR);
+ }
+
+ /**
+ * Retrieves the boundary mode for the R coordinate.
+ * @return the current boundary mode for the R coordinate.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public int getBoundaryModeR() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(Texture.ALLOW_BOUNDARY_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture3D0"));
+ return ((Texture3DRetained)this.retained).getBoundaryModeR();
+ }
+
+ /**
+ * Retrieves the depth of this Texture3D object.
+ * @return the depth of this Texture3D object.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getDepth() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_SIZE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("Texture3D2"));
+
+ return ((Texture3DRetained)this.retained).getDepth();
+ }
+
+ /**
+ * Creates a retained mode Texture3DRetained object that this
+ * Texture3D component object will point to.
+ */
+ void createRetained() {
+ this.retained = new Texture3DRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ Texture3DRetained t3d = (Texture3DRetained) retained;
+ Texture3D t = new Texture3D(t3d.getMipMapMode(), t3d.format,
+ t3d.width, t3d.height, t3d.depth);
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+
+
+ /**
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @deprecated replaced with duplicateNodeComponent(
+ * NodeComponent originalNodeComponent, boolean forceDuplicate)
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+ checkDuplicateNodeComponent(originalNodeComponent);
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ ((Texture3DRetained) retained).initBoundaryModeR(((Texture3DRetained)
+ originalNodeComponent.retained).getBoundaryModeR());
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Texture3DRetained.java b/src/classes/share/javax/media/j3d/Texture3DRetained.java
new file mode 100644
index 0000000..04c06fc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Texture3DRetained.java
@@ -0,0 +1,199 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.Enumeration;
+import java.util.BitSet;
+
+/**
+ * Texture3D is a subclass of Texture class. It extends Texture
+ * class by adding a third co-ordinate, constructor and a mutator
+ * method for setting a 3D texture image.
+ */
+
+class Texture3DRetained extends TextureRetained {
+ // Boundary mode for R coordinate (wrap, clamp)
+ int boundaryModeR = Texture.WRAP;
+ int depth = 1; // Depth (num slices) of texture map (2**p)
+
+ final void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ final int getDepth() {
+ return this.depth;
+ }
+
+ /**
+ * Sets the boundary mode for the R coordinate in this texture object.
+ * @param boundaryModeR the boundary mode for the R coordinate,
+ * one of: CLAMP or WRAP.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final void initBoundaryModeR(int boundaryModeR) {
+ this.boundaryModeR = boundaryModeR;
+ }
+
+ /**
+ * Retrieves the boundary mode for the R coordinate.
+ * @return the current boundary mode for the R coordinate.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final int getBoundaryModeR() {
+ return boundaryModeR;
+ }
+
+ /**
+ * This method updates the native context.
+ */
+ native void bindTexture(long ctx, int objectId, boolean enable);
+
+ native void updateTextureBoundary(long ctx,
+ int boundaryModeS, int boundaryModeT,
+ int boundaryModeR, float boundaryRed,
+ float boundaryGreen, float boundaryBlue,
+ float boundaryAlpha);
+
+ native void updateTextureFilterModes(long ctx,
+ int minFilter, int magFilter);
+
+ native void updateTextureSharpenFunc(long ctx,
+ int numSharpenTextureFuncPts,
+ float[] sharpenTextureFuncPts);
+
+ native void updateTextureFilter4Func(long ctx,
+ int numFilter4FuncPts,
+ float[] filter4FuncPts);
+
+ native void updateTextureAnisotropicFilter(long ctx, float degree);
+
+
+ native void updateTextureImage(long ctx, int numLevels, int level,
+ int format, int internalFormat, int width,
+ int height, int depth,
+ int boundaryWidth, byte[] imageYup);
+
+ native void updateTextureSubImage(long ctx, int level,
+ int xoffset, int yoffset, int zoffset,
+ int internalFormat, int format,
+ int imgXoffset, int imgYoffset, int imgZoffset,
+ int tilew, int tileh,
+ int width, int height, int depth,
+ byte[] imageYup);
+
+
+ // get an ID for Texture3D
+
+ int getTextureId() {
+ return (VirtualUniverse.mc.getTexture3DId());
+ }
+
+
+ // get a Texture3D Id
+
+ void freeTextureId(int id) {
+ synchronized (resourceLock) {
+ if (objectId == id) {
+ objectId = -1;
+ VirtualUniverse.mc.freeTexture3DId(id);
+ }
+ }
+ }
+
+
+ // load level 0 image with null data pointer, just to enable
+ // mipmapping when level 0 is not the base level
+
+ void updateTextureDimensions(Canvas3D cv) {
+ updateTextureImage(cv.ctx, maxLevels, 0,
+ format, ImageComponentRetained.BYTE_RGBA,
+ width, height, depth, boundaryWidth, null);
+ }
+
+
+ void updateTextureBoundary(Canvas3D cv) {
+ updateTextureBoundary(cv.ctx,
+ boundaryModeS, boundaryModeT, boundaryModeR,
+ boundaryColor.x, boundaryColor.y,
+ boundaryColor.z, boundaryColor.w);
+
+ }
+
+ void reloadTextureImage(Canvas3D cv, int face, int level,
+ ImageComponentRetained image, int numLevels) {
+
+/*
+ System.out.println("Texture3D.reloadTextureImage: level= " + level +
+ " image.imageYup= " + image.imageYup + " w= " + image.width +
+ " h= " + image.height + " d= " + depth +
+ " numLevels= " + numLevels);
+*/
+
+
+ updateTextureImage(cv.ctx, numLevels, level, format,
+ image.storedYupFormat,
+ image.width, image.height, depth,
+ boundaryWidth, image.imageYup);
+
+ }
+
+ void reloadTextureSubImage(Canvas3D cv, int level, int face,
+ ImageComponentUpdateInfo info,
+ ImageComponentRetained image) {
+ int x = info.x,
+ y = info.y,
+ z = info.z,
+ width = info.width,
+ height = info.height;
+
+ int xoffset = x - image.minX;
+ int yoffset = y - image.minY;
+
+ updateTextureSubImage(cv.ctx, level, xoffset, yoffset, z,
+ format, image.storedYupFormat,
+ xoffset, yoffset, z,
+ image.width, image.height,
+ width, height, 1, image.imageYup);
+ }
+
+
+
+ protected void finalize() {
+
+ if (objectId > 0) {
+ // memory not yet free
+ // send a message to the request renderer
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ boolean found = false;
+
+ for (Enumeration e = Screen3D.deviceRendererMap.elements();
+ e.hasMoreElements(); ) {
+ Renderer rdr = (Renderer) e.nextElement();
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = null;
+ renderMessage.args[1] = new Integer(objectId);
+ renderMessage.args[2] = "3D";
+ rdr.rendererStructure.addMessage(renderMessage);
+ }
+ objectId = -1;
+ }
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/TextureAttributes.java b/src/classes/share/javax/media/j3d/TextureAttributes.java
new file mode 100644
index 0000000..cb41875
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureAttributes.java
@@ -0,0 +1,1403 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color4f;
+
+/**
+ * The TextureAttributes object defines attributes that apply to
+ * texture mapping.
+ * The texture attributes include the following:<P>
+ * <UL>
+ * <LI>Texture mode - defines how the object and texture colors
+ * are blended. The mode may be one of the following:</LI><P>
+ * <UL>
+ * <LI>MODULATE - modulates the incoming color with the texture
+ * color.<P>
+ * <UL>
+ * C' = C Ct
+ * </UL></LI><P>
+ * <LI>DECAL - applies the texture color to the incoming color as a decal.<P>
+ * <UL>
+ * C'<sub>rgb</sub> = C<sub>rgb</sub> (1 - Ct<sub>a</sub>) + Ct<sub>rgb</sub> Ct<sub>a</sub><P>
+ * C'<sub>a</sub> = C<sub>a</sub>
+ * </UL></LI><P>
+ * <LI>BLEND - blends the texture blend color with the incoming color.<P>
+ * <UL>
+ * C'<sub>rgb</sub> = C<sub>rgb</sub> (1 - Ct<sub>rgb</sub>) + Cb<sub>rgb</sub> Ct<sub>rgb</sub><P>
+ * C'<sub>a</sub> = C<sub>a</sub> Ct<sub>a</sub><P>
+ * </UL>
+ * Note that if the texture format is INTENSITY, alpha is computed identically
+ * to red, green, and blue: <P>
+ * <UL>
+ * C'<sub>a</sub> = C<sub>a</sub> (1 - Ct<sub>a</sub>) + Cb<sub>a</sub> Ct<sub>a</sub>
+ * </UL></LI><P>
+ * <LI>REPLACE - replaces the incoming color with the texture color.<P>
+ * <UL>
+ * C' = Ct <P>
+ * </UL></LI><P>
+ * <LI>COMBINE - combines the object color with the texture color or texture
+ * blend color according to the combine operation as specified in the
+ * texture combine mode. </LI><P>
+ * <p>
+ * </UL>
+ * C = Incoming color to the texture unit state. For texture unit state 0, C is the object color
+ * Ct = Texture color<br>
+ * Cb = Texture blend color<br>
+ * <p>
+ * <LI>Combine Mode - defines the combine operation when texture mode
+ * specifies COMBINE. The combine mode includes the following:<p>
+ * <UL>
+ * <LI>COMBINE_REPLACE<P>
+ * <UL>
+ * C' = C<sub>0</sub> <P>
+ * </UL></LI><P>
+ * <LI>COMBINE_MODULATE<P>
+ * <UL>
+ * C' = C<sub>0</sub> C<sub>1</sub>
+ * </UL></LI><P>
+ * <LI>COMBINE_ADD<P>
+ * <UL>
+ * C' = C<sub>0</sub> + C<sub>1</sub> <P>
+ * </UL></LI><P>
+ * <LI>COMBINE_ADD_SIGNED <P>
+ * <UL>
+ * C' = C<sub>0</sub> + C<sub>1</sub> - 0.5 <P>
+ * </UL></LI><P>
+ * <LI>COMBINE_SUBTRACT <P>
+ * <UL>
+ * C' = C<sub>0</sub> - C<sub>1</sub> <P>
+ * </UL></LI><P>
+ * <LI>COMBINE_INTERPOLATE<P>
+ * <UL>
+ * C' = C<sub>0</sub> C<sub>2</sub> + C<sub>1</sub> (1 - C<sub>2</sub>) <P>
+ * </UL></LI><P>
+ * <LI>COMBINE_DOT3<P>
+ * <UL>
+ * C' = 4 * (
+ * (C<sub>0<sub>r</sub></sub> - 0.5) * (C<sub>1<sub>r</sub></sub> - 0.5) +
+ * (C<sub>0<sub>g</sub></sub> - 0.5) * (C<sub>1<sub>g</sub></sub> - 0.5) +
+ * (C<sub>0<sub>b</sub></sub> - 0.5) * (C<sub>1<sub>b</sub></sub> - 0.5))<P>
+ * where C<sub>N<sub>x</sub></sub> is the x component of the Nth color operand
+ * in the combine operation.<P>
+ * The value C' will be placed to the all three r,g,b components or the
+ * a component of the output.
+ * </UL></LI><P>
+ * </UL></LI><P>
+ * where C<sub>0</sub>, C<sub>1</sub> and C<sub>2</sub> are determined by
+ * the color source, and the color operand.
+ * </UL></LI><P>
+ * <UL>
+ * <LI>Combine Color Source - defines the source for a color operand in the
+ * combine operation. The color source includes the following:<p>
+ * <UL>
+ * <LI> COMBINE_OBJECT_COLOR - object color<P>
+ * <LI> COMBINE_TEXTURE_COLOR - texture color<P>
+ * <LI> COMBINE_CONSTANT_COLOR - texture blend color<P>
+ * <LI> COMBINE_PREVIOUS_TEXTURE_UNIT_STATE - color from the previous texture
+ * unit state. For texture unit state 0, this is equivalent to
+ * COMBINE_OBJECT_COLOR.<P>
+ * </UL></LI><P>
+ * <LI>Combine Color Function - specifies the function for a color operand
+ * in the combine operation. The valid values are:<P>
+ * <UL>
+ * <LI>COMBINE_SRC_COLOR - the color function is f = C<sub>rgb</sub><P>
+ * <LI>COMBINE_ONE_MINUS_SRC_COLOR - the color function is f = (1 - C<sub>rgb</sub>)<P>
+ * <LI>COMBINE_SRC_ALPHA - the color function is f = C<sub>a</sub><P>
+ * <LI>COMBINE_ONE_MINUS_SRC_ALPHA - the color function is f = (1 - C<sub>a</sub>)<P>
+ * </UL></LI><P>
+ * <LI>Combine scale factor - specifies the scale factor to be applied to
+ * the output color of the combine operation. The valid values include:
+ * 1, 2, or 4.</LI><P>
+ * <LI>Transform - the texture transform object used to transform
+ * texture coordinates. The texture transform can translate, scale,
+ * or rotate the texture coordinates before the texture is applied
+ * to the object.</LI><P>
+ * <LI>Blend color - the constant texture blend color</LI><P>
+ * <LI>Perspective correction - the perspective correction mode
+ * used for color and texture coordinate interpolation. One of
+ * the following:</LI><P>
+ * <UL>
+ * <LI>NICEST - uses the nicest (highest quality) available
+ * method for texture mapping perspective correction.</LI><P>
+ * <LI>FASTEST - uses the fastest available method for texture
+ * mapping perspective correction.</LI><P>
+ * </UL>
+ * <LI>Texture color table - defines a table that is used to look up
+ * texture colors before applying the texture mode.</LI>
+ * </UL>
+ *
+ * @see Appearance
+ * @see Canvas3D#queryProperties
+ */
+public class TextureAttributes extends NodeComponent {
+ /**
+ * Specifies that this TextureAttributes object allows
+ * reading its texture mode component
+ * information and perspective correction mode.
+ */
+ public static final int
+ ALLOW_MODE_READ = CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_MODE_READ;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * writing its texture mode component
+ * information and perspective correction mode.
+ */
+ public static final int
+ ALLOW_MODE_WRITE = CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_MODE_WRITE;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * reading its texture blend color component
+ * information.
+ */
+ public static final int
+ ALLOW_BLEND_COLOR_READ = CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_BLEND_COLOR_READ;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * writing its texture blend color component
+ * information.
+ */
+ public static final int
+ ALLOW_BLEND_COLOR_WRITE = CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_BLEND_COLOR_WRITE;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * reading its texture transform component
+ * information.
+ */
+ public static final int
+ ALLOW_TRANSFORM_READ = CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_TRANSFORM_READ;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * writing its texture transform component
+ * information.
+ */
+ public static final int
+ ALLOW_TRANSFORM_WRITE = CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_TRANSFORM_WRITE;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * reading its texture color table component
+ * information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_COLOR_TABLE_READ =
+ CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_COLOR_TABLE_READ;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * writing its texture color table component
+ * information.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_COLOR_TABLE_WRITE =
+ CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_COLOR_TABLE_WRITE;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * reading its texture combine mode information. (e.g. combine mode,
+ * combine color source, combine color function, combine scale factor)
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_COMBINE_READ =
+ CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_COMBINE_READ;
+
+ /**
+ * Specifies that this TextureAttributes object allows
+ * writing its texture combine mode information. (e.g. combine mode,
+ * combine color source, combine color function, combine scale factor)
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int ALLOW_COMBINE_WRITE =
+ CapabilityBits.TEXTURE_ATTRIBUTES_ALLOW_COMBINE_WRITE;
+
+
+ /**
+ * Use the fastest available method for perspective correction.
+ * @see #setPerspectiveCorrectionMode
+ */
+ public static final int FASTEST = 0;
+
+ /**
+ * Use the nicest (highest quality) available method for texture
+ * mapping perspective correction.
+ * @see #setPerspectiveCorrectionMode
+ */
+ public static final int NICEST = 1;
+
+ /**
+ * Modulate the object color with the texture color.
+ * @see #setTextureMode
+ */
+ public static final int MODULATE = 2;
+
+ /**
+ * Apply the texture color to the object as a decal.
+ * @see #setTextureMode
+ */
+ public static final int DECAL = 3;
+
+ /**
+ * Blend the texture blend color with the object color.
+ * @see #setTextureMode
+ */
+ public static final int BLEND = 4;
+
+ /**
+ * Replace the object color with the texture color.
+ * @see #setTextureMode
+ */
+ public static final int REPLACE = 5;
+
+ /**
+ * Combine the object color with texture color as specified in
+ * the combine mode.
+ *
+ * @see #setTextureMode
+ * @since Java 3D 1.3
+ */
+ public static final int COMBINE = 6;
+
+
+ /**
+ * Replace the input color with the specified color.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbMode
+ * @see #setCombineAlphaMode
+ */
+ public static final int COMBINE_REPLACE = 0;
+
+ /**
+ * Modulates one color with another color.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbMode
+ * @see #setCombineAlphaMode
+ */
+ public static final int COMBINE_MODULATE = 1;
+
+ /**
+ * Add two colors.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbMode
+ * @see #setCombineAlphaMode
+ */
+ public static final int COMBINE_ADD = 2;
+
+ /**
+ * Add two colors plus an implicit offset.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbMode
+ * @see #setCombineAlphaMode
+ */
+ public static final int COMBINE_ADD_SIGNED = 3;
+
+ /**
+ * Subtract one color from another color.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbMode
+ * @see #setCombineAlphaMode
+ */
+ public static final int COMBINE_SUBTRACT = 4;
+
+ /**
+ * Interpolate two colors with a factor.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbMode
+ * @see #setCombineAlphaMode
+ */
+ public static final int COMBINE_INTERPOLATE = 5;
+
+ /**
+ * Dot product of two colors.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbMode
+ * @see #setCombineAlphaMode
+ */
+ public static final int COMBINE_DOT3 = 6;
+
+
+ /**
+ * Object color coming into the texturing state.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbSource
+ * @see #setCombineAlphaSource
+ */
+ public static final int COMBINE_OBJECT_COLOR = 0;
+
+ /**
+ * Texture color of the corresponding texture unit state.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbSource
+ * @see #setCombineAlphaSource
+ */
+ public static final int COMBINE_TEXTURE_COLOR = 1;
+
+ /**
+ * Texture blend color.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbSource
+ * @see #setCombineAlphaSource
+ */
+ public static final int COMBINE_CONSTANT_COLOR = 2;
+
+ /**
+ * Color from the previous texture unit state.
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbSource
+ * @see #setCombineAlphaSource
+ */
+ public static final int COMBINE_PREVIOUS_TEXTURE_UNIT_STATE = 3;
+
+ /**
+ * Color function is f = C<sub>rgb</sub>
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbFunction
+ */
+ public static final int COMBINE_SRC_COLOR = 0;
+
+ /**
+ * Color function is f = (1 - C<sub>rgb</sub>)
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbFunction
+ */
+ public static final int COMBINE_ONE_MINUS_SRC_COLOR = 1;
+
+ /**
+ * Color function is f = C<sub>a</sub>
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbFunction
+ * @see #setCombineAlphaFunction
+ */
+ public static final int COMBINE_SRC_ALPHA = 2;
+
+ /**
+ * Color function is f = (1 - C<sub>a</sub>)
+ *
+ * @since Java 3D 1.3
+ * @see #setCombineRgbFunction
+ * @see #setCombineAlphaFunction
+ */
+ public static final int COMBINE_ONE_MINUS_SRC_ALPHA = 3;
+
+ /**
+ * Constructs a TextureAttributes object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * texture mode : REPLACE<br>
+ * blend color : black (0,0,0,0)<br>
+ * transform : identity<br>
+ * perspective correction mode : NICEST<br>
+ * texture color table : null<br>
+ * combine rgb mode : COMBINE_MODULATE<br>
+ * combine alpha mode : COMBINE_MODULATE<br>
+ * combine rgb source :
+ * <ul>
+ * C<sub>0</sub>=COMBINE_TEXTURE_COLOR<br>
+ * C<sub>1</sub>=COMBINE_PREVIOUS_TEXTURE_UNIT_STATE<br>
+ * C<sub>2</sub>=COMBINE_CONSTANT_COLOR<br>
+ * </ul>
+ * combine alpha source :
+ * <ul>
+ * C<sub>0</sub>=COMBINE_TEXTURE_COLOR<br>
+ * C<sub>1</sub>=COMBINE_PREVIOUS_TEXTURE_UNIT_STATE<br>
+ * C<sub>2</sub>=COMBINE_CONSTANT_COLOR<br>
+ * </ul>
+ * combine rgb function : COMBINE_SRC_COLOR<br>
+ * combine alpha function : COMBINE_SRC_ALPHA<br>
+ * combine rgb scale : 1<br>
+ * combine alpha scale : 1<br>
+ * </ul>
+ */
+ public TextureAttributes() {
+ }
+
+ /**
+ * Constructs a TextureAttributes object with the specified values.
+ * @param textureMode the texture mode; one of <code>MODULATE</code>,
+ * <code>DECAL</code>, <code>BLEND</code>, <code>REPLACE</code>, or
+ * <code>COMBINE</code>
+ * @param transform the transform object, used to transform texture
+ * coordinates
+ * @param textureBlendColor the texture constant color
+ * @param perspCorrectionMode the perspective correction mode to
+ * be used for color and/or texture coordinate interpolation;
+ * one of <code>NICEST</code> or <code>FASTEST</code>
+ * @exception IllegalArgumentException if <code>textureMode</code>
+ * is a value other than <code>MODULATE</code>,
+ * <code>DECAL</code>, <code>BLEND</code>, <code>REPLACE</code>, or
+ * <code>COMBINE</code>
+ * @exception IllegalArgumentException if mode value is other
+ * than <code>FASTEST</code> or <code>NICEST</code>.
+ */
+ public TextureAttributes(int textureMode, Transform3D transform,
+ Color4f textureBlendColor,
+ int perspCorrectionMode) {
+
+ if ((textureMode < MODULATE) || (textureMode > COMBINE)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes10"));
+ }
+
+ if ((perspCorrectionMode != FASTEST) &&
+ (perspCorrectionMode!= NICEST)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes9"));
+ }
+
+ ((TextureAttributesRetained)this.retained).initTextureMode(textureMode);
+ ((TextureAttributesRetained)this.retained).initTextureBlendColor(textureBlendColor);
+ ((TextureAttributesRetained)this.retained).initTextureTransform(transform);
+ ((TextureAttributesRetained)this.retained).initPerspectiveCorrectionMode(perspCorrectionMode);
+ }
+
+ /**
+ * Sets the texture mode parameter for this
+ * appearance component object.
+ * @param textureMode the texture mode, one of: <code>MODULATE</code>,
+ * <code>DECAL</code>, <code>BLEND</code>, <code>REPLACE</code>, or
+ * <code>COMBINE</code>
+ * @exception IllegalArgumentException if <code>textureMode</code>
+ * is a value other than <code>MODULATE</code>,
+ * <code>DECAL</code>, <code>BLEND</code>, <code>REPLACE</code>, or
+ * <code>COMBINE</code>
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public void setTextureMode(int textureMode) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes0"));
+
+ if ((textureMode < MODULATE) || (textureMode > COMBINE)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes10"));
+ }
+
+ if (isLive())
+ ((TextureAttributesRetained)this.retained).setTextureMode(textureMode);
+ else
+ ((TextureAttributesRetained)this.retained).initTextureMode(textureMode);
+ }
+
+ /**
+ * Gets the texture mode parameter for this
+ * texture attributes object.
+ * @return textureMode the texture mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getTextureMode() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes1"));
+
+ return ((TextureAttributesRetained)this.retained).getTextureMode();
+ }
+
+ /**
+ * Sets the texture constant color for this
+ * texture attributes object.
+ * @param textureBlendColor the texture constant color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setTextureBlendColor(Color4f textureBlendColor) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_BLEND_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes2"));
+
+ if (isLive())
+ ((TextureAttributesRetained)this.retained).setTextureBlendColor(textureBlendColor);
+ else
+ ((TextureAttributesRetained)this.retained).initTextureBlendColor(textureBlendColor);
+ }
+
+ /**
+ * Sets the texture blend color for this
+ * appearance component object.
+ * @param r the red component of the color
+ * @param g the green component of the color
+ * @param b the blue component of the color
+ * @param a the alpha component of the color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setTextureBlendColor(float r, float g, float b, float a) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_BLEND_COLOR_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes3"));
+
+ if (isLive())
+ ((TextureAttributesRetained)this.retained).setTextureBlendColor(r, g, b, a);
+ else
+ ((TextureAttributesRetained)this.retained).initTextureBlendColor(r, g, b, a);
+ }
+
+ /**
+ * Gets the texture blend color for this
+ * appearance component object.
+ * @param textureBlendColor the vector that will receive the texture
+ * constant color
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getTextureBlendColor(Color4f textureBlendColor) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_BLEND_COLOR_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes4"));
+
+ ((TextureAttributesRetained)this.retained).getTextureBlendColor(textureBlendColor);
+ }
+
+ /**
+ * Sets the texture transform object used to transform texture
+ * coordinates. A copy of the specified Transform3D object is
+ * stored in this TextureAttributes object.
+ * @param transform the new transform object
+ * @exception CapabilityNotSetException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void setTextureTransform(Transform3D transform) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TRANSFORM_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes5"));
+
+ if (isLive())
+ ((TextureAttributesRetained)this.retained).setTextureTransform(transform);
+ else
+ ((TextureAttributesRetained)this.retained).initTextureTransform(transform);
+ }
+
+ /**
+ * Retrieves a copy of the texture transform object.
+ * @param transform the transform object that will receive the
+ * current texture transform
+ * @exception CapabilityNotSetException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ public void getTextureTransform(Transform3D transform) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_TRANSFORM_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes6"));
+
+ ((TextureAttributesRetained)this.retained).getTextureTransform(transform);
+ }
+
+ /**
+ * Sets perspective correction mode to be used for color
+ * and/or texture coordinate interpolation.
+ * A value of <code>NICEST</code> indicates that perspective correction should be
+ * performed and that the highest quality method should be used.
+ * A value of <code>FASTEST</code> indicates that the most efficient perspective
+ * correction method should be used.
+ * @param mode one of <code>NICEST</code> or <code>FASTEST</code>
+ * The default value is <code>NICEST</code>.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if mode value is other
+ * than <code>FASTEST</code> or <code>NICEST</code>.
+ */
+ public void setPerspectiveCorrectionMode(int mode) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes7"));
+
+ if ((mode != FASTEST) && (mode!= NICEST))
+ throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes9"));
+
+ if (isLive())
+ ((TextureAttributesRetained)this.retained).setPerspectiveCorrectionMode(mode);
+ else
+ ((TextureAttributesRetained)this.retained).initPerspectiveCorrectionMode(mode);
+ }
+
+ /**
+ * Gets perspective correction mode value.
+ * @return mode the value of perspective correction mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getPerspectiveCorrectionMode() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes8"));
+ return ((TextureAttributesRetained)this.retained).getPerspectiveCorrectionMode();
+ }
+
+ /**
+ * Sets the texture color table from the specified table. The
+ * individual integer array elements are copied. The array is
+ * indexed first by color component (<i>r</i>, <i>g</i>, <i>b</i>,
+ * and <i>a</i>, respectively) and then by color value;
+ * <code>table.length</code> defines the number of color
+ * components and <code>table[0].length</code> defines the texture
+ * color table size. If the table is non-null, the number of
+ * color components must either be 3, for <i>rgb</i> data, or 4,
+ * for <i>rgba</i> data. The size of each array for each color
+ * component must be the same and must be a power of 2. If table
+ * is null or if the texture color table size is 0, the texture
+ * color table is disabled. If the texture color table size is
+ * greater than the device-dependent maximum texture color table
+ * size for a particular Canvas3D, the texture color table is
+ * ignored for that canvas.
+ *
+ * <p>
+ * When enabled, the texture color table is applied after the
+ * texture filtering operation and before texture application.
+ * Each of the <i>r</i>, <i>g</i>, <i>b</i>, and <i>a</i>
+ * components are clamped to the range [0,1], multiplied by
+ * <code>textureColorTableSize-1</code>, and rounded to the
+ * nearest integer. The resulting value for each component is
+ * then used as an index into the respective table for that
+ * component. If the texture color table contains 3 components,
+ * alpha is passed through unmodified.
+ *
+ * @param table the new texture color table
+ *
+ * @exception IllegalArgumentException if <code>table.length</code>
+ * is not 3 or 4, or if the arrays for each component are not all
+ * the same length, or if the texture color table size
+ * is not a power of 2
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.2
+ */
+ public void setTextureColorTable(int[][] table) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLOR_TABLE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes11"));
+
+ if (isLive())
+ ((TextureAttributesRetained)this.retained).setTextureColorTable(table);
+ else
+ ((TextureAttributesRetained)this.retained).initTextureColorTable(table);
+ }
+
+ /**
+ * Retrieves the texture color table and copies it into the
+ * specified array. If the current texture color table is null,
+ * no values are copied.
+ *
+ * @param table the array that will receive a copy of the
+ * texture color table from this TextureAttributes object.
+ * The array must be allocated by the caller and must be large
+ * enough to hold the entire table (that is,
+ * <code>int[numTextureColorTableComponents][textureColorTableSize]</code>).
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public void getTextureColorTable(int[][] table) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_COLOR_TABLE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureAttributes12"));
+ ((TextureAttributesRetained)this.retained).getTextureColorTable(table);
+ return;
+ }
+
+ /**
+ * Retrieves the number of color components in the current texture
+ * color table. A value of 0 is returned if the texture color
+ * table is null.
+ *
+ * @return the number of color components in the texture color
+ * table, or 0 if the table is null
+ *
+ * @since Java 3D 1.2
+ */
+ public int getNumTextureColorTableComponents() {
+ return (((TextureAttributesRetained)this.retained).getNumTextureColorTableComponents());
+ }
+
+ /**
+ * Retrieves the size of the current texture color table. A value
+ * of 0 is returned if the texture color table is null.
+ *
+ * @return the size of the texture color table, or 0 if the table
+ * is null
+ *
+ * @since Java 3D 1.2
+ */
+ public int getTextureColorTableSize() {
+ return (((TextureAttributesRetained)this.retained).getTextureColorTableSize());
+ }
+
+
+ /**
+ * Sets the combine mode for the rgb components of the output color
+ * for this object.
+ *
+ * @param combineMode the combine mode, one of:
+ * <code>COMBINE_REPLACE</code>,
+ * <code>COMBINE_MODULATE</code>, <code>COMBINE_ADD</code>,
+ * <code>COMBINE_ADD_SIGNED</code>, <code>COMBINE_SUBTRACT</code>,
+ * <code>COMBINE_INTERPOLATE</code>, or <code>COMBINE_DOT3</code>
+ *
+ * @exception IllegalArgumentException if <code>combineMode</code>
+ * is a value other than <code>COMBINE_REPLACE</code>,
+ * <code>COMBINE_MODULATE</code>, <code>COMBINE_ADD</code>,
+ * <code>COMBINE_ADD_SIGNED</code>, <code>COMBINE_SUBTRACT</code>,
+ * <code>COMBINE_INTERPOLATE</code>, or <code>COMBINE_DOT3</code>
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineRgbMode(int combineMode) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes16"));
+ }
+ }
+
+ if ((combineMode < COMBINE_REPLACE) || (combineMode > COMBINE_DOT3)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes20"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineRgbMode(combineMode);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineRgbMode(combineMode);
+ }
+ }
+
+ /**
+ * Sets the combine mode for the alpha component of the output color
+ * for this object.
+ *
+ * @param combineMode the combine mode, one of:
+ * <code>COMBINE_REPLACE</code>,
+ * <code>COMBINE_MODULATE</code>, <code>COMBINE_ADD</code>,
+ * <code>COMBINE_ADD_SIGNED</code>, <code>COMBINE_SUBTRACT</code>,
+ * <code>COMBINE_INTERPOLATE</code>, or <code>COMBINE_DOT3</code>
+ *
+ * @exception IllegalArgumentException if <code>combineMode</code>
+ * is a value other than <code>COMBINE_REPLACE</code>,
+ * <code>COMBINE_MODULATE</code>, <code>COMBINE_ADD</code>,
+ * <code>COMBINE_ADD_SIGNED</code>, <code>COMBINE_SUBTRACT</code>,
+ * <code>COMBINE_INTERPOLATE</code>, or <code>COMBINE_DOT3</code>
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineAlphaMode(int combineMode) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes18"));
+ }
+ }
+
+ if ((combineMode < COMBINE_REPLACE) || (combineMode > COMBINE_DOT3)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes20"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineAlphaMode(combineMode);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineAlphaMode(combineMode);
+ }
+ }
+
+ /**
+ * Retrieves the combine mode for the rgb components of the output color
+ * for this object.
+ * @return the combine mode for the rgb components.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineRgbMode() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes17"));
+ }
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineRgbMode();
+ }
+
+ /**
+ * Retrieves the combine mode for the alpha component of the output color
+ * for this object.
+ * @return the combine mode for the alpha component.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineAlphaMode() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes19"));
+ }
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineAlphaMode();
+ }
+
+ /**
+ * Sets the source for the rgb components of the specified color operand
+ * for this object.
+ *
+ * @param index color operand in the combine operation
+ * @param src the color source, one of: <code>COMBINE_OBJECT_COLOR</code>,
+ * <code>COMBINE_TEXTURE_COLOR</code>,
+ * <code>COMBINE_CONSTANT_COLOR</code>, or
+ * <code>COMBINE_PREVIOUS_TEXTURE_UNIT_STATE</code>
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception IllegalArgumentException if <code>src</code>
+ * is a value other than <code>COMBINE_OBJECT_COLOR</code>,
+ * <code>COMBINE_TEXTURE_COLOR</code>,
+ * <code>COMBINE_CONSTANT_COLOR</code>, or
+ * <code>COMBINE_PREVIOUS_TEXTURE_UNIT_STATE</code>
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineRgbSource(int index, int src) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes21"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ if ((src < COMBINE_OBJECT_COLOR) ||
+ (src > COMBINE_PREVIOUS_TEXTURE_UNIT_STATE)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes26"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineRgbSource(
+ index, src);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineRgbSource(
+ index, src);
+ }
+ }
+
+ /**
+ * Sets the source for the alpha component of the specified color operand
+ * for this object.
+ *
+ * @param index color operand in the combine operation
+ * @param src the color source, one of: <code>COMBINE_OBJECT_COLOR</code>,
+ * <code>COMBINE_TEXTURE_COLOR</code>,
+ * <code>COMBINE_CONSTANT_COLOR</code>, or
+ * <code>COMBINE_PREVIOUS_TEXTURE_UNIT_STATE</code>
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception IllegalArgumentException if <code>src</code>
+ * is a value other than <code>COMBINE_OBJECT_COLOR</code>,
+ * <code>COMBINE_TEXTURE_COLOR</code>,
+ * <code>COMBINE_CONSTANT_COLOR</code>, or
+ * <code>COMBINE_PREVIOUS_TEXTURE_UNIT_STATE</code>
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineAlphaSource(int index, int src) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes23"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ if ((src < COMBINE_OBJECT_COLOR) ||
+ (src > COMBINE_PREVIOUS_TEXTURE_UNIT_STATE)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes26"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineAlphaSource(
+ index, src);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineAlphaSource(
+ index, src);
+ }
+ }
+
+ /**
+ * Retrieves the source for the rgb components of the specified
+ * color operand for this object.
+ *
+ * @param index color operand in the combine operation
+ *
+ * @return the source for the rgb components of the specified color
+ * operand for this object
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineRgbSource(int index) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes22"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineRgbSource(index);
+ }
+
+ /**
+ * Retrieves the source for the alpha component of the specified
+ * color operand for this object.
+ *
+ * @param index color operand in the combine operation
+ *
+ * @return the source for the alpha component of the specified color
+ * operand for this object
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineAlphaSource(int index) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes24"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineAlphaSource(index);
+ }
+
+ /**
+ * Sets the function for the rgb components of the specified color operand
+ * for this object.
+ *
+ * @param index color operand in the combine operation
+ * @param function the color function, one of:
+ * <code>COMBINE_SRC_COLOR</code>,
+ * <code>COMBINE_ONE_MINUS_SRC_COLOR</code>,
+ * <code>COMBINE_SRC_ALPHA</code>, or
+ * <code>COMBINE_ONE_MINUS_SRC_ALPHA</code>
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception IllegalArgumentException if <code>function</code>
+ * is a value other than <code>COMBINE_SRC_COLOR</code>,
+ * <code>COMBINE_ONE_MINUS_SRC_COLOR</code>,
+ * <code>COMBINE_SRC_ALPHA</code>, or
+ * <code>COMBINE_ONE_MINUS_SRC_ALPHA</code>
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineRgbFunction(int index, int function) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes27"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ if ((function < COMBINE_SRC_COLOR) ||
+ (function > COMBINE_ONE_MINUS_SRC_ALPHA)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes31"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineRgbFunction(
+ index, function);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineRgbFunction(
+ index, function);
+ }
+ }
+
+ /**
+ * Sets the function for the alpha component of the specified color operand
+ * for this object.
+ *
+ * @param index color operand in the combine operation
+ * @param function the color function, one of:
+ * <code>COMBINE_SRC_ALPHA</code>, or
+ * <code>COMBINE_ONE_MINUS_SRC_ALPHA</code>
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception IllegalArgumentException if <code>function</code>
+ * is a value other than
+ * <code>COMBINE_SRC_ALPHA</code> or
+ * <code>COMBINE_ONE_MINUS_SRC_ALPHA</code>
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineAlphaFunction(int index, int function) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes29"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ if ((function < COMBINE_SRC_ALPHA) ||
+ (function > COMBINE_ONE_MINUS_SRC_ALPHA)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes31"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineAlphaFunction(
+ index, function);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineAlphaFunction(
+ index, function);
+ }
+ }
+
+ /**
+ * Retrieves the function for the rgb components of the specified color
+ * operand for this object.
+ *
+ * @param index color operand in the combine operation
+ *
+ * @return the function for the rgb components of the specified color
+ * operand for this object.
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineRgbFunction(int index) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes28"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineRgbFunction(index);
+ }
+
+ /**
+ * Retrieves the function for the alpha component of the specified color
+ * operand for this object.
+ *
+ * @param index color operand in the combine operation
+ *
+ * @return the function for the alpha component of the specified color
+ * operand for this object.
+ *
+ * @exception IndexOutOfBoundsException if <code>index</code> < 0 or
+ * <code>index</code> > 2
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineAlphaFunction(int index) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes30"));
+ }
+ }
+
+ if ((index < 0) || (index > 2)) {
+ throw new IndexOutOfBoundsException(
+ J3dI18N.getString("TextureAttributes25"));
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineAlphaFunction(index);
+ }
+
+ /**
+ * Sets the scale factor for the rgb components of the output color
+ * for this object.
+ *
+ * @param scale the scale factor for the rgb components of the output
+ * color. It must be one of the following: 1, 2, or 4.
+ *
+ * @exception IllegalArgumentException if <code>scale</code> is a
+ * value other than 1, 2, or 4.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineRgbScale(int scale) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes32"));
+ }
+ }
+
+
+ if ((scale != 1) && (scale != 2) && (scale != 4)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes36"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineRgbScale(scale);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineRgbScale(scale);
+ }
+ }
+
+ /**
+ * Sets the scale factor for the alpha component of the output color
+ * for this object.
+ *
+ * @param scale the scale factor for the alpha component of the output
+ * color. It must be one of the following: 1, 2, or 4.
+ *
+ * @exception IllegalArgumentException if <code>scale</code> is a
+ * value other than 1, 2, or 4.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @see Canvas3D#queryProperties
+ *
+ * @since Java 3D 1.3
+ */
+ public void setCombineAlphaScale(int scale) {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_WRITE)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes34"));
+ }
+ }
+
+ if ((scale != 1) && (scale != 2) && (scale != 4)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureAttributes36"));
+ }
+
+ if (isLive()) {
+ ((TextureAttributesRetained)this.retained).setCombineAlphaScale(scale);
+ } else {
+ ((TextureAttributesRetained)this.retained).initCombineAlphaScale(scale);
+ }
+ }
+
+ /**
+ * Retrieves the scale factor for the rgb components of the output color
+ * for this object.
+ *
+ * @return the scale factor for the rgb components of the output color
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineRgbScale() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes33"));
+ }
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineRgbScale();
+ }
+
+ /**
+ * Retrieves the scale factor for the alpha component of the output color
+ * for this object.
+ *
+ * @return the scale factor for the alpha component of the output color
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int getCombineAlphaScale() {
+ if (isLiveOrCompiled()) {
+ if (!this.getCapability(ALLOW_COMBINE_READ)) {
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureAttributes35"));
+ }
+ }
+
+ return ((TextureAttributesRetained)this.retained).getCombineAlphaScale();
+ }
+
+
+ /**
+ * Creates a retained mode TextureAttributesRetained object that this
+ * TextureAttributes component object will point to.
+ */
+ void createRetained() {
+ this.retained = new TextureAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ TextureAttributes ta = new TextureAttributes();
+ ta.duplicateNodeComponent(this);
+ return ta;
+ }
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ TextureAttributesRetained attr =
+ (TextureAttributesRetained) originalNodeComponent.retained;
+ TextureAttributesRetained rt = (TextureAttributesRetained) retained;
+
+ Color4f c = new Color4f();
+ attr.getTextureBlendColor(c);
+ Transform3D t = new Transform3D();
+ attr.getTextureTransform(t);
+
+ rt.initTextureMode(attr.getTextureMode());
+ rt.initPerspectiveCorrectionMode(attr.getPerspectiveCorrectionMode());
+ rt.initTextureBlendColor(c);
+ rt.initTextureTransform(t);
+
+ if ((attr.getNumTextureColorTableComponents() != 0) &&
+ (attr.getTextureColorTableSize() != 0)) {
+ int table[][] = new
+ int[attr.getNumTextureColorTableComponents()][attr.getTextureColorTableSize()];
+ attr.getTextureColorTable(table);
+ rt.initTextureColorTable(table);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TextureAttributesRetained.java b/src/classes/share/javax/media/j3d/TextureAttributesRetained.java
new file mode 100644
index 0000000..46066af
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureAttributesRetained.java
@@ -0,0 +1,1015 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color4f;
+import java.util.ArrayList;
+
+/**
+ * The TextureAttributes object defines attributes that apply to
+ * to texture mapping.
+ */
+class TextureAttributesRetained extends NodeComponentRetained {
+
+ // A list of pre-defined bits to indicate which component
+ // in this TextureAttributes object changed.
+ static final int TRANSFORM_CHANGED = 0x0001;
+ static final int MODE_CHANGED = 0x0002;
+ static final int COLOR_CHANGED = 0x0004;
+ static final int CORRECTION_CHANGED = 0x0008;
+ static final int TEXTURE_COLOR_TABLE_CHANGED = 0x0010;
+ static final int COMBINE_RGB_MODE_CHANGED = 0x0020;
+ static final int COMBINE_ALPHA_MODE_CHANGED = 0x0040;
+ static final int COMBINE_RGB_SRC_CHANGED = 0x0080;
+ static final int COMBINE_ALPHA_SRC_CHANGED = 0x0100;
+ static final int COMBINE_RGB_FCN_CHANGED = 0x0200;
+ static final int COMBINE_ALPHA_FCN_CHANGED = 0x0400;
+ static final int COMBINE_RGB_SCALE_CHANGED = 0x0800;
+ static final int COMBINE_ALPHA_SCALE_CHANGED = 0x1000;
+
+ // static class variable for commands used in messages
+ static Integer commandInt[] = null;
+
+ // static class variable for enums. Currently only supports 0 - 9.
+ static Integer enums[] = null;
+
+ // Texture transform
+ Transform3D transform = new Transform3D();
+
+ // Texture mode
+ int textureMode = TextureAttributes.REPLACE;
+
+ // Texture blend color
+ Color4f textureBlendColor = new Color4f(0.0f, 0.0f, 0.0f, 0.0f);
+
+ // Texture color table
+ int textureColorTable[] = null;
+ int numTextureColorTableComponents = 0;
+ int textureColorTableSize = 0;
+
+ // Texture Combine Mode
+
+ int combineRgbMode = TextureAttributes.COMBINE_MODULATE;
+ int combineAlphaMode = TextureAttributes.COMBINE_MODULATE;
+
+ // the following fields are only applicable if textureMode specifies
+ // COMBINE. If COMBINE mode is specified, then each of the following
+ // fields will be referencing an array of 3 integers, each representing
+ // an operand in the combine equation.
+ int [] combineRgbSrc = null;
+ int [] combineAlphaSrc = null;
+ int [] combineRgbFcn = null;
+ int [] combineAlphaFcn = null;
+
+ int combineRgbScale = 1;
+ int combineAlphaScale = 1;
+
+ //Perspective correction mode, used for color/texCoord interpolation
+ int perspCorrectionMode = TextureAttributes.NICEST;
+
+ // true when mirror texCoord component set
+ boolean mirrorCompDirty = false;
+
+ static final void initTextureEnums() {
+ // create some of the enums Integer to be used in the messages
+ // this can be eliminated if the message is modified to take
+ // integer itself
+ //
+ // NOTE: check with the actual enum value before using this
+ // list. This list only supports 0 - 9
+ if (enums == null) {
+ enums = new Integer[10];
+ for (int i = 0; i < 10; i++) {
+ enums[i] = new Integer(i);
+ }
+ }
+ }
+
+
+ TextureAttributesRetained() {
+ initTextureEnums();
+ }
+
+ // initCombineMode -- initializes the combine mode related fields
+ // delay the allocation of memory to minimize
+ // memory footprint
+
+ final void initCombineMode(TextureAttributesRetained tr) {
+ tr.combineRgbSrc = new int[3];
+ tr.combineAlphaSrc = new int[3];
+ tr.combineRgbFcn = new int[3];
+ tr.combineAlphaFcn = new int[3];
+
+ //default values
+
+ tr.combineRgbSrc[0] = TextureAttributes.COMBINE_TEXTURE_COLOR;
+ tr.combineRgbSrc[1] = TextureAttributes.COMBINE_PREVIOUS_TEXTURE_UNIT_STATE;
+ tr.combineRgbSrc[2] = TextureAttributes.COMBINE_CONSTANT_COLOR;
+
+ tr.combineAlphaSrc[0] = TextureAttributes.COMBINE_TEXTURE_COLOR;
+ tr.combineAlphaSrc[1] = TextureAttributes.COMBINE_PREVIOUS_TEXTURE_UNIT_STATE;
+ tr.combineAlphaSrc[2] = TextureAttributes.COMBINE_CONSTANT_COLOR;
+
+ tr.combineRgbFcn[0] = TextureAttributes.COMBINE_SRC_COLOR;
+ tr.combineRgbFcn[1] = TextureAttributes.COMBINE_SRC_COLOR;
+ tr.combineRgbFcn[2] = TextureAttributes.COMBINE_SRC_COLOR;
+
+ tr.combineAlphaFcn[0] = TextureAttributes.COMBINE_SRC_ALPHA;
+ tr.combineAlphaFcn[1] = TextureAttributes.COMBINE_SRC_ALPHA;
+ tr.combineAlphaFcn[2] = TextureAttributes.COMBINE_SRC_ALPHA;
+ }
+
+ final void initTextureMode(int textureMode) {
+ this.textureMode = textureMode;
+
+ if (textureMode == TextureAttributes.COMBINE) {
+ if (combineRgbSrc == null) {
+ initCombineMode(this);
+ }
+ }
+ }
+
+ /**
+ * Sets the texture mode parameter for this
+ * appearance component object.
+ * @param textureMode the texture mode, one of: MODULATE,
+ * DECAL, BLEND, or REPLACE
+ */
+ final void setTextureMode(int textureMode) {
+ initTextureMode(textureMode);
+ sendMessage(MODE_CHANGED, enums[textureMode], null);
+ }
+
+ /**
+ * Gets the texture mode parameter for this
+ * texture attributes object.
+ * @return textureMode the texture mode
+ */
+ final int getTextureMode() {
+ return textureMode;
+ }
+
+ final void initTextureBlendColor(Color4f textureBlendColor) {
+ this.textureBlendColor.set(textureBlendColor);
+
+ }
+
+ /**
+ * Sets the texture blend color for this
+ * texture attributes object.
+ * @param textureBlendColor the texture blend color used when
+ * the mode is BLEND
+ */
+ final void setTextureBlendColor(Color4f textureBlendColor) {
+ this.textureBlendColor.set(textureBlendColor);
+ sendMessage(COLOR_CHANGED, new Color4f(textureBlendColor), null);
+ }
+
+
+ final void initTextureBlendColor(float r, float g, float b, float a) {
+ this.textureBlendColor.set(r, g, b, a);
+ }
+
+
+ /**
+ * Sets the texture blend color for this
+ * appearance component object. This color is used when
+ * the mode is BLEND.
+ * @param r the red component of the color
+ * @param g the green component of the color
+ * @param b the blue component of the color
+ * @param a the alpha component of the color
+ */
+ final void setTextureBlendColor(float r, float g, float b, float a) {
+ this.textureBlendColor.set(r, g, b, a);
+ sendMessage(COLOR_CHANGED, new Color4f(r, g, b, a), null);
+ }
+
+
+ /**
+ * Gets the texture blend color for this
+ * appearance component object.
+ * @param textureBlendColor the vector that will receive the texture
+ * blend color used when the mode is BLEND
+ */
+ final void getTextureBlendColor(Color4f textureBlendColor) {
+ textureBlendColor.set(this.textureBlendColor);
+ }
+
+
+ final void initTextureTransform(Transform3D transform) {
+ this.transform.set(transform);
+ }
+
+
+ /**
+ * Sets the texture transform object used to transform texture
+ * coordinates. A copy of the specified Transform3D object is
+ * stored in this TextureAttributes object.
+ * @param transform the new transform object
+ */
+ final void setTextureTransform(Transform3D transform) {
+ this.transform.set(transform);
+ sendMessage(TRANSFORM_CHANGED,
+ VirtualUniverse.mc.getTransform3D(transform), null);
+ }
+
+
+ /**
+ * Retrieves a copy of the texture transform object.
+ * @param transform the transform object that will receive the
+ * current texture transform.
+ */
+ final void getTextureTransform(Transform3D transform) {
+ transform.set(this.transform);
+ }
+
+
+ final void initPerspectiveCorrectionMode(int mode) {
+ this.perspCorrectionMode = mode;
+ }
+
+ /**
+ * Sets perspective correction mode to be used for color
+ * and/or texture coordinate interpolation.
+ * A value of NICEST indicates that perspective correction should be
+ * performed and that the highest quality method should be used.
+ * A value of FASTEST indicates that the most efficient perspective
+ * correction method should be used.
+ * @param mode one of NICEST or FASTEST.
+ * The default value is NICEST.
+ */
+ final void setPerspectiveCorrectionMode(int mode) {
+ this.perspCorrectionMode = mode;
+ sendMessage(CORRECTION_CHANGED, enums[mode], null);
+ }
+
+ /**
+ * Gets perspective correction mode value.
+ * @return mode the value of perspective correction mode.
+ */
+ final int getPerspectiveCorrectionMode() {
+ return perspCorrectionMode;
+ }
+
+ final void setTextureColorTable(int[][] table) {
+ initTextureColorTable(table);
+
+ //clone a copy of the texture for the mirror object
+ if (table == null) {
+ sendMessage(TEXTURE_COLOR_TABLE_CHANGED, null, null);
+ } else {
+ int ctable[] = new int[textureColorTableSize *
+ numTextureColorTableComponents];
+ System.arraycopy(textureColorTable, 0, ctable, 0,
+ textureColorTable.length);
+ Object args[] = new Object[3];
+
+ args[0] = new Integer(numTextureColorTableComponents);
+ args[1] = new Integer(textureColorTableSize);
+ args[2] = ctable;
+ sendMessage(TEXTURE_COLOR_TABLE_CHANGED, args, null);
+ }
+ }
+
+ final void initTextureColorTable(int[][] table) {
+
+ numTextureColorTableComponents = 0;
+ textureColorTableSize = 0;
+
+ if (table == null) {
+ textureColorTable = null;
+ return;
+ }
+
+ if (table.length < 3 || table.length > 4) {
+ throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes13"));
+ }
+
+ if (Texture.getPowerOf2(table[0].length) == -1) {
+ throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes14"));
+ }
+
+ for (int i = 1; i < table.length; i++) {
+ if (table[i].length != table[0].length)
+ throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes15"));
+ }
+
+ numTextureColorTableComponents = table.length;
+ textureColorTableSize = table[0].length;
+
+ if (textureColorTable == null ||
+ textureColorTable.length != numTextureColorTableComponents *
+ textureColorTableSize) {
+ textureColorTable = new int[numTextureColorTableComponents *
+ textureColorTableSize];
+ }
+
+ int k = 0;
+ for (int i = 0; i < textureColorTableSize; i++) {
+ for (int j = 0; j < numTextureColorTableComponents; j++) {
+ textureColorTable[k++] = table[j][i];
+ }
+ }
+ }
+
+
+ final void getTextureColorTable(int[][] table) {
+
+ if (textureColorTable == null)
+ return;
+
+ int k = 0;
+ for (int i = 0; i < textureColorTableSize; i++) {
+ for (int j = 0; j < numTextureColorTableComponents; j++) {
+ table[j][i] = textureColorTable[k++];
+ }
+ }
+ }
+
+ final int getNumTextureColorTableComponents() {
+ return numTextureColorTableComponents;
+ }
+
+ final int getTextureColorTableSize() {
+ return textureColorTableSize;
+ }
+
+
+ final void initCombineRgbMode(int mode) {
+ combineRgbMode = mode;
+ }
+
+ final void setCombineRgbMode(int mode) {
+ initCombineRgbMode(mode);
+ sendMessage(COMBINE_RGB_MODE_CHANGED, enums[mode], null);
+ }
+
+ final int getCombineRgbMode() {
+ return combineRgbMode;
+ }
+
+ final void initCombineAlphaMode(int mode) {
+ combineAlphaMode = mode;
+ }
+
+ final void setCombineAlphaMode(int mode) {
+ initCombineAlphaMode(mode);
+ sendMessage(COMBINE_ALPHA_MODE_CHANGED, enums[mode], null);
+ }
+
+ final int getCombineAlphaMode() {
+ return combineAlphaMode;
+ }
+
+ final void initCombineRgbSource(int index, int src) {
+ if (combineRgbSrc == null) {
+ // it is possible to set the combineRgbSource before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ combineRgbSrc[index] = src;
+ }
+
+ final void setCombineRgbSource(int index, int src) {
+ initCombineRgbSource(index, src);
+ sendMessage(COMBINE_RGB_SRC_CHANGED, enums[index], enums[src]);
+ }
+
+ final int getCombineRgbSource(int index) {
+ if (combineRgbSrc == null) {
+ // it is possible to do a get before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ return combineRgbSrc[index];
+ }
+
+ final void initCombineAlphaSource(int index, int src) {
+ if (combineRgbSrc == null) {
+ // it is possible to set the combineAlphaSource before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ combineAlphaSrc[index] = src;
+ }
+
+ final void setCombineAlphaSource(int index, int src) {
+ initCombineAlphaSource(index, src);
+ sendMessage(COMBINE_ALPHA_SRC_CHANGED, enums[index], enums[src]);
+ }
+
+ final int getCombineAlphaSource(int index) {
+ if (combineRgbSrc == null) {
+ // it is possible to do a get before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ return combineAlphaSrc[index];
+ }
+
+ final void initCombineRgbFunction(int index, int fcn) {
+ if (combineRgbSrc == null) {
+ // it is possible to set the combineRgbFcn before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ combineRgbFcn[index] = fcn;
+ }
+
+ final void setCombineRgbFunction(int index, int fcn) {
+ initCombineRgbFunction(index, fcn);
+ sendMessage(COMBINE_RGB_FCN_CHANGED, enums[index], enums[fcn]);
+ }
+
+ final int getCombineRgbFunction(int index) {
+ if (combineRgbSrc == null) {
+ // it is possible to do a get before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ return combineRgbFcn[index];
+ }
+
+ final void initCombineAlphaFunction(int index, int fcn) {
+ if (combineRgbSrc == null) {
+ // it is possible to set the combineAlphaFcn before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ combineAlphaFcn[index] = fcn;
+ }
+
+ final void setCombineAlphaFunction(int index, int fcn) {
+ initCombineAlphaFunction(index, fcn);
+ sendMessage(COMBINE_ALPHA_FCN_CHANGED, enums[index], enums[fcn]);
+ }
+
+ final int getCombineAlphaFunction(int index) {
+ if (combineRgbSrc == null) {
+ // it is possible to do a get before
+ // setting the texture mode to COMBINE, so need to initialize
+ // the combine mode related fields here
+ initCombineMode(this);
+ }
+ return combineAlphaFcn[index];
+ }
+
+ final void initCombineRgbScale(int scale) {
+ combineRgbScale = scale;
+ }
+
+ final void setCombineRgbScale(int scale) {
+ initCombineRgbScale(scale);
+ sendMessage(COMBINE_RGB_SCALE_CHANGED, enums[scale], null);
+ }
+
+ final int getCombineRgbScale() {
+ return combineRgbScale;
+ }
+
+ final void initCombineAlphaScale(int scale) {
+ combineAlphaScale = scale;
+ }
+
+ final void setCombineAlphaScale(int scale) {
+ initCombineAlphaScale(scale);
+ sendMessage(COMBINE_ALPHA_SCALE_CHANGED, enums[scale], null);
+ }
+
+ final int getCombineAlphaScale() {
+ return combineAlphaScale;
+ }
+
+ // These methods update the native context.
+ native void updateNative(long ctx,
+ double[] transform, boolean isIdentity, int textureMode,
+ int perspCorrectionMode, float red,
+ float green, float blue, float alpha,
+ int textureFormat);
+
+ native void updateNativeRegisterCombiners(long ctx,
+ double[] transform, boolean isIdentity, int textureMode,
+ int perspCorrectionMode, float red,
+ float green, float blue, float alpha,
+ int textureFormat,
+ int combineRgbMode, int combineAlphaMode,
+ int[] combineRgbSrc, int[] combineAlphaSrc,
+ int[] combineRgbFcn, int[] combineAlphaFcn,
+ int combineRgbScale, int combineAlphaScale);
+
+ native void updateTextureColorTableNative(long ctx, int numComponents,
+ int colorTableSize,
+ int[] colorTable);
+
+ native void updateCombinerNative(long ctx,
+ int combineRgbMode, int combineAlphaMode,
+ int[] combineRgbSrc, int[] combineAlphaSrc,
+ int[] combineRgbFcn, int[] combineAlphaFcn,
+ int combineRgbScale, int combineAlphaScale);
+
+ native void restoreBlend1Pass(long ctx);
+ native void updateBlend2Pass(long ctx);
+
+
+ void updateNative(Canvas3D cv, boolean simulate, int textureFormat) {
+
+ //System.out.println("TextureAttributes/updateNative: simulate= " + simulate + " " + this);
+
+ //if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_COLOR_TABLE)
+ // == 0) && textureColorTable != null) {
+ // System.out.println("TextureColorTable Not supported");
+ //}
+
+ //System.out.println("textureMode= " + textureMode);
+ boolean isIdentity =
+ ((transform.getType() & Transform3D.IDENTITY) != 0);
+
+ if (simulate == false) {
+ if (VirtualUniverse.mc.useCombiners &&
+ (cv.textureExtendedFeatures &
+ Canvas3D.TEXTURE_REGISTER_COMBINERS) != 0) {
+ updateNativeRegisterCombiners(cv.ctx,
+ transform.mat, isIdentity, textureMode, perspCorrectionMode,
+ textureBlendColor.x, textureBlendColor.y,
+ textureBlendColor.z, textureBlendColor.w,
+ textureFormat, combineRgbMode, combineAlphaMode,
+ combineRgbSrc, combineAlphaSrc,
+ combineRgbFcn, combineAlphaFcn,
+ combineRgbScale, combineAlphaScale);
+ } else {
+ if (textureMode == TextureAttributes.COMBINE) {
+
+ if ((cv.textureExtendedFeatures &
+ Canvas3D.TEXTURE_COMBINE) != 0) {
+
+ // Texture COMBINE is supported by the underlying layer
+
+ int _combineRgbMode = combineRgbMode;
+ int _combineAlphaMode = combineAlphaMode;
+
+ updateNative(cv.ctx, transform.mat, isIdentity, textureMode,
+ perspCorrectionMode,
+ textureBlendColor.x, textureBlendColor.y,
+ textureBlendColor.z, textureBlendColor.w,
+ textureFormat);
+
+
+ if (((combineRgbMode == TextureAttributes.COMBINE_DOT3) &&
+ ((cv.textureExtendedFeatures &
+ Canvas3D.TEXTURE_COMBINE_DOT3) == 0)) ||
+ ((combineRgbMode == TextureAttributes.COMBINE_SUBTRACT) &&
+ ((cv.textureExtendedFeatures &
+ Canvas3D.TEXTURE_COMBINE_SUBTRACT) == 0))) {
+
+ // Combine DOT3/SUBTRACT is not supported by the
+ // underlying layer, fallback to COMBINE_REPLACE
+
+ _combineRgbMode = TextureAttributes.COMBINE_REPLACE;
+ }
+
+ if (((combineAlphaMode == TextureAttributes.COMBINE_DOT3) &&
+ ((cv.textureExtendedFeatures &
+ Canvas3D.TEXTURE_COMBINE_DOT3) == 0)) ||
+ ((combineAlphaMode == TextureAttributes.COMBINE_SUBTRACT) &&
+ ((cv.textureExtendedFeatures &
+ Canvas3D.TEXTURE_COMBINE_SUBTRACT) == 0))) {
+
+ // Combine DOT3/SUBTRACT is not supported by the
+ // underlying layer, fallback to COMBINE_REPLACE
+
+ _combineAlphaMode = TextureAttributes.COMBINE_REPLACE;
+ }
+
+ updateCombinerNative(cv.ctx,
+ _combineRgbMode, _combineAlphaMode,
+ combineRgbSrc, combineAlphaSrc,
+ combineRgbFcn, combineAlphaFcn,
+ combineRgbScale, combineAlphaScale);
+
+ } else {
+
+ // Texture COMBINE is not supported by the underlying
+ // layer, fallback to REPLACE
+
+ updateNative(cv.ctx, transform.mat, isIdentity,
+ TextureAttributes.REPLACE,
+ perspCorrectionMode,
+ textureBlendColor.x, textureBlendColor.y,
+ textureBlendColor.z, textureBlendColor.w,
+ textureFormat);
+ }
+ } else {
+ updateNative(cv.ctx, transform.mat, isIdentity, textureMode,
+ perspCorrectionMode,
+ textureBlendColor.x, textureBlendColor.y,
+ textureBlendColor.z, textureBlendColor.w,
+ textureFormat);
+ }
+ }
+
+
+ if (((cv.textureExtendedFeatures & Canvas3D.TEXTURE_COLOR_TABLE)
+ != 0) && textureColorTable != null) {
+
+ updateTextureColorTableNative(cv.ctx,
+ numTextureColorTableComponents,
+ textureColorTableSize, textureColorTable);
+ }
+ } else {
+ // we are in the multi-pass mode,
+ // in this case, set the texture Mode to replace and use
+ // blending to simulate the original textureMode
+ updateNative(cv.ctx, transform.mat, isIdentity, TextureAttributes.REPLACE,
+ perspCorrectionMode,
+ textureBlendColor.x, textureBlendColor.y,
+ textureBlendColor.z, textureBlendColor.w, textureFormat);
+
+ if (((cv.textureExtendedFeatures & Canvas3D.TEXTURE_COLOR_TABLE)
+ != 0) && textureColorTable != null) {
+
+ updateTextureColorTableNative(cv.ctx, numTextureColorTableComponents,
+ textureColorTableSize, textureColorTable);
+ }
+
+ switch (textureMode) {
+ case TextureAttributes.COMBINE:
+ case TextureAttributes.REPLACE:
+ cv.setBlendFunc(cv.ctx,
+ TransparencyAttributesRetained.BLEND_ONE,
+ TransparencyAttributesRetained.BLEND_ZERO);
+ break;
+ case TextureAttributes.MODULATE:
+ cv.setBlendFunc(cv.ctx,
+ TransparencyAttributesRetained.BLEND_DST_COLOR,
+ TransparencyAttributesRetained.BLEND_ZERO);
+ break;
+ case TextureAttributes.DECAL:
+ if (textureFormat == Texture.RGBA) {
+ cv.setBlendFunc(cv.ctx,
+ TransparencyAttributesRetained.BLEND_SRC_ALPHA,
+ TransparencyAttributesRetained.BLEND_ONE_MINUS_SRC_ALPHA);
+ } else {
+ cv.setBlendFunc(cv.ctx,
+ TransparencyAttributesRetained.BLEND_ONE,
+ TransparencyAttributesRetained.BLEND_ZERO);
+ }
+ break;
+ case TextureAttributes.BLEND:
+ cv.setBlendColor(cv.ctx, textureBlendColor.x, textureBlendColor.y,
+ textureBlendColor.z, textureBlendColor.w);
+ cv.setBlendFunc(cv.ctx,
+ TransparencyAttributesRetained.BLEND_CONSTANT_COLOR,
+ TransparencyAttributesRetained.BLEND_ONE_MINUS_SRC_COLOR);
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ } else {
+ TextureAttributesRetained mirrorTa = new TextureAttributesRetained();
+ mirrorTa.source = source;
+ mirrorTa.set(this);
+ mirror = mirrorTa;
+ }
+ } else {
+ ((TextureAttributesRetained)mirror).set(this);
+ }
+ }
+
+ /**
+ * Initializes a mirror object
+ */
+ synchronized void initMirrorObject() {
+ ((TextureAttributesRetained)mirror).set(this);
+ }
+
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value,
+ Object value2) {
+ TextureAttributesRetained mirrorTa = (TextureAttributesRetained)mirror;
+ mirrorTa.mirrorCompDirty = true;
+
+ if ((component & TRANSFORM_CHANGED) != 0) {
+ mirrorTa.transform.set((Transform3D)value);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ (Transform3D)value);
+ }
+ else if ((component & MODE_CHANGED) != 0) {
+ mirrorTa.textureMode = ((Integer)value).intValue();
+
+ if ((mirrorTa.textureMode == TextureAttributes.COMBINE) &&
+ (mirrorTa.combineRgbSrc == null)) {
+ initCombineMode(mirrorTa);
+ }
+ }
+ else if ((component & COLOR_CHANGED) != 0) {
+ mirrorTa.textureBlendColor.set((Color4f)value);
+ }
+ else if ((component & CORRECTION_CHANGED) != 0) {
+ mirrorTa.perspCorrectionMode = ((Integer)value).intValue();
+ }
+ else if ((component & TEXTURE_COLOR_TABLE_CHANGED) != 0) {
+ if (value == null) {
+ mirrorTa.textureColorTable = null;
+ mirrorTa.numTextureColorTableComponents = 0;
+ mirrorTa.textureColorTableSize = 0;
+ } else {
+ Object args[] = (Object[])value;
+ mirrorTa.textureColorTable = (int[])args[2];
+ mirrorTa.numTextureColorTableComponents =
+ ((Integer)args[0]).intValue();
+ mirrorTa.textureColorTableSize =
+ ((Integer)args[1]).intValue();
+ }
+ }
+ else if ((component & COMBINE_RGB_MODE_CHANGED) != 0) {
+ mirrorTa.combineRgbMode = ((Integer)value).intValue();
+ }
+ else if ((component & COMBINE_ALPHA_MODE_CHANGED) != 0) {
+ mirrorTa.combineAlphaMode = ((Integer)value).intValue();
+ }
+ else if ((component & COMBINE_RGB_SRC_CHANGED) != 0) {
+ if (mirrorTa.combineRgbSrc == null) {
+ //initialize the memory for combine mode
+ initCombineMode(mirrorTa);
+ }
+ int index = ((Integer)value).intValue();
+ mirrorTa.combineRgbSrc[index] = ((Integer)value2).intValue();
+ }
+ else if ((component & COMBINE_ALPHA_SRC_CHANGED) != 0) {
+ if (mirrorTa.combineRgbSrc == null) {
+ //initialize the memory for combine mode
+ initCombineMode(mirrorTa);
+ }
+ int index = ((Integer)value).intValue();
+ mirrorTa.combineAlphaSrc[index] = ((Integer)value2).intValue();
+ }
+ else if ((component & COMBINE_RGB_FCN_CHANGED) != 0) {
+ if (mirrorTa.combineRgbSrc == null) {
+ //initialize the memory for combine mode
+ initCombineMode(mirrorTa);
+ }
+ int index = ((Integer)value).intValue();
+ mirrorTa.combineRgbFcn[index] = ((Integer)value2).intValue();
+ }
+ else if ((component & COMBINE_ALPHA_FCN_CHANGED) != 0) {
+ if (mirrorTa.combineRgbSrc == null) {
+ //initialize the memory for combine mode
+ initCombineMode(mirrorTa);
+ }
+ int index = ((Integer)value).intValue();
+ mirrorTa.combineAlphaFcn[index] = ((Integer)value2).intValue();
+ }
+ else if ((component & COMBINE_RGB_SCALE_CHANGED) != 0) {
+ mirrorTa.combineRgbScale = ((Integer)value).intValue();
+ }
+ else if ((component & COMBINE_ALPHA_SCALE_CHANGED) != 0) {
+ mirrorTa.combineAlphaScale = ((Integer)value).intValue();
+ }
+ }
+
+
+ boolean equivalent(TextureAttributesRetained tr) {
+
+ if (tr == null) {
+ return (false);
+
+ } else if ((this.changedFrequent != 0) || (tr.changedFrequent != 0)) {
+ return (this == tr);
+ }
+
+ if (!(tr.transform.equals(transform) &&
+ tr.textureBlendColor.equals(textureBlendColor) &&
+ (tr.textureMode == textureMode) &&
+ (tr.perspCorrectionMode == perspCorrectionMode))) {
+ return false;
+ }
+
+
+ // now check for combine mode attributes if textureMode specifies
+ // COMBINE
+
+ if (textureMode == TextureAttributes.COMBINE) {
+
+ if ((tr.combineRgbMode != combineRgbMode) ||
+ (tr.combineAlphaMode != combineAlphaMode) ||
+ (tr.combineRgbScale != combineRgbScale) ||
+ (tr.combineAlphaScale != combineAlphaScale)) {
+ return false;
+ }
+
+ // now check if the operands for the combine equations are
+ // equivalent
+
+ int nOpNeeded = 0;
+
+ if (combineRgbMode == TextureAttributes.COMBINE_REPLACE) {
+ nOpNeeded = 1;
+ } else if (combineRgbMode == TextureAttributes.COMBINE_INTERPOLATE) {
+ nOpNeeded = 3;
+ } else {
+ nOpNeeded = 2;
+ }
+
+ for (int i = 0; i < nOpNeeded; i++) {
+ if ((tr.combineRgbSrc[i] != combineRgbSrc[i]) ||
+ (tr.combineAlphaSrc[i] != combineAlphaSrc[i]) ||
+ (tr.combineRgbFcn[i] != combineRgbFcn[i]) ||
+ (tr.combineAlphaFcn[i] != combineAlphaFcn[i])) {
+ return false;
+ }
+ }
+ }
+
+ // now check for texture color table
+
+ if (tr.textureColorTable == null) {
+ if (this.textureColorTable == null)
+ return true;
+ else
+ return false;
+ } else if (this.textureColorTable == null) {
+ // tr.textureColorTable != null
+ return false;
+ } else {
+ if (tr.textureColorTable.length != this.textureColorTable.length)
+ return false;
+
+ for (int i = 0; i < this.textureColorTable.length; i++) {
+ if (this.textureColorTable[i] != tr.textureColorTable[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ }
+
+
+ protected Object clone() {
+ TextureAttributesRetained tr = (TextureAttributesRetained)super.clone();
+ tr.transform = new Transform3D(transform);
+ tr.textureBlendColor = new Color4f(textureBlendColor);
+ if (textureColorTable != null) {
+ tr.textureColorTable = new int[textureColorTable.length];
+ System.arraycopy(textureColorTable, 0, tr.textureColorTable, 0,
+ textureColorTable.length);
+ } else {
+ tr.textureColorTable = null;
+ }
+
+ // clone the combine mode attributes
+ if (combineRgbSrc != null) {
+ tr.combineRgbSrc = new int[3];
+ tr.combineAlphaSrc = new int[3];
+ tr.combineRgbFcn = new int[3];
+ tr.combineAlphaFcn = new int[3];
+
+ for (int i = 0; i < 3; i++) {
+ tr.combineRgbSrc[i] = combineRgbSrc[i];
+ tr.combineAlphaSrc[i] = combineAlphaSrc[i];
+ tr.combineRgbFcn[i] = combineRgbFcn[i];
+ tr.combineAlphaFcn[i] = combineAlphaFcn[i];
+ }
+ }
+
+ // other attributes are copied in super.clone()
+ return tr;
+ }
+
+ protected void set(TextureAttributesRetained tr) {
+ super.set(tr);
+ transform.set(tr.transform);
+ textureBlendColor.set(tr.textureBlendColor);
+ textureMode = tr.textureMode;
+ perspCorrectionMode = tr.perspCorrectionMode;
+
+ // set texture color table
+
+ if (tr.textureColorTable != null) {
+ if (textureColorTable == null ||
+ textureColorTable.length != tr.textureColorTable.length) {
+ textureColorTable = new int[tr.textureColorTable.length];
+ }
+ System.arraycopy(tr.textureColorTable, 0, textureColorTable, 0,
+ tr.textureColorTable.length);
+ } else {
+ textureColorTable = null;
+ }
+ numTextureColorTableComponents = tr.numTextureColorTableComponents;
+ textureColorTableSize = tr.textureColorTableSize;
+
+
+ // set the combine mode attributes
+
+ combineRgbMode = tr.combineRgbMode;
+ combineAlphaMode = tr.combineAlphaMode;
+ combineRgbScale = tr.combineRgbScale;
+ combineAlphaScale = tr.combineAlphaScale;
+
+ if (tr.combineRgbSrc != null) {
+ if (combineRgbSrc == null) {
+ combineRgbSrc = new int[3];
+ combineAlphaSrc = new int[3];
+ combineRgbFcn = new int[3];
+ combineAlphaFcn = new int[3];
+ }
+
+ for (int i = 0; i < 3; i++) {
+ combineRgbSrc[i] = tr.combineRgbSrc[i];
+ combineAlphaSrc[i] = tr.combineAlphaSrc[i];
+ combineRgbFcn[i] = tr.combineRgbFcn[i];
+ combineAlphaFcn[i] = tr.combineAlphaFcn[i];
+ }
+ }
+ }
+
+
+ final void sendMessage(int attrMask, Object attr1, Object attr2) {
+
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.TEXTUREATTRIBUTES_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1] = new Integer(attrMask);
+ createMessage.args[2] = attr1;
+ createMessage.args[3] = attr2;
+ createMessage.args[4] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.TEXTUREATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1] = new Integer(attrMask);
+ createMessage.args[2] = attr1;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+ }
+
+ void handleFrequencyChange(int bit) {
+ switch (bit) {
+ case TextureAttributes.ALLOW_MODE_WRITE:
+ case TextureAttributes.ALLOW_BLEND_COLOR_WRITE:
+ case TextureAttributes.ALLOW_TRANSFORM_WRITE:
+ case TextureAttributes.ALLOW_COLOR_TABLE_WRITE:
+ case TextureAttributes.ALLOW_COMBINE_WRITE: {
+ setFrequencyChangeMask(bit, bit);
+ }
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TextureBin.java b/src/classes/share/javax/media/j3d/TextureBin.java
new file mode 100644
index 0000000..a34eb47
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureBin.java
@@ -0,0 +1,1683 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+/**
+ * The TextureBin manages a collection of TextureSetting objects.
+ * All objects in the TextureBin share the same Texture reference.
+ */
+
+
+//class TextureBin extends Object implements ObjectUpdate, NodeComponentUpdate {
+class TextureBin extends Object implements ObjectUpdate {
+
+ TextureUnitStateRetained [] texUnitState = null;
+
+ // last active texture unit
+ int lastActiveTexUnitIndex;
+
+ // number of active texture unit
+ int numActiveTexUnit;
+
+ /**
+ * The RenderBin for this object
+ */
+ RenderBin renderBin = null;
+
+ /**
+ * The AttribureBin that this TextureBin resides
+ */
+ AttributeBin attributeBin = null;
+
+ /**
+ * The references to the next and previous TextureBins in the
+ * list.
+ */
+ TextureBin next = null;
+ TextureBin prev = null;
+
+ /**
+ * Oring of the equivalence bits for all appearance attrs under
+ * this renderBin
+ */
+ int equivalent = 0;
+
+ /**
+ * If any of the texture reference in an appearance is frequently
+ * changable, then a separate TextureBin will be created for this
+ * appearance, and this TextureBin is marked as the sole user of
+ * this appearance, and app will be pointing to the appearance
+ * being referenced by this TextureBin. Otherwise, app is null
+ */
+ AppearanceRetained app = null;
+
+
+ /**
+ * Sole user node component dirty mask. The first bit is reserved
+ * for node component reference dirty bit. It is set if any of the
+ * texture related node component reference in the appearance is
+ * being modified. The second bit onwords are for the individual
+ * TextureUnitState dirty bit. The ith bit set means the (i-1)
+ * texture unit state is modified. Note, this mask only supports
+ * 30 texture unit states. If the appearance uses more than 31
+ * texture unit states, then the modification of the 32nd texture
+ * unit state and up will have the first bit set, that means
+ * the TextureBin will be reset, rather than only the particular
+ * texture unit state will be reset.
+ */
+ int soleUserCompDirty;
+
+ static final int SOLE_USER_DIRTY_REF = 0x1;
+ static final int SOLE_USER_DIRTY_TA = 0x2;
+ static final int SOLE_USER_DIRTY_TC = 0x4;
+ static final int SOLE_USER_DIRTY_TEXTURE = 0x8;
+ static final int SOLE_USER_DIRTY_TUS = 0x10;
+
+
+ /**
+ * The hashMap of RenderMolecules in this TextureBin
+ * this is used in rendering, the key used is localToVworld
+ */
+ HashMap addOpaqueRMs = new HashMap();
+ HashMap addTransparentRMs = new HashMap();
+
+
+ // A hashmap based on localToVworld for fast
+ // insertion of new renderMolecules
+ HashMap opaqueRenderMoleculeMap = new HashMap();
+ HashMap transparentRenderMoleculeMap = new HashMap();
+
+ // List of renderMolecules - used in rendering ..
+ RenderMolecule opaqueRMList = null;
+
+ RenderMolecule transparentRMList = null;
+ TransparentRenderingInfo parentTInfo;
+
+ int numRenderMolecules = 0;
+ int numEditingRenderMolecules = 0;
+
+ int tbFlag = 0; // a general bitmask for TextureBin
+
+ // Following are the bits used in flag
+
+ final static int ON_RENDER_BIN_LIST = 0x0001;
+ final static int ON_UPDATE_LIST = 0x0002;
+ final static int SOLE_USER = 0x0004;
+ final static int CONTIGUOUS_ACTIVE_UNITS = 0x0008;
+ final static int RESORT = 0x0010;
+ final static int ON_UPDATE_CHECK_LIST = 0x0020;
+
+ final static int USE_DISPLAYLIST = -2;
+ final static int USE_VERTEXARRAY = -1;
+
+ TextureBin(TextureUnitStateRetained[] state, AppearanceRetained app,
+ RenderBin rb) {
+ renderBin = rb;
+ tbFlag = 0;
+ reset(state, app);
+ }
+
+
+ /**
+ * For now, clone everything just like the other NodeComponent
+ */
+ void reset(TextureUnitStateRetained[] state, AppearanceRetained app) {
+
+ prev = null;
+ next = null;
+ opaqueRMList = null;
+ transparentRMList = null;
+ numEditingRenderMolecules = 0;
+
+ // determine if this appearance is a sole user of this
+ // TextureBin
+ if ((app != null) &&
+ (app.changedFrequent &
+ (AppearanceRetained.TEXTURE |
+ AppearanceRetained.TEXCOORD_GEN |
+ AppearanceRetained.TEXTURE_ATTR |
+ AppearanceRetained.TEXTURE_UNIT_STATE)) != 0) {
+ tbFlag |= TextureBin.SOLE_USER;
+ this.app = app;
+
+ } else {
+ tbFlag &= ~TextureBin.SOLE_USER;
+ this.app = null;
+ }
+
+ resetTextureState(state);
+
+ if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) == 0) {
+ renderBin.addTextureBin(this);
+ tbFlag |= TextureBin.ON_RENDER_BIN_LIST;
+ }
+
+ }
+
+ void resetTextureState(TextureUnitStateRetained[] state) {
+
+ int i, j;
+ boolean foundDisableUnit = false;
+ numActiveTexUnit = 0;
+ boolean d3dBlendMode = false;
+ lastActiveTexUnitIndex = 0;
+ boolean soleUser = ((tbFlag & TextureBin.SOLE_USER) != 0);
+ TextureRetained prevFirstTexture = null;
+ TextureRetained tex;
+
+
+ tbFlag |= TextureBin.CONTIGUOUS_ACTIVE_UNITS;
+
+ if (state != null) {
+
+ foundDisableUnit = false;
+
+ if (texUnitState == null || (texUnitState.length != state.length)) {
+ texUnitState = new TextureUnitStateRetained[state.length];
+ } else if (texUnitState.length > 0 && texUnitState[0] != null) {
+ prevFirstTexture = texUnitState[0].texture;
+ }
+
+ for (i = 0; i < state.length; i++) {
+ if (state[i] == null) {
+ texUnitState[i] = null;
+ foundDisableUnit = true;
+ } else {
+
+ // create a clone texture unit state
+ if (texUnitState[i] == null) {
+ texUnitState[i] = new TextureUnitStateRetained();
+ }
+
+ // for sole user TextureUnitState, save
+ // the node component reference in the mirror reference
+ // of the cloned copy for equal test, and
+ // for native download optimization
+ if (soleUser || state[i].changedFrequent != 0) {
+ texUnitState[i].mirror = state[i];
+ }
+
+ // for the lowest level of node component in
+ // TextureBin, clone it only if it is not
+ // changedFrequent; in other words, if the
+ // lowest level of texture related node components
+ // such as TextureAttributes & TexCoordGen is
+ // changedFrequent, have the cloned texUnitState
+ // reference the mirror of those node components
+ // directly. For Texture, we'll always reference
+ // the mirror.
+
+ // decrement the TextureBin ref count of the previous
+ // texture
+ tex = texUnitState[i].texture;
+ if (tex != null) {
+ tex.decTextureBinRefCount(this);
+ if (soleUser &&
+ (tex.textureBinRefCount == 0) &&
+ (tex != state[i].texture)) {
+ // In this case texture change but
+ // TextureBin will not invoke clear() to reset.
+ // So we need to free the texture resource here.
+ renderBin.addTextureResourceFreeList(tex);
+ }
+ }
+
+ texUnitState[i].texture = state[i].texture;
+
+ // increment the TextureBin ref count of the new
+ // texture
+
+ if (texUnitState[i].texture != null) {
+ texUnitState[i].texture.incTextureBinRefCount(this);
+ }
+
+ if (state[i].texAttrs != null) {
+
+ if (state[i].texAttrs.changedFrequent != 0) {
+ texUnitState[i].texAttrs = state[i].texAttrs;
+
+ } else {
+
+ // need to check for texAttrs.source because
+ // texAttrs could be pointing to the mirror
+ // in the last frame, so don't want to
+ // overwrite the mirror
+
+ if (texUnitState[i].texAttrs == null ||
+ texUnitState[i].texAttrs.source != null) {
+ texUnitState[i].texAttrs =
+ new TextureAttributesRetained();
+ }
+ texUnitState[i].texAttrs.set(
+ state[i].texAttrs);
+ texUnitState[i].texAttrs.mirrorCompDirty = true;
+
+ // for sole user TextureBin, we are saving
+ // the mirror node component in the mirror
+ // reference in the clone object. This
+ // will be used in state download to
+ // avoid redundant download
+
+ if (soleUser) {
+ texUnitState[i].texAttrs.mirror =
+ state[i].texAttrs;
+ } else {
+ texUnitState[i].texAttrs.mirror = null;
+ }
+
+ }
+ } else {
+ texUnitState[i].texAttrs = null;
+ }
+
+
+ if (state[i].texGen != null) {
+ if (state[i].texGen.changedFrequent != 0) {
+ texUnitState[i].texGen = state[i].texGen;
+ } else {
+
+ // need to check for texGen.source because
+ // texGen could be pointing to the mirror
+ // in the last frame, so don't want to
+ // overwrite the mirror
+
+ if (texUnitState[i].texGen == null ||
+ texUnitState[i].texGen.source != null) {
+ texUnitState[i].texGen =
+ new TexCoordGenerationRetained();
+ }
+
+ texUnitState[i].texGen.set(state[i].texGen);
+ texUnitState[i].texGen.mirrorCompDirty = true;
+
+
+ // for sole user TextureBin, we are saving
+ // the mirror node component in the mirror
+ // reference in the clone object. This
+ // will be used in state download to
+ // avoid redundant download
+
+ if (soleUser) {
+ texUnitState[i].texGen.mirror = state[i].texGen;
+ } else {
+ texUnitState[i].texGen.mirror = null;
+ }
+ }
+ } else {
+ texUnitState[i].texGen = null;
+ }
+
+
+ // track the last active texture unit
+ // and the total number of active texture unit
+ if (texUnitState[i].isTextureEnabled()) {
+ numActiveTexUnit++;
+ lastActiveTexUnitIndex = i;
+
+ if (foundDisableUnit) {
+
+ // mark that active texture units are not
+ // contiguous
+ tbFlag &= ~TextureBin.CONTIGUOUS_ACTIVE_UNITS;
+ }
+ } else {
+ foundDisableUnit = true;
+ }
+ }
+ }
+
+ // check to see if the TextureBin sorting criteria is
+ // modified for this textureBin; if yes, mark that
+ // resorting is needed
+
+ if ((texUnitState[0] == null && prevFirstTexture != null) ||
+ (texUnitState[0] != null &&
+ texUnitState[0].texture != prevFirstTexture)) {
+ tbFlag |= TextureBin.RESORT;
+ }
+
+ } else {
+
+ // check to see if the TextureBin sorting criteria is
+ // modified for this textureBin; if yes, mark that
+ // resorting is needed
+
+ if (texUnitState != null && texUnitState[0].texture != null) {
+ tbFlag |= TextureBin.RESORT;
+ }
+ texUnitState = null;
+ }
+
+ soleUserCompDirty = 0;
+ }
+
+
+ /**
+ * The TextureBin is to be removed from RenderBin,
+ * do the proper unsetting of any references
+ */
+ void clear() {
+
+ // make sure there is no reference to the scenegraph
+ app = null;
+
+ // for each texture referenced in the texture units, decrement
+ // the reference count. If the reference count == 0, tell
+ // the renderer to free up the resource
+ if (texUnitState != null) {
+
+ TextureRetained tex;
+
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null) {
+ if (texUnitState[i].texture != null) {
+ tex = texUnitState[i].texture;
+ tex.decTextureBinRefCount(this);
+
+ if (tex.textureBinRefCount == 0) {
+ renderBin.addTextureResourceFreeList(tex);
+ }
+
+ texUnitState[i].texture = null;
+ }
+
+ // make sure there is no more reference to the scenegraph
+
+ texUnitState[i].mirror = null;
+ texUnitState[i].texture = null;
+ if (texUnitState[i].texAttrs != null &&
+ texUnitState[i].texAttrs.source != null) {
+ texUnitState[i].texAttrs = null;
+ }
+ if (texUnitState[i].texGen != null &&
+ texUnitState[i].texGen.source != null) {
+ texUnitState[i].texGen = null;
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * This tests if the qiven textureUnitState matches this TextureBin
+ */
+ boolean equals(TextureUnitStateRetained state[], RenderAtom ra) {
+
+ int i, j, k = 0;
+ TextureRetained texture;
+
+ // if this TextureBin is a soleUser case or the incoming
+ // app has changedFrequent bit set for any of the texture
+ // related component, then either the current TextureBin
+ // or the incoming app requires the same app match
+ if (((tbFlag & TextureBin.SOLE_USER) != 0) ||
+ ((ra.app != null) &&
+ (ra.app.changedFrequent &
+ (AppearanceRetained.TEXTURE |
+ AppearanceRetained.TEXCOORD_GEN |
+ AppearanceRetained.TEXTURE_ATTR |
+ AppearanceRetained.TEXTURE_UNIT_STATE)) != 0)) {
+
+ if (app == ra.app) {
+
+ // if this textureBin is currently on a zombie state,
+ // we'll need to put it on the update list to reevaluate
+ // the state, because while it is on a zombie state,
+ // texture state could have been changed. Example,
+ // application could have detached an appearance,
+ // made changes to the texture references, and then
+ // reattached the appearance. In this case, the texture
+ // changes would not have reflected to the textureBin
+
+ if (numEditingRenderMolecules == 0) {
+
+ //System.out.println("===> TB in zombie state " + this);
+
+ if (soleUserCompDirty == 0) {
+ this.renderBin.tbUpdateList.add(this);
+ }
+ soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_REF;
+ }
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ if (texUnitState == null && state == null)
+ return (true);
+
+ if (texUnitState == null || state == null)
+ return (false);
+
+ if (state.length != texUnitState.length)
+ return (false);
+
+ for (i = 0; i < texUnitState.length; i++) {
+ // If texture Unit State is null
+ if (texUnitState[i] == null) {
+ if (state[i] != null)
+ return (false);
+ }
+ else {
+ if (!texUnitState[i].equivalent(state[i])) {
+ return (false);
+ }
+ }
+ }
+
+ // Check if the image component has changed(may be a clearLive texture
+ // change img component. setLive case)
+ //
+ if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) == 0) {
+ renderBin.addTextureBin(this);
+ tbFlag |= TextureBin.ON_RENDER_BIN_LIST;
+ }
+
+ return (true);
+
+ }
+
+
+ /*
+ // updateNodeComponentCheck is called for each soleUser TextureBin
+ // into which new renderAtom has been added. This method is called before
+ // updateNodeComponent() to allow TextureBin to catch any node
+ // component changes that have been missed because the changes
+ // come when there is no active renderAtom associated with the
+ // TextureBin. See bug# 4503926 for details.
+ public void updateNodeComponentCheck() {
+
+ //System.out.println("TextureBin.updateNodeComponentCheck()");
+
+ tbFlag &= ~TextureBin.ON_UPDATE_CHECK_LIST;
+
+ if ((soleUserCompDirty & SOLE_USER_DIRTY_REF) != 0) {
+ return ;
+ }
+
+ if ((app.compChanged & (AppearanceRetained.TEXTURE |
+ AppearanceRetained.TEXCOORD_GEN |
+ AppearanceRetained.TEXTURE_ATTR |
+ AppearanceRetained.TEXTURE_UNIT_STATE)) != 0) {
+ if (soleUserCompDirty == 0) {
+ this.renderBin.tbUpdateList.add(this);
+ }
+ soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_REF;
+
+ } else if (app.texUnitState != null) {
+
+ // if one texture unit state has to be reevaluated, then
+ // it's enough update checking because reevaluating texture unit
+ // state will automatically take care of its node component
+ // updates.
+
+ boolean done = false;
+
+ for (int i = 0; i < app.texUnitState.length && !done; i++) {
+ if (app.texUnitState[i] != null) {
+ if (app.texUnitState[i].compChanged != 0) {
+ if (soleUserCompDirty == 0) {
+ this.renderBin.tbUpdateList.add(this);
+ }
+ soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TUS;
+ done = true;
+ } else {
+ if (app.texUnitState[i].texAttrs != null &&
+ app.texUnitState[i].texAttrs.compChanged != 0) {
+ if (soleUserCompDirty == 0) {
+ this.renderBin.tbUpdateList.add(this);
+ }
+ soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TA;
+ }
+ if (app.texUnitState[i].texGen != null &&
+ app.texUnitState[i].texGen.compChanged != 0) {
+ if (soleUserCompDirty == 0) {
+ this.renderBin.tbUpdateList.add(this);
+ }
+ soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TC;
+ }
+ if (app.texUnitState[i].texture != null &&
+ ((app.texUnitState[i].texture.compChanged &
+ TextureRetained.ENABLE_CHANGED) != 0)) {
+ if (soleUserCompDirty == 0) {
+ this.renderBin.tbUpdateList.add(this);
+ }
+ soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TEXTURE;
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+
+
+
+
+ /**
+ * updateNodeComponent is called from RenderBin to update the
+ * clone copy of the sole user node component in TextureBin when the
+ * corresponding node component is being modified
+ */
+ public void updateNodeComponent() {
+
+ // don't bother to update if the TextureBin is already
+ // removed from RenderBin
+
+ if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) == 0)
+ return;
+
+ // if any of the texture reference in the appearance referenced
+ // by a sole user TextureBin is being modified, just do a reset
+
+ if (((tbFlag & TextureBin.SOLE_USER) != 0) &&
+ ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_REF) != 0)) {
+
+ resetTextureState(app.texUnitState);
+ return;
+ }
+
+ if (texUnitState == null) {
+ soleUserCompDirty = 0;
+ return;
+ }
+
+ if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TUS) != 0) {
+
+ // Now take care of the Texture Unit State changes
+ TextureUnitStateRetained tus, mirrorTUS = null;
+ boolean soleUser = ((tbFlag & TextureBin.SOLE_USER) != 0);
+
+ for (int i = 0; i < texUnitState.length; i++) {
+ tus = texUnitState[i];
+ if (tus != null) {
+ if (tus.mirror != null) {
+
+ mirrorTUS = (TextureUnitStateRetained)tus.mirror;
+
+ if (tus.texture != mirrorTUS.texture) {
+ if (tus.texture != null) {
+ tus.texture.decTextureBinRefCount(this);
+ }
+ tus.texture = mirrorTUS.texture;
+ if (tus.texture != null) {
+ tus.texture.incTextureBinRefCount(this);
+ }
+
+ // the first texture (TextureBin sorting
+ // criteria) is modified, so needs to resort
+
+ if (i == 0) {
+ tbFlag |= TextureBin.RESORT;
+ }
+ }
+
+
+ if (mirrorTUS.texAttrs != null) {
+ if (mirrorTUS.texAttrs.changedFrequent != 0) {
+ tus.texAttrs = mirrorTUS.texAttrs;
+ } else {
+ if (tus.texAttrs == null ||
+ tus.texAttrs.source != null) {
+ tus.texAttrs =
+ new TextureAttributesRetained();
+ }
+ tus.texAttrs.set(mirrorTUS.texAttrs);
+ tus.texAttrs.mirrorCompDirty = true;
+
+ if (soleUser) {
+ tus.texAttrs.mirror = mirrorTUS.texAttrs;
+ } else {
+ tus.texAttrs.mirror = null;
+ }
+ }
+ } else {
+ tus.texAttrs = null;
+ }
+
+ if (mirrorTUS.texGen != null) {
+ if (mirrorTUS.texGen.changedFrequent != 0) {
+ tus.texGen = mirrorTUS.texGen;
+ } else {
+ if (tus.texGen == null ||
+ tus.texGen.source != null) {
+ tus.texGen =
+ new TexCoordGenerationRetained();
+ }
+ tus.texGen.set(mirrorTUS.texGen);
+ tus.texGen.mirrorCompDirty = true;
+
+ if (soleUser) {
+ tus.texGen.mirror = mirrorTUS.texGen;
+ } else {
+ tus.texGen.mirror = null;
+ }
+ }
+ } else {
+ tus.texGen = null;
+ }
+ }
+ }
+ }
+
+ // need to reEvaluate # of active textures after the update
+ soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TEXTURE;
+
+ // TextureUnitState update automatically taken care of
+ // TextureAttributes & TexCoordGeneration update
+
+ soleUserCompDirty &= ~(TextureBin.SOLE_USER_DIRTY_TA |
+ TextureBin.SOLE_USER_DIRTY_TC);
+ }
+
+ if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TEXTURE) != 0) {
+
+
+
+ boolean foundDisableUnit = false;
+
+ numActiveTexUnit = 0;
+ lastActiveTexUnitIndex = 0;
+ tbFlag |= TextureBin.CONTIGUOUS_ACTIVE_UNITS;
+ for (int i = 0; i < texUnitState.length; i++) {
+
+ // track the last active texture unit
+ // and the total number of active texture unit
+ if (texUnitState[i] != null &&
+ texUnitState[i].isTextureEnabled()) {
+ numActiveTexUnit++;
+ lastActiveTexUnitIndex = i;
+
+ if (foundDisableUnit) {
+
+ // mark that active texture units are not
+ // contiguous
+ tbFlag &= ~TextureBin.CONTIGUOUS_ACTIVE_UNITS;
+ }
+ } else {
+ foundDisableUnit = true;
+ }
+ }
+ }
+
+ if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TA) != 0) {
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null &&
+ texUnitState[i].texAttrs != null &&
+ texUnitState[i].texAttrs.mirror != null &&
+ texUnitState[i].texAttrs.mirror.changedFrequent != 0) {
+ texUnitState[i].texAttrs = (TextureAttributesRetained)
+ texUnitState[i].texAttrs.mirror;
+ }
+ }
+ }
+
+ if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TC) != 0) {
+ for (int i = 0; i < texUnitState.length; i++) {
+ if (texUnitState[i] != null &&
+ texUnitState[i].texGen != null &&
+ texUnitState[i].texGen.mirror != null &&
+ texUnitState[i].texGen.mirror.changedFrequent != 0) {
+ texUnitState[i].texGen = (TexCoordGenerationRetained)
+ texUnitState[i].texGen.mirror;
+ }
+ }
+ }
+
+ soleUserCompDirty = 0;
+ }
+
+ public void updateObject() {
+ if (!addOpaqueRMs.isEmpty()) {
+ opaqueRMList = addAll(opaqueRenderMoleculeMap, addOpaqueRMs,
+ opaqueRMList, true);
+ }
+ if (!addTransparentRMs.isEmpty()) {
+ // If transparent and not in bg geometry and inodepth
+ // sorted transparency
+ if (transparentRMList == null &&
+ (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
+ attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+ // System.out.println("========> addTransparentTextureBin "+this);
+ transparentRMList = addAll(transparentRenderMoleculeMap,
+ addTransparentRMs, transparentRMList, false);
+ // Eventhough we are adding to transparentList , if all the RMS
+ // have been switched already due to changeLists, then there is
+ // nothing to add, and TBIN does not have any transparentRMList
+ if (transparentRMList != null) {
+ renderBin.addTransparentObject(this);
+ }
+
+ }
+ else {
+ transparentRMList = addAll(transparentRenderMoleculeMap,
+ addTransparentRMs, transparentRMList, false);
+ }
+ }
+ tbFlag &= ~TextureBin.ON_UPDATE_LIST;
+
+ }
+
+
+ /**
+ * Each list of renderMoledule with the same localToVworld
+ * is connect by rm.next and rm.prev.
+ * At the end of the list (i.e. rm.next = null) the field
+ * rm.nextMap is link to another list (with the same
+ * localToVworld). So during rendering it will traverse
+ * rm.next until this is null, then follow the .nextMap
+ * to access another list and use rm.next to continue
+ * until both rm.next and rm.nextMap are null.
+ *
+ * renderMoleculeMap is use to assist faster location of
+ * renderMolecule List with the same localToVWorld. The
+ * start of renderMolecule in the list with same
+ * localToVworld is insert in renderMoleculeMap. This
+ * map is clean up at removeRenderMolecule(). TextureBin
+ * also use the map for quick location of renderMolecule
+ * with the same localToVworld and attributes in
+ * findRenderMolecule().
+ */
+ RenderMolecule addAll(HashMap renderMoleculeMap, HashMap addRMs,
+ RenderMolecule startList,
+ boolean opaqueList) {
+ int i;
+ RenderMolecule r;
+ Collection c = addRMs.values();
+ Iterator listIterator = c.iterator();
+ RenderMolecule renderMoleculeList, head;
+
+ while (listIterator.hasNext()) {
+ boolean changed = false;
+ ArrayList curList = (ArrayList)listIterator.next();
+ r = (RenderMolecule)curList.get(0);
+ // If this is a opaque one , but has been switched to a transparentList or
+ // vice-versa (dur to changeLists function called before this), then
+ // do nothing!
+ // For changedFrequent case: Consider the case when a RM is added
+ // (so is in the addRM list) and then
+ // a change in transparent value occurs that make it from opaque to
+ // transparent (the switch is handled before this function is called)
+ if (r.isOpaqueOrInOG != opaqueList) {
+ continue;
+ }
+ // Get the list of renderMolecules for this transform
+ renderMoleculeList = (RenderMolecule)renderMoleculeMap.get(
+ r.localToVworld);
+ if (renderMoleculeList == null) {
+ renderMoleculeList = r;
+ renderMoleculeMap.put(r.localToVworld, renderMoleculeList);
+ // Add this renderMolecule at the beginning of RM list
+ if (startList == null) {
+ startList = r;
+ r.nextMap = null;
+ r.prevMap = null;
+ startList.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS;
+ }
+ else {
+ r.nextMap = startList;
+ startList.prevMap = r;
+ startList = r;
+ startList.nextMap.checkEquivalenceWithLeftNeighbor(r,
+ RenderMolecule.ALL_DIRTY_BITS);
+ }
+
+ }
+ else {
+ // Insert the renderMolecule next to a RM that has equivalent
+ // texture unit state
+ if ((head = insertRenderMolecule(r, renderMoleculeList)) != null) {
+ if (renderMoleculeList.prevMap != null) {
+ renderMoleculeList.prevMap.nextMap = head;
+ }
+ head.prevMap = renderMoleculeList.prevMap;
+ renderMoleculeList.prevMap = null;
+ renderMoleculeList = head;
+ changed = true;
+ }
+ }
+ for (i = 1; i < curList.size(); i++) {
+ r = (RenderMolecule)curList.get(i);
+ // If this is a opaque one , but has been switched to a transparentList or
+ // vice-versa (dur to changeLists function called before this), then
+ // do nothing!
+ // For changedFrequent case: Consider the case when a RM is added
+ // (so is in the addRM list) and then
+ // a change in transparent value occurs that make it from opaque to
+ // transparent (the switch is handled before this function is called)
+ if (r.isOpaqueOrInOG != opaqueList)
+ continue;
+ if ((head = insertRenderMolecule(r, renderMoleculeList)) != null) {
+ if (renderMoleculeList.prevMap != null) {
+ renderMoleculeList.prevMap.nextMap = head;
+ }
+ head.prevMap = renderMoleculeList.prevMap;
+ renderMoleculeList.prevMap = null;
+ renderMoleculeList = head;
+ changed = true;
+ }
+
+ }
+ if (changed) {
+ renderMoleculeMap.put(r.localToVworld, renderMoleculeList);
+ if (renderMoleculeList.prevMap != null) {
+ renderMoleculeList.checkEquivalenceWithLeftNeighbor(
+ renderMoleculeList.prevMap,
+ RenderMolecule.ALL_DIRTY_BITS);
+ }
+ else {
+ startList = renderMoleculeList;
+ startList.dirtyAttrsAcrossRms =
+ RenderMolecule.ALL_DIRTY_BITS;
+ }
+ }
+ }
+
+ addRMs.clear();
+ return startList;
+ }
+
+
+ // TODO: Could the analysis be done during insertRenderMolecule?
+ // Return the head of the list,
+ // if the insertion occurred at beginning of the list
+ RenderMolecule insertRenderMolecule(RenderMolecule r,
+ RenderMolecule renderMoleculeList) {
+ RenderMolecule rm, retval;
+
+ // Look for a RM that has an equivalent material
+ rm = renderMoleculeList;
+ while (rm != null) {
+ if (rm.material == r.material ||
+ (rm.definingMaterial != null &&
+ rm.definingMaterial.equivalent(r.definingMaterial))) {
+ // Put it here
+ r.next = rm;
+ r.prev = rm.prev;
+ if (rm.prev == null) {
+ renderMoleculeList = r;
+ retval = renderMoleculeList;
+ } else {
+ rm.prev.next = r;
+ retval = null;
+ }
+ rm.prev = r;
+ r.checkEquivalenceWithBothNeighbors(RenderMolecule.ALL_DIRTY_BITS);
+ return retval;
+ }
+ // If they are not equivalent, then skip to the first one
+ // that has a different material using the dirty bits
+ else {
+ rm = rm.next;
+ while (rm != null &&
+ ((rm.dirtyAttrsAcrossRms & RenderMolecule.MATERIAL_DIRTY) == 0)) {
+ rm = rm.next;
+ }
+ }
+ }
+ // Just put it up front
+ r.next = renderMoleculeList;
+ renderMoleculeList.prev = r;
+ renderMoleculeList = r;
+ r.checkEquivalenceWithBothNeighbors(RenderMolecule.ALL_DIRTY_BITS);
+ return renderMoleculeList;
+ }
+
+
+ /**
+ * Adds the given RenderMolecule to this TextureBin
+ */
+ void addRenderMolecule(RenderMolecule r, RenderBin rb) {
+ RenderMolecule rm;
+ ArrayList list;
+ HashMap map;
+ r.textureBin = this;
+
+ if (r.isOpaqueOrInOG)
+ map = addOpaqueRMs;
+ else
+ map = addTransparentRMs;
+
+ if ((list = (ArrayList)map.get(r.localToVworld)) == null) {
+ list = new ArrayList();
+ map.put(r.localToVworld, list);
+ }
+ list.add(r);
+
+ if ((tbFlag & TextureBin.ON_UPDATE_LIST) == 0) {
+ tbFlag |= TextureBin.ON_UPDATE_LIST;
+ rb.objUpdateList.add(this);
+ }
+ }
+
+ /**
+ * Removes the given RenderMolecule from this TextureBin
+ */
+ void removeRenderMolecule(RenderMolecule r) {
+ ArrayList list;
+ int index;
+ boolean found = false;
+ RenderMolecule renderMoleculeList, rmlist;
+ HashMap addMap;
+ HashMap allMap;
+ r.textureBin = null;
+
+ if (r.isOpaqueOrInOG) {
+ rmlist = opaqueRMList;
+ allMap = opaqueRenderMoleculeMap;
+ addMap = addOpaqueRMs;
+ }
+ else {
+ rmlist = transparentRMList;
+ allMap = transparentRenderMoleculeMap;
+ addMap = addTransparentRMs;
+ }
+ // If the renderMolecule being remove is contained in addRMs, then
+ // remove the renderMolecule from the addList
+ if ((list = (ArrayList) addMap.get(r.localToVworld)) != null) {
+ if ((index = list.indexOf(r)) != -1) {
+ list.remove(index);
+ // If this was the last element for this localToVworld, then remove
+ // the entry from the addRMs list
+ if (list.isEmpty()) {
+ addMap.remove(r.localToVworld);
+ }
+
+ r.prev = null;
+ r.next = null;
+ renderBin.renderMoleculeFreelist.add(r);
+ found = true;
+ }
+ }
+ if (!found) {
+ RenderMolecule head = removeOneRM(r, allMap, rmlist);
+
+ r.soleUserCompDirty = 0;
+ r.onUpdateList = 0;
+ if (r.definingPolygonAttributes != null &&
+ (r.definingPolygonAttributes.changedFrequent != 0))
+ r.definingPolygonAttributes = null;
+
+ if (r.definingLineAttributes != null &&
+ (r.definingLineAttributes.changedFrequent != 0))
+ r.definingLineAttributes = null;
+
+ if (r.definingPointAttributes != null &&
+ (r.definingPointAttributes.changedFrequent != 0))
+ r.definingPointAttributes = null;
+
+ if (r.definingMaterial != null &&
+ (r.definingMaterial.changedFrequent != 0))
+ r.definingMaterial = null;
+
+ if (r.definingColoringAttributes != null &&
+ (r.definingColoringAttributes.changedFrequent != 0))
+ r.definingColoringAttributes = null;
+
+ if (r.definingTransparency != null &&
+ (r.definingTransparency.changedFrequent != 0))
+ r.definingTransparency = null;
+
+ renderBin.removeRenderMolecule(r);
+ if (r.isOpaqueOrInOG) {
+ opaqueRMList = head;
+ }
+ else {
+ transparentRMList = head;
+ }
+
+ }
+ // If the renderMolecule removed is not opaque then ..
+ if (!r.isOpaqueOrInOG && transparentRMList == null && (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
+ attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+ renderBin.removeTransparentObject(this);
+ }
+ // If the rm removed is the one that is referenced in the tinfo
+ // then change this reference
+ else if (parentTInfo != null && parentTInfo.rm == r) {
+ parentTInfo.rm = transparentRMList;
+ }
+ // Removal of this texture setting from the texCoordGenartion
+ // is done during the removeRenderAtom routine in RenderMolecule.java
+ // Only remove this texture bin if there are no more renderMolcules
+ // waiting to be added
+ if (opaqueRenderMoleculeMap.isEmpty() && addOpaqueRMs.isEmpty() &&
+ transparentRenderMoleculeMap.isEmpty() && addTransparentRMs.isEmpty()) {
+ if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) != 0) {
+ tbFlag &= ~TextureBin.ON_RENDER_BIN_LIST;
+ renderBin.removeTextureBin(this);
+ }
+
+ attributeBin.removeTextureBin(this);
+ texUnitState = null;
+ }
+ }
+
+
+ /**
+ * This method is called to update the state for this
+ * TextureBin. This is only applicable in the single-pass case.
+ * Multi-pass render will have to take care of its own
+ * state update.
+ */
+ void updateAttributes(Canvas3D cv, int pass) {
+
+ boolean dirty = ((cv.canvasDirty & (Canvas3D.TEXTUREBIN_DIRTY|
+ Canvas3D.TEXTUREATTRIBUTES_DIRTY)) != 0);
+
+
+ if (cv.textureBin == this && !dirty) {
+ return;
+ }
+
+ cv.textureBin = this;
+
+ // save the current number of active texture unit so as
+ // to be able to reset the one that is not enabled in this bin
+
+ int lastActiveTexUnitIdx = -1;
+
+ // set the number active texture unit in Canvas3D
+ cv.setNumActiveTexUnit(numActiveTexUnit);
+
+ // state update
+ if (numActiveTexUnit <= 0) {
+ if (cv.getLastActiveTexUnit() >= 0) {
+ // no texture units enabled
+
+ // when the canvas supports multi texture units,
+ // we'll need to reset texture for all texture units
+ if (cv.multiTexAccelerated) {
+ for (int i = 0; i <= cv.getLastActiveTexUnit(); i++) {
+ cv.resetTexture(cv.ctx, i);
+ }
+ // set the active texture unit back to 0
+ cv.setNumActiveTexUnit(0);
+ cv.activeTextureUnit(cv.ctx, 0);
+ } else {
+ cv.resetTexture(cv.ctx, -1);
+ }
+ cv.setLastActiveTexUnit(-1);
+ }
+ } else if (pass < 0) {
+ int j = 0;
+ boolean oneToOneMapping;
+
+ if ((pass == USE_VERTEXARRAY) || VirtualUniverse.mc.isD3D()) {
+ // d3d or when the texUnitStateMap requires more texture
+ // units than what is supported by the canvas, then
+ // we'll need a compact texture unit mapping, that is,
+ // only the enabled texUnitStates will be mapped to
+ // texture units. And as a matter of fact, the
+ // render atoms will be rendered as vertex array.
+ oneToOneMapping = false;
+ } else {
+ oneToOneMapping = true;
+ }
+
+ for (int i = 0; i < texUnitState.length; i++) {
+
+ if (j >= cv.texUnitState.length) {
+ // We finish enabling the texture state.
+ // Note that it is possible
+ // texUnitState.length > cv.texUnitState.length
+
+ break;
+ }
+
+ if ((texUnitState[i] != null) &&
+ texUnitState[i].isTextureEnabled()) {
+ if (dirty ||
+ cv.texUnitState[j].mirror == null ||
+ cv.texUnitState[j].mirror != texUnitState[i].mirror) {
+ // update the texture unit state
+ texUnitState[i].updateNative(j, cv, false, false);
+ cv.texUnitState[j].mirror = texUnitState[i].mirror;
+ }
+
+ // create a mapping that maps an active texture
+ // unit to a texture unit state
+
+ lastActiveTexUnitIdx = j;
+ cv.setTexUnitStateMap(i, j++);
+
+
+ } else if (oneToOneMapping) {
+ // one to one mapping is needed when display list
+ // is used to render multi-textured geometries,
+ // since when display list is created, the texture
+ // unit state to texture unit mapping is based on
+ // the geometry texCoordMap only. At render time,
+ // the texture unit state enable flags could have
+ // been changed. In keeping a one to one mapping,
+ // we'll not need to rebuild the display list
+ if (j <= cv.getLastActiveTexUnit()) {
+ cv.resetTexture(cv.ctx, j);
+ }
+
+ cv.setTexUnitStateMap(i, j++);
+ }
+ }
+
+ // make sure to disable the remaining texture units
+ // since they could have been enabled from the previous
+ // texture bin
+
+ for (int i = j; i <= cv.getLastActiveTexUnit(); i++) {
+ cv.resetTexture(cv.ctx, i);
+ }
+
+ cv.setLastActiveTexUnit(lastActiveTexUnitIdx);
+ // tell the underlying library the texture unit mapping
+
+ if ((pass == USE_DISPLAYLIST) &&
+ (cv.numActiveTexUnit > 0)) {
+ cv.updateTexUnitStateMap();
+ }
+
+ // set the active texture unit back to 0
+ cv.activeTextureUnit(cv.ctx, 0);
+
+ } else {
+ // update the last active texture unit state
+ if (dirty || cv.texUnitState[0].mirror == null ||
+ cv.texUnitState[0].mirror !=
+ texUnitState[lastActiveTexUnitIndex].mirror) {
+ texUnitState[lastActiveTexUnitIndex].updateNative(
+ -1, cv, false, false);
+ cv.texUnitState[0].mirror =
+ texUnitState[lastActiveTexUnitIndex].mirror;
+
+ cv.setTexUnitStateMap(0, 0);
+ cv.setLastActiveTexUnit(0);
+ }
+ }
+ cv.canvasDirty &= ~Canvas3D.TEXTUREBIN_DIRTY;
+ }
+
+
+ /**
+ * Renders this TextureBin
+ */
+ void render(Canvas3D cv) {
+ render(cv, (Object) opaqueRMList);
+ }
+
+ void render(Canvas3D cv, Object rlist) {
+
+ boolean d3dBlendMode = false;
+ cv.texLinearMode = false;
+
+ /*
+ System.out.println("TextureBin/render " + this +
+ " numActiveTexUnit= " + numActiveTexUnit +
+ " numTexUnitSupported= " + cv.numTexUnitSupported);
+ */
+
+ // include this TextureBin to the to-be-updated state set in canvas
+ cv.setStateToUpdate(Canvas3D.TEXTUREBIN_BIT, this);
+
+ if ((texUnitState != null) &&
+ VirtualUniverse.mc.isD3D()) {
+ TextureUnitStateRetained tus;
+ // use multi-pass if one of the stage use blend mode
+ for (int i = 0; i < texUnitState.length; i++) {
+ tus = texUnitState[i];
+ if ((tus != null) &&
+ tus.isTextureEnabled()) {
+ if (tus.needBlend2Pass(cv)) {
+ d3dBlendMode = true;
+ }
+ if ((tus.texGen != null) &&
+ (tus.texGen.genMode ==
+ TexCoordGeneration.OBJECT_LINEAR)) {
+ cv.texLinearMode = true;
+ }
+ }
+ }
+ }
+
+ if ((numActiveTexUnit > cv.numTexUnitSupported) ||
+ d3dBlendMode) {
+ multiPassRender(cv, rlist);
+ } else if ((numActiveTexUnit > 0) &&
+ !VirtualUniverse.mc.isD3D() &&
+ (texUnitState.length > cv.numTexUnitSupported) &&
+ ((tbFlag & TextureBin.CONTIGUOUS_ACTIVE_UNITS) == 0)) {
+ renderList(cv, USE_VERTEXARRAY, rlist);
+ } else {
+ renderList(cv, USE_DISPLAYLIST, rlist);
+ }
+ }
+
+
+ /**
+ * render a render list
+ */
+ void renderList(Canvas3D cv, int pass, Object rlist) {
+
+ if (rlist instanceof RenderMolecule) {
+ renderList(cv, pass, (RenderMolecule) rlist);
+ } else if (rlist instanceof TransparentRenderingInfo) {
+ renderList(cv, pass, (TransparentRenderingInfo) rlist);
+ }
+ }
+
+
+ /**
+ * render list of RenderMolecule
+ */
+ void renderList(Canvas3D cv, int pass, RenderMolecule rlist) {
+
+ // bit mask of all attr fields that are equivalent across
+ // renderMolecules thro. ORing of invisible RMs.
+ int combinedDirtyBits = 0;
+ boolean rmVisible = true;
+ RenderMolecule rm = rlist;
+
+ while (rm != null) {
+ if(rmVisible) {
+ combinedDirtyBits = rm.dirtyAttrsAcrossRms;
+ }
+ else {
+ combinedDirtyBits |= rm.dirtyAttrsAcrossRms;
+ }
+
+ rmVisible = rm.render(cv, pass, combinedDirtyBits);
+
+
+ // next render molecule or the nextmap
+ if (rm.next == null) {
+ rm = rm.nextMap;
+ }
+ else {
+ rm = rm.next;
+ }
+ }
+ }
+
+
+ /**
+ * render sorted transparent list
+ */
+ void renderList(Canvas3D cv, int pass, TransparentRenderingInfo tinfo) {
+
+ RenderMolecule rm = tinfo.rm;
+ if (rm.isSwitchOn()) {
+ rm.transparentSortRender(cv, pass, tinfo);
+ }
+ }
+
+
+
+ /**
+ * multi rendering pass to simulate multiple texture units
+ */
+ void multiPassRender(Canvas3D cv, Object rlist) {
+
+ boolean startToSimulate = false;
+ boolean isFogEnabled = false;
+
+ // No lazy download of texture for multi-pass,
+ // update the texture states here now
+
+ // update the environment state
+
+ // no need to update the texture state in updateAttributes(), the state
+ // will be explicitly updated in the multi-pass
+ cv.setStateIsUpdated(Canvas3D.TEXTUREBIN_BIT);
+ cv.textureBin = this;
+ cv.canvasDirty &= ~Canvas3D.TEXTUREBIN_DIRTY;
+ cv.updateEnvState();
+
+
+ // first reset those texture units that are currently enabled
+
+ if (cv.multiTexAccelerated) {
+ int activeTexUnit = cv.getNumActiveTexUnit();
+ for (int i = 0; i < activeTexUnit; i++) {
+ cv.resetTexture(cv.ctx, i);
+ }
+ // set the active texture unit back to 0
+ cv.activeTextureUnit(cv.ctx, 0);
+ }
+
+ // only texture unit 0 will be used in multi-pass case
+ cv.setNumActiveTexUnit(1);
+ cv.setLastActiveTexUnit(0);
+
+ // first check if there is fog in the path
+ // if there is, then turn off fog now and turn it back on
+ // for the last pass only
+ isFogEnabled = (attributeBin.environmentSet.fog != null);
+
+ TextureUnitStateRetained tus;
+
+ for (int i = 0; i < texUnitState.length; i++) {
+ tus = texUnitState[i];
+
+ if (tus != null && tus.isTextureEnabled()) {
+
+
+ // update the canvas texture unit state cache
+ cv.texUnitState[0].mirror = tus.mirror;
+
+ tus.updateNative(-1, cv, false, startToSimulate);
+
+ if (!startToSimulate) {
+ startToSimulate = true;
+ if (isFogEnabled) {
+ cv.setFogEnableFlag(cv.ctx, false);
+ }
+ }
+
+ if (!tus.needBlend2Pass(cv)) {
+ // turn on fog again in the last pass
+
+ if (i == lastActiveTexUnitIndex && isFogEnabled) {
+ cv.setFogEnableFlag(cv.ctx, true);
+ }
+ renderList(cv, i, rlist);
+
+ } else {
+ // D3d needs two passes to simulate Texture.Blend mode
+ tus.texAttrs.updateNative(cv, false, tus.texture.format);
+ renderList(cv, i, rlist);
+
+ tus.texAttrs.updateBlend2Pass(cv.ctx);
+
+ // turn on fog again in the last pass
+
+ if (i == lastActiveTexUnitIndex && isFogEnabled) {
+ cv.setFogEnableFlag(cv.ctx, true);
+ }
+ renderList(cv, i, rlist);
+
+ // restore original blend mode in case
+ tus.texAttrs.restoreBlend1Pass(cv.ctx);
+ }
+ }
+ }
+
+ // adjust the depth test back to what it was
+ // and adjust the blend func to what it was
+ if (startToSimulate) {
+ cv.setStateToUpdate(Canvas3D.TRANSPARENCY_BIT);
+ }
+ }
+
+
+ void changeLists(RenderMolecule r) {
+ RenderMolecule renderMoleculeList, rmlist = null, head;
+ HashMap allMap = null;
+ ArrayList list;
+ int index;
+ boolean newRM = false;
+ // System.out.println("changeLists r = "+r+" tBin = "+this);
+ // If its a new RM then do nothing, otherwise move lists
+ if (r.isOpaqueOrInOG) {
+ if (opaqueRMList == null &&
+ (r.prev == null && r.prevMap == null && r.next == null &&
+ r.nextMap == null)) {
+ newRM = true;
+ }
+ else {
+ rmlist = opaqueRMList;
+ allMap = opaqueRenderMoleculeMap;
+ }
+
+ }
+ else {
+ if (transparentRMList == null &&
+ (r.prev == null && r.prevMap == null && r.next == null &&
+ r.nextMap == null) ){
+ newRM = true;
+ }
+ else {
+ rmlist = transparentRMList;
+ allMap = transparentRenderMoleculeMap;
+ }
+ }
+ if (!newRM) {
+ head = removeOneRM(r, allMap, rmlist);
+
+ if (r.isOpaqueOrInOG) {
+ opaqueRMList = head;
+ }
+ else {
+ transparentRMList = head;
+ if (transparentRMList == null &&
+ (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
+ attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+ renderBin.removeTransparentObject(this);
+ }
+ }
+ }
+ HashMap renderMoleculeMap;
+ RenderMolecule startList;
+
+ // Now insert in the other bin
+ r.evalAlphaUsage(attributeBin.definingRenderingAttributes, texUnitState);
+ r.isOpaqueOrInOG = r.isOpaque() ||r.inOrderedGroup;
+ if (r.isOpaqueOrInOG) {
+ startList = opaqueRMList;
+ renderMoleculeMap = opaqueRenderMoleculeMap;
+ markDlistAsDirty(r);
+ }
+ else {
+ startList = transparentRMList;
+ renderMoleculeMap = transparentRenderMoleculeMap;
+ if ((r.primaryMoleculeType &RenderMolecule.DLIST_MOLECULE) != 0 &&
+ renderBin.transpSortMode != View.TRANSPARENCY_SORT_NONE) {
+ renderBin.addDisplayListResourceFreeList(r);
+ renderBin.removeDirtyRenderMolecule(r);
+
+ r.vwcBounds.set(null);
+ r.displayListId = 0;
+ r.displayListIdObj = null;
+ // Change the group type for all the rlistInfo in the primaryList
+ RenderAtomListInfo rinfo = r.primaryRenderAtomList;
+ while (rinfo != null) {
+ rinfo.groupType = RenderAtom.SEPARATE_DLIST_PER_RINFO;
+ if (rinfo.renderAtom.dlistIds == null) {
+ rinfo.renderAtom.dlistIds = new int[rinfo.renderAtom.rListInfo.length];
+
+ for (int k = 0; k < rinfo.renderAtom.dlistIds.length; k++) {
+ rinfo.renderAtom.dlistIds[k] = -1;
+ }
+ }
+ if (rinfo.renderAtom.dlistIds[rinfo.index] == -1) {
+ rinfo.renderAtom.dlistIds[rinfo.index] = VirtualUniverse.mc.getDisplayListId().intValue();
+ renderBin.addDlistPerRinfo.add(rinfo);
+ }
+ rinfo = rinfo.next;
+ }
+ r.primaryMoleculeType = RenderMolecule.SEPARATE_DLIST_PER_RINFO_MOLECULE;
+ }
+ else {
+ markDlistAsDirty(r);
+ }
+
+ }
+ renderMoleculeList = (RenderMolecule)renderMoleculeMap.get(r.localToVworld);
+
+ if (renderMoleculeList == null) {
+ renderMoleculeList = r;
+ renderMoleculeMap.put(r.localToVworld, renderMoleculeList);
+ // Add this renderMolecule at the beginning of RM list
+ if (startList == null) {
+ startList = r;
+ r.nextMap = null;
+ r.prevMap = null;
+ startList.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS;
+ }
+ else {
+ r.nextMap = startList;
+ startList.prevMap = r;
+ startList = r;
+ startList.nextMap.checkEquivalenceWithLeftNeighbor(r,RenderMolecule.ALL_DIRTY_BITS);
+ }
+
+ }
+ else {
+ // Insert the renderMolecule next to a RM that has equivalent
+ // texture unit state
+ if ((head = insertRenderMolecule(r, renderMoleculeList)) != null) {
+ if (renderMoleculeList.prevMap != null) {
+ renderMoleculeList.prevMap.nextMap = head;
+ }
+ head.prevMap = renderMoleculeList.prevMap;
+ renderMoleculeList.prevMap = null;
+ renderMoleculeList = head;
+ renderMoleculeMap.put(r.localToVworld, renderMoleculeList);
+ if (renderMoleculeList.prevMap != null) {
+ renderMoleculeList.checkEquivalenceWithLeftNeighbor(renderMoleculeList.prevMap,
+ RenderMolecule.ALL_DIRTY_BITS);
+ }
+ else {
+ startList.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS;
+ startList = renderMoleculeList;
+ }
+ }
+
+ }
+ if (r.isOpaqueOrInOG) {
+ opaqueRMList = startList;
+ }
+ else {
+ // If transparent and not in bg geometry and inodepth sorted transparency
+ if (transparentRMList == null&&
+ (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
+ attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+ transparentRMList = startList;
+ renderBin.addTransparentObject(this);
+ }
+ else {
+ transparentRMList = startList;
+ }
+
+ }
+ }
+
+ RenderMolecule removeOneRM(RenderMolecule r, HashMap allMap, RenderMolecule list) {
+ RenderMolecule rmlist = list;
+ // In the middle, just remove and update
+ if (r.prev != null && r.next != null) {
+ r.prev.next = r.next;
+ r.next.prev = r.prev;
+ r.next.checkEquivalenceWithLeftNeighbor(r.prev,RenderMolecule.ALL_DIRTY_BITS);
+ }
+ // If whats is removed is at the end of an entry
+ else if (r.prev != null && r.next == null) {
+ r.prev.next = r.next;
+ r.prev.nextMap = r.nextMap;
+ if (r.nextMap != null) {
+ r.nextMap.prevMap = r.prev;
+ r.nextMap.checkEquivalenceWithLeftNeighbor(r.prev,RenderMolecule.ALL_DIRTY_BITS);
+ }
+ }
+ else if (r.prev == null && r.next != null) {
+ r.next.prev = null;
+ r.next.prevMap = r.prevMap;
+ if (r.prevMap != null) {
+ r.prevMap.nextMap = r.next;
+ r.next.checkEquivalenceWithLeftNeighbor(r.prevMap,RenderMolecule.ALL_DIRTY_BITS);
+ }
+ // Head of the rmList
+ else {
+ rmlist = r.next;
+ rmlist.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS;
+ }
+ allMap.put(r.localToVworld, r.next);
+ }
+ // Update the maps and remove this entry from the map list
+ else if (r.prev == null && r.next == null) {
+ if (r.prevMap != null) {
+ r.prevMap.nextMap = r.nextMap;
+
+ }
+ else {
+ rmlist = r.nextMap;
+ if (r.nextMap != null) {
+ rmlist.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS;
+ }
+ }
+ if (r.nextMap != null) {
+ r.nextMap.prevMap = r.prevMap;
+ if (r.prevMap != null) {
+ r.nextMap.checkEquivalenceWithLeftNeighbor(r.prevMap,RenderMolecule.ALL_DIRTY_BITS);
+ }
+
+ }
+
+ allMap.remove(r.localToVworld);
+
+
+ }
+ r.prev = null;
+ r.next = null;
+ r.prevMap = null;
+ r.nextMap = null;
+ return rmlist;
+ }
+
+ void markDlistAsDirty(RenderMolecule r) {
+
+ if (r.primaryMoleculeType == RenderMolecule.DLIST_MOLECULE) {
+ renderBin.addDirtyRenderMolecule(r);
+ }
+ else if (r.primaryMoleculeType == RenderMolecule.SEPARATE_DLIST_PER_RINFO_MOLECULE) {
+ RenderAtomListInfo ra = r.primaryRenderAtomList;
+ while (ra != null) {
+ renderBin.addDlistPerRinfo.add(ra);
+ ra = ra.next;
+ }
+ }
+ }
+
+
+ void decrActiveRenderMolecule() {
+ numEditingRenderMolecules--;
+
+ if (numEditingRenderMolecules == 0) {
+
+ // if number of editing renderMolecules goes to 0,
+ // inform the attributeBin that this textureBin goes to
+ // zombie state
+
+ attributeBin.decrActiveTextureBin();
+ }
+ }
+
+ void incrActiveRenderMolecule() {
+
+ if (numEditingRenderMolecules == 0) {
+
+ // if this textureBin is in zombie state, inform
+ // the attributeBin that this textureBin is activated again.
+
+ attributeBin.incrActiveTextureBin();
+ }
+
+ numEditingRenderMolecules++;
+ }
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/TextureCubeMap.java b/src/classes/share/javax/media/j3d/TextureCubeMap.java
new file mode 100644
index 0000000..6927cbf
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureCubeMap.java
@@ -0,0 +1,344 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * TextureCubeMap is a subclass of Texture class. It defines
+ * a special kind of texture mapping which is composed of a set of six
+ * 2D images representating the six faces of a cube. The texture coordinate
+ * (s,t,r) is used as a 3D direction vector emanating from the center
+ * of a cube to select a particular face of the cube based on the
+ * largest magnitude coordinate (the major axis). A new 2D texture coordinate
+ * (s,t) is then determined by dividing the other two coordinates (the minor
+ * axes) by the major axis value. The new coordinate is then used for
+ * texel lookup from the selected texture image of this cube map.
+ *
+ * The TextureCubeMap image is defined by specifying the images for each
+ * face of the cube. The cube map texture can be thought of as centered at
+ * the orgin of and aligned to an XYZ coordinate system. The names
+ * of the cube faces are:
+ *
+ * <UL>
+ * <LI>POSITIVE_X</LI>
+ * <LI>NEGATIVE_X</LI>
+ * <LI>POSITIVE_Y</LI>
+ * <LI>NEGATIVE_Y</LI>
+ * <LI>POSITIVE_Z</LI>
+ * <LI>NEGATIVE_Z</LI>
+ * </UL>
+ *
+ * @since Java 3D 1.3
+ * @see Canvas3D#queryProperties
+ */
+public class TextureCubeMap extends Texture {
+
+ /**
+ * Specifies the face of the cube that is pierced by the positive x axis
+ */
+ public static final int POSITIVE_X = 0;
+
+ /**
+ * Specifies the face of the cube that is pierced by the negative x axis
+ */
+ public static final int NEGATIVE_X = 1;
+
+ /**
+ * Specifies the face of the cube that is pierced by the positive y axis
+ */
+ public static final int POSITIVE_Y = 2;
+
+ /**
+ * Specifies the face of the cube that is pierced by the negative y axis
+ */
+ public static final int NEGATIVE_Y = 3;
+
+ /**
+ * Specifies the face of the cube that is pierced by the positive z axis
+ */
+ public static final int POSITIVE_Z = 4;
+
+ /**
+ * Specifies the face of the cube that is pierced by the negative z axis
+ */
+ public static final int NEGATIVE_Z = 5;
+
+
+ /**
+ * Constructs a texture object using default values.
+ * Note that the default constructor creates a texture object with
+ * a width of 0 and is, therefore, not useful.
+ */
+ public TextureCubeMap() {
+ super();
+ }
+
+ /**
+ * Constructs an empty TextureCubeMap object with specified mipmapMode
+ * format, and width. Image at base level
+ * must be set by
+ * the application using 'setImage' method. If mipmapMode is
+ * set to MULTI_LEVEL_MIPMAP, images for base level through maximum level
+ * must be set.
+ * Note that cube map is square in dimensions, hence specifying width
+ * is sufficient.
+ * @param mipmapMode type of mipmap for this Texture: One of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP.
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA.
+ * @param width width of image at level 0. Must be power of 2.
+ * @exception IllegalArgumentException if width is NOT
+ * power of 2 OR invalid format/mipmapMode is specified.
+ */
+ public TextureCubeMap(
+ int mipmapMode,
+ int format,
+ int width){
+
+ super(mipmapMode, format, width, width);
+ }
+
+ /**
+ * Constructs an empty TextureCubeMap object with specified mipmapMode
+ * format, width, and boundary width. Image at base level
+ * must be set by
+ * the application using 'setImage' method. If mipmapMode is
+ * set to MULTI_LEVEL_MIPMAP, images for base level through maximum level
+ * must be set.
+ * Note that cube map is square in dimensions, hence specifying width
+ * is sufficient.
+ * @param mipmapMode type of mipmap for this Texture: One of
+ * BASE_LEVEL, MULTI_LEVEL_MIPMAP.
+ * @param format data format of Textures saved in this object.
+ * One of INTENSITY, LUMINANCE, ALPHA, LUMINANCE_ALPHA, RGB, RGBA.
+ * @param width width of image at level 0. Must be power of 2.
+ * @param boundaryWidth width of the boundary.
+ *
+ * @exception IllegalArgumentException if width is NOT
+ * power of 2 OR invalid format/mipmapMode is specified.
+ */
+ public TextureCubeMap(
+ int mipmapMode,
+ int format,
+ int width,
+ int boundaryWidth){
+
+ super(mipmapMode, format, width, width, boundaryWidth);
+ }
+
+ /**
+ * Sets the image for a specified mipmap level of a specified face
+ * of the cube map
+ *
+ * @param level mipmap level
+ * @param face face of the cube map. One of:
+ * <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ * @param image ImageComponent2D object containing the image
+ *
+ * @exception IllegalArgumentException if
+ * <code>face</code> has a value other
+ * than <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ */
+ public void setImage(int level, int face, ImageComponent2D image) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureCubeMap1"));
+ }
+
+ if (isLive())
+ ((TextureCubeMapRetained)this.retained).setImage(level, face, image);
+ else
+ ((TextureCubeMapRetained)this.retained).initImage(level, face, image);
+ }
+
+ /**
+ * Sets the array of images for mipmap levels from base level through
+ * max level for a specified face of the cube map
+ *
+ * @param face face of the cube map. One of:
+ * <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ * @param images array of ImageComponent2D objects containing the images
+ *
+ * @exception IllegalArgumentException if
+ * <code>face</code> has a value other
+ * than <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ */
+ public void setImages(int face, ImageComponent2D[] images) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_WRITE))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureCubeMap1"));
+ }
+
+ if (isLive())
+ ((TextureCubeMapRetained)this.retained).setImages(face, images);
+ else
+ ((TextureCubeMapRetained)this.retained).initImages(face, images);
+
+ }
+
+
+ /**
+ * Retrieves the image for a specified mipmap level of a particular
+ * face of the cube map.
+ * @param level mipmap level to get.
+ * @param face face of the cube map. One of:
+ * <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ * @return the ImageComponent object containing the texture image at
+ * the specified mipmap level.
+ *
+ * @exception IllegalArgumentException if
+ * <code>face</code> has a value other
+ * than <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public ImageComponent getImage(int level, int face) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureCubeMap2"));
+ }
+
+ return ((TextureCubeMapRetained)this.retained).getImage(level, face);
+ }
+
+ /**
+ * Retrieves the array of images for all mipmap level of a particular
+ * face of the cube map.
+ * @param face face of the cube map. One of:
+ * <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ * @return an array of ImageComponent object for the particular face of
+ * of the cube map.
+ *
+ * @exception IllegalArgumentException if
+ * <code>face</code> has a value other
+ * than <code>POSITIVE_X</code>, <code>NEGATIVE_X</code>,
+ * <code>POSITIVE_Y</code>, <code>NEGATIVE_Y</code>,
+ * <code>POSITIVE_Z</code> or <code>NEGATIVE_Z</code>.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public ImageComponent[] getImages(int face) {
+ if (isLiveOrCompiled()) {
+ if(!this.getCapability(ALLOW_IMAGE_READ))
+ throw new CapabilityNotSetException(
+ J3dI18N.getString("TextureCubeMap2"));
+ }
+
+ return ((TextureCubeMapRetained)this.retained).getImages(face);
+ }
+
+
+ /**
+ * This method is not supported for TextureCubeMap.
+ * A face of the cube map has to be specified when setting an image
+ * for a particular level of the cube map.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setImage(int level, ImageComponent image) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * This method is not supported for TextureCubeMap.
+ * A face of the cube map has to be specified when setting images
+ * for the cube map.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public void setImages(ImageComponent[] images) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * This method is not supported for TextureCubeMap.
+ * A face of the cube map has to be specified when retrieving an image
+ * for a particular level of the cube map.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public ImageComponent getImage(int level) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * This method is not supported for TextureCubeMap.
+ * A face of the cube map has to be specified when retrieving images
+ * for the cube map.
+ *
+ * @exception UnsupportedOperationException this method is not supported
+ *
+ * @since Java 3D 1.3
+ */
+ public ImageComponent[] getImages() {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Creates a retained mode TextureCubeMapRetained object that this
+ * TextureCubeMap component object will point to.
+ */
+ void createRetained() {
+ this.retained = new TextureCubeMapRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * NOTE: Applications should not call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @deprecated replaced with duplicateNodeComponent(
+ * NodeComponent originalNodeComponent, boolean forceDuplicate)
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+ checkDuplicateNodeComponent(originalNodeComponent);
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/TextureCubeMapRetained.java b/src/classes/share/javax/media/j3d/TextureCubeMapRetained.java
new file mode 100644
index 0000000..4283b2e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureCubeMapRetained.java
@@ -0,0 +1,301 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import javax.vecmath.Color4f;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+
+/**
+ * TextureCubeMap is a subclass of Texture class.
+ */
+class TextureCubeMapRetained extends TextureRetained {
+
+
+ static final int NUMFACES = 6;
+
+
+ void initialize(int format, int width, int widPower,
+ int height, int heiPower, int mipmapMode,
+ int boundaryWidth) {
+
+ this.numFaces = 6;
+
+ super.initialize(format, width, widPower, height, heiPower,
+ mipmapMode, boundaryWidth);
+ }
+
+
+ /**
+ * Sets a specified mipmap level for a particular face of the cubemap.
+ */
+ void initImage(int level, int face, ImageComponent image) {
+ if (this.images == null) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureRetained0"));
+ }
+
+ if (image instanceof ImageComponent3D) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureCubeMap3"));
+ }
+
+
+ if (face < TextureCubeMap.POSITIVE_X ||
+ face > TextureCubeMap.NEGATIVE_Z) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureCubeMap4"));
+ }
+
+ if (this.source.isLive()) {
+ if (this.images[face][level] != null) {
+ this.images[face][level].clearLive(refCount);
+ }
+
+
+ if (image != null) {
+ ((ImageComponentRetained)image.retained).setLive(
+ inBackgroundGroup, refCount);
+ }
+ }
+
+ ((ImageComponent2DRetained)image.retained).setTextureRef();
+
+ if (image != null) {
+ this.images[face][level] = (ImageComponentRetained)image.retained;
+ } else {
+ this.images[face][level] = null;
+ }
+ }
+
+ final void setImage(int level, int face, ImageComponent image) {
+
+ checkImageSize(level, image);
+
+ initImage(level, face, image);
+
+ Object arg[] = new Object[3];
+ arg[0] = new Integer(level);
+ arg[1] = image;
+ arg[2] = new Integer(face);
+ sendMessage(IMAGE_CHANGED, arg);
+
+ // If the user has set enable to true, then if the image is null
+ // turn off texture enable
+ if (userSpecifiedEnable) {
+ enable = userSpecifiedEnable;
+ if (image != null && level < maxLevels) {
+ ImageComponentRetained img= (ImageComponentRetained)image.retained;
+ if (img.isByReference()) {
+ if (img.bImage[0] == null) {
+ enable = false;
+ }
+ }
+ else {
+ if (img.imageYup == null) {
+ enable = false;
+ }
+ }
+ if (!enable)
+ sendMessage(ENABLE_CHANGED, Boolean.FALSE);
+ }
+ }
+ }
+
+ void initImages(int face, ImageComponent[] images) {
+
+ if (images.length != maxLevels)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture20"));
+
+ for (int i = 0; i < images.length; i++) {
+ initImage(i, face, images[i]);
+ }
+ }
+
+ final void setImages(int face, ImageComponent[] images) {
+
+ int i;
+ ImageComponentRetained[] imagesRet =
+ new ImageComponentRetained[images.length];
+ for (i = 0; i < images.length; i++) {
+ imagesRet[i] = (ImageComponentRetained)images[i].retained;
+ }
+ checkSizes(imagesRet);
+
+ initImages(face, images);
+
+ ImageComponent [] imgs = new ImageComponent[images.length];
+ for (i = 0; i < images.length; i++) {
+ imgs[i] = images[i];
+ }
+
+ Object args[] = new Object[2];
+ args[0] = imgs;
+ args[1] = new Integer(face);
+
+ sendMessage(IMAGES_CHANGED, args);
+ // If the user has set enable to true, then if the image is null
+ // turn off texture enable
+ if (userSpecifiedEnable) {
+ enable = userSpecifiedEnable;
+ i = 0;
+ while (enable && i < maxLevels) {
+ if (images[i] != null) {
+ ImageComponentRetained img= (ImageComponentRetained)images[i].retained;
+ if (img.isByReference()) {
+ if (img.bImage[0] == null) {
+ enable = false;
+ }
+ }
+ else {
+ if (img.imageYup == null) {
+ enable = false;
+ }
+ }
+ }
+ i++;
+ }
+ if (!enable) {
+ sendMessage(ENABLE_CHANGED, Boolean.FALSE);
+ }
+ }
+ }
+
+
+
+
+ /**
+ * Gets a specified mipmap level of a particular face of the cube map.
+ * @param level mipmap level to get
+ * @param face face of the cube map
+ * @return the pixel array object containing the texture image
+ */
+ final ImageComponent getImage(int level, int face) {
+
+ if (face < TextureCubeMap.POSITIVE_X ||
+ face > TextureCubeMap.NEGATIVE_Z) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureCubeMap4"));
+ }
+
+ return (((images != null) && (images[face][level] != null)) ?
+ (ImageComponent)images[face][level].source : null);
+ }
+
+
+ /**
+ * Gets an array of image for a particular face of the cube map.
+ * @param face face of the cube map
+ * @return the pixel array object containing the texture image
+ */
+ final ImageComponent[] getImages(int face) {
+
+ if (images == null)
+ return null;
+
+ if (face < TextureCubeMap.POSITIVE_X ||
+ face > TextureCubeMap.NEGATIVE_Z) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureCubeMap4"));
+ }
+
+ ImageComponent [] rImages = new ImageComponent[images[face].length];
+ for (int i = 0; i < images[face].length; i++) {
+ if (images[face][i] != null)
+ rImages[i] = (ImageComponent)images[face][i].source;
+ else
+ rImages[i] = null;
+ }
+ return rImages;
+ }
+
+
+ native void bindTexture(long ctx, int objectId, boolean enable);
+
+ native void updateTextureFilterModes(long ctx, int minFilter,
+ int magFilter);
+
+ native void updateTextureBoundary(long ctx,
+ int boundaryModeS, int boundaryModeT,
+ float boundaryRed, float boundaryGreen,
+ float boundaryBlue, float boundaryAlpha);
+
+ native void updateTextureSharpenFunc(long ctx,
+ int numSharpenTextureFuncPts,
+ float[] sharpenTextureFuncPts);
+
+ native void updateTextureFilter4Func(long ctx,
+ int numFilter4FuncPts,
+ float[] filter4FuncPts);
+
+ native void updateTextureAnisotropicFilter(long ctx, float degree);
+
+ native void updateTextureImage(long ctx,
+ int face,
+ int numLevels,
+ int level,
+ int internalFormat, int format,
+ int width, int height,
+ int boundaryWidth, byte[] imageYup);
+
+ native void updateTextureSubImage(long ctx, int face,
+ int level, int xoffset, int yoffset,
+ int internalFormat,int format,
+ int imgXOffset, int imgYOffset,
+ int tilew,
+ int width, int height,
+ byte[] image);
+
+
+
+ /**
+ * Load level 0 explicitly with null data pointer to allow
+ * mipmapping when level 0 is not the base level
+ */
+ void updateTextureDimensions(Canvas3D cv) {
+ for (int i = 0; i < 6; i++) {
+ updateTextureImage(cv.ctx, i, maxLevels, 0,
+ format, ImageComponentRetained.BYTE_RGBA,
+ width, height, boundaryWidth, null);
+ }
+ }
+
+
+
+ // This is just a wrapper of the native method.
+
+ void updateTextureImage(Canvas3D cv, int face, int numLevels, int level,
+ int format, int storedFormat,
+ int width, int height,
+ int boundaryWidth, byte[] data) {
+
+ updateTextureImage(cv.ctx, face, numLevels, level, format,
+ storedFormat, width, height,
+ boundaryWidth, data);
+ }
+
+
+ // This is just a wrapper of the native method.
+
+ void updateTextureSubImage(Canvas3D cv, int face, int level,
+ int xoffset, int yoffset, int format,
+ int storedFormat, int imgXOffset,
+ int imgYOffset, int tileWidth,
+ int width, int height, byte[] data) {
+
+ updateTextureSubImage(cv.ctx, face, level, xoffset, yoffset, format,
+ storedFormat, imgXOffset, imgYOffset,
+ tileWidth, width, height, data);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TextureRetained.java b/src/classes/share/javax/media/j3d/TextureRetained.java
new file mode 100644
index 0000000..c392881
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureRetained.java
@@ -0,0 +1,2597 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import javax.vecmath.*;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+
+/**
+ * The Texture object is a component object of an Appearance object
+ * that defines the texture properties used when texture mapping is
+ * enabled. Texture object is an abstract class and all texture
+ * objects must be created as either a Texture2D object or a
+ * Texture3D object.
+ */
+abstract class TextureRetained extends NodeComponentRetained {
+ // A list of pre-defined bits to indicate which component
+ // in this Texture object changed.
+ static final int ENABLE_CHANGED = 0x001;
+ static final int COLOR_CHANGED = 0x002;
+ static final int IMAGE_CHANGED = 0x004;
+ static final int STATE_CHANGED = 0x008;
+ static final int UPDATE_IMAGE = 0x010;
+ static final int IMAGES_CHANGED = 0x020;
+ static final int BASE_LEVEL_CHANGED = 0x040;
+ static final int MAX_LEVEL_CHANGED = 0x080;
+ static final int MIN_LOD_CHANGED = 0x100;
+ static final int MAX_LOD_CHANGED = 0x200;
+ static final int LOD_OFFSET_CHANGED = 0x400;
+
+ // constants for min and mag filter
+ static final int MIN_FILTER = 0;
+ static final int MAG_FILTER = 1;
+
+ // Boundary width
+ int boundaryWidth = 0;
+
+ // Boundary modes (wrap, clamp, clamp_to_edge, clamp_to_boundary)
+ int boundaryModeS = Texture.WRAP;
+ int boundaryModeT = Texture.WRAP;
+
+ // Filter modes
+ int minFilter = Texture.BASE_LEVEL_POINT;
+ int magFilter = Texture.BASE_LEVEL_POINT;
+
+ // Integer flag that contains bitset to indicate
+ // which field changed.
+ int isDirty = 0xffff;
+
+ // Texture boundary color
+ Color4f boundaryColor = new Color4f(0.0f, 0.0f, 0.0f, 0.0f);
+
+ // Texture Object Id used by native code.
+ int objectId = -1;
+
+ int mipmapMode = Texture.BASE_LEVEL; // Type of mip-mapping
+ int format = Texture.RGB; // Texture format
+ int width = 1; // Width in pixels (2**n)
+ int height = 1; // Height in pixels (2**m)
+
+ ImageComponentRetained images[][]; // Array of images (one for each mipmap level)
+ boolean imagesLoaded = false; // TRUE if all mipmap levels are loaded
+ int mipmapLevels; // Number of MIPMAP levels needed
+ int maxLevels = 0; // maximum number of levels needed for
+ // the mipmapMode of this texture
+ int maxMipMapLevels = 0; // maximum number of mipmap levels that
+ // can be defined for this texture
+
+ int numFaces = 1; // For CubeMap, it is 6
+
+ int baseLevel = 0;
+ int maximumLevel = 0;
+ float minimumLod = -1000.0f;
+ float maximumLod = 1000.0f;
+ Point3f lodOffset = null;
+
+
+ // Texture mapping enable switch
+ // This enable is derived from the user specified enable
+ // and whether the buf image in the imagecomp is null
+ boolean enable = true;
+
+ // User specified enable
+ boolean userSpecifiedEnable = true;
+
+
+ // true if alpha channel need update during rendering
+ boolean isAlphaNeedUpdate = false;
+
+ // sharpen texture info
+ int numSharpenTextureFuncPts = 0;
+ float sharpenTextureFuncPts[] = null; // array of pairs of floats
+ // first value for LOD
+ // second value for the fcn value
+
+ // filter4 info
+ float filter4FuncPts[] = null;
+
+ // anisotropic filter info
+ int anisotropicFilterMode = Texture.ANISOTROPIC_NONE;
+ float anisotropicFilterDegree = 1.0f;
+
+
+ // Each bit corresponds to a unique renderer if shared context
+ // or a unique canvas otherwise.
+ // This mask specifies which renderer/canvas has loaded the
+ // texture images. 0 means no renderer/canvas has loaded the texture.
+ // 1 at the particular bit means that renderer/canvas has loaded the
+ // texture. 0 means otherwise.
+ int resourceCreationMask = 0x0;
+
+ // Each bit corresponds to a unique renderer if shared context
+ // or a unique canvas otherwise
+ // This mask specifies if texture images are up-to-date.
+ // 0 at a particular bit means texture images are not up-to-date.
+ // 1 means otherwise. If it specifies 0, then it needs to go
+ // through the imageUpdateInfo to update the images accordingly.
+ //
+ int resourceUpdatedMask = 0x0;
+
+ // Each bit corresponds to a unique renderer if shared context
+ // or a unique canvas otherwise
+ // This mask specifies if texture lod info is up-to-date.
+ // 0 at a particular bit means texture lod info is not up-to-date.
+ // 1 means otherwise.
+ //
+ int resourceLodUpdatedMask = 0x0;
+
+ // Each bit corresponds to a unique renderer if shared context
+ // or a unique canvas otherwise
+ // This mask specifies if texture is in the resource reload list
+ // 0 at a particular bit means texture is not in reload list
+ // 1 means otherwise.
+ //
+ int resourceInReloadList = 0x0;
+
+ // image update info
+ ArrayList imageUpdateInfo[][];
+
+
+ int imageUpdatePruneMask[];
+
+
+ // textureBin reference counter
+ int textureBinRefCount = 0;
+
+ // This is used for D3D only to check whether texture need to
+ // resend down
+ private int texTimestamp = 0;
+
+ // need to synchronize access from multiple rendering threads
+ Object resourceLock = new Object();
+
+
+ void initialize(int format, int width, int widPower,
+ int height, int heiPower, int mipmapMode,
+ int boundaryWidth) {
+
+ this.mipmapMode = mipmapMode;
+ this.format = format;
+ this.width = width;
+ this.height = height;
+ this.boundaryWidth = boundaryWidth;
+
+ // determine the maximum number of mipmap levels that can be
+ // defined from the specified dimension
+
+ if (widPower > heiPower) {
+ maxMipMapLevels = widPower + 1;
+ } else {
+ maxMipMapLevels = heiPower + 1;
+ }
+
+
+ // determine the maximum number of mipmap levels that will be
+ // needed with the current mipmapMode
+
+ if (mipmapMode != Texture.BASE_LEVEL) {
+ baseLevel = 0;
+ maximumLevel = maxMipMapLevels - 1;
+ maxLevels = maxMipMapLevels;
+ } else {
+ baseLevel = 0;
+ maximumLevel = 0;
+ maxLevels = 1;
+ }
+
+ images = new ImageComponentRetained[numFaces][maxLevels];
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = 0; i < maxLevels; i++) {
+ images[j][i] = null;
+ }
+ }
+ imagesLoaded = false;
+
+ }
+
+ final int getFormat() {
+ return this.format;
+ }
+
+ final int getWidth() {
+ return this.width;
+ }
+
+ final int getHeight() {
+ return this.height;
+ }
+
+ final int numMipMapLevels() {
+ return (maximumLevel - baseLevel + 1);
+ }
+
+ /**
+ * Sets the boundary mode for the S coordinate in this texture object.
+ * @param boundaryModeS the boundary mode for the S coordinate,
+ * one of: CLAMP or WRAP.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final void initBoundaryModeS(int boundaryModeS) {
+ this.boundaryModeS = boundaryModeS;
+ }
+
+ /**
+ * Retrieves the boundary mode for the S coordinate.
+ * @return the current boundary mode for the S coordinate.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final int getBoundaryModeS() {
+ return boundaryModeS;
+ }
+
+ /**
+ * Sets the boundary mode for the T coordinate in this texture object.
+ * @param boundaryModeT the boundary mode for the T coordinate,
+ * one of: CLAMP or WRAP.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final void initBoundaryModeT(int boundaryModeT) {
+ this.boundaryModeT = boundaryModeT;
+ }
+
+ /**
+ * Retrieves the boundary mode for the T coordinate.
+ * @return the current boundary mode for the T coordinate.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final int getBoundaryModeT() {
+ return boundaryModeT;
+ }
+
+ /**
+ * Retrieves the boundary width.
+ * @return the boundary width of this texture.
+ */
+ final int getBoundaryWidth() {
+ return boundaryWidth;
+ }
+
+ /**
+ * Sets the minification filter function. This
+ * function is used when the pixel being rendered maps to an area
+ * greater than one texel.
+ * @param minFilter the minification filter, one of:
+ * FASTEST, NICEST, BASE_LEVEL_POINT, BASE_LEVEL_LINEAR,
+ * MULTI_LEVEL_POINT, MULTI_LEVEL_LINEAR.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final void initMinFilter(int minFilter) {
+ this.minFilter = minFilter;
+ }
+
+ /**
+ * Retrieves the minification filter.
+ * @return the current minification filter function.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final int getMinFilter() {
+ return minFilter;
+ }
+
+ /**
+ * Sets the magnification filter function. This
+ * function is used when the pixel being rendered maps to an area
+ * less than or equal to one texel.
+ * @param magFilter the magnification filter, one of:
+ * FASTEST, NICEST, BASE_LEVEL_POINT, or BASE_LEVEL_LINEAR.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final void initMagFilter(int magFilter) {
+ this.magFilter = magFilter;
+ }
+
+ /**
+ * Retrieves the magnification filter.
+ * @return the current magnification filter function.
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final int getMagFilter() {
+ return magFilter;
+ }
+
+ /**
+ * Sets a specified mipmap level.
+ * @param level mipmap level to set: 0 is the base level
+ * @param image pixel array object containing the texture image
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ * @exception IllegalArgumentException if an ImageComponent3D
+ * is used in a Texture2D or ImageComponent2D in Texture3D
+ * power of 2 OR invalid format/mipmapMode is specified.
+ */
+ void initImage(int level, ImageComponent image) {
+ if (this.images == null) {
+ throw new IllegalArgumentException(J3dI18N.getString("TextureRetained0"));
+ }
+
+ if (this.source instanceof Texture2D) {
+ if (image instanceof ImageComponent3D)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture8"))
+;
+ } else {
+
+ if (image instanceof ImageComponent2D)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture14")
+);
+ }
+
+
+ if (this.source.isLive()) {
+
+ if (this.images[0][level] != null) {
+ this.images[0][level].clearLive(refCount);
+ }
+
+
+ if (image != null) {
+ ((ImageComponentRetained)image.retained).setLive(inBackgroundGroup, refCount);
+ }
+ }
+
+ if (this instanceof Texture2DRetained) {
+ ((ImageComponent2DRetained)image.retained).setTextureRef();
+ } else {
+ ((ImageComponent3DRetained)image.retained).setTextureRef();
+ }
+
+ if (image != null) {
+ this.images[0][level] = (ImageComponentRetained)image.retained;
+
+ } else {
+ this.images[0][level] = null;
+ }
+ }
+
+ final void checkImageSize(int level, ImageComponent image) {
+ if (image != null) {
+ int imgHeight = ((ImageComponentRetained)image.retained).height;
+ int imgWidth = ((ImageComponentRetained)image.retained).width;
+ int i, tmp = 1;
+ // calculate tmp = 2**level
+ for (i=0; i < level; i++,tmp *= 2);
+
+ int hgt = height/tmp, wdh = width / tmp;
+ if (hgt < 1) hgt = 1;
+ if (wdh < 1) wdh = 1;
+
+ if ((hgt != imgHeight) || (wdh != imgWidth)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureRetained1"));
+ }
+ }
+ }
+
+ final void checkSizes(ImageComponentRetained images[]) {
+ // check that the image at each level is w/2 h/2 of the image at the
+ // previous level
+ if (images != null) {
+
+ // only need to check if there is more than 1 level
+ if (images.length > 1) {
+ int compareW = images[0].width/2;
+ int compareH = images[0].height/2;
+ int w, h;
+ for (int i = 1; i < images.length; i++) {
+ w = images[i].width;
+ h = images[i].height;
+ if (compareW < 1) compareW = 1;
+ if (compareH < 1) compareH = 1;
+ if ((w != compareW) && (h != compareH)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureRetained1"));
+ }
+ compareW = w/2;
+ compareH = h/2;
+ }
+ }
+ }
+ }
+
+ final void setImage(int level, ImageComponent image) {
+
+ checkImageSize(level, image);
+
+ initImage(level, image);
+
+ Object arg[] = new Object[3];
+ arg[0] = new Integer(level);
+ arg[1] = image;
+ arg[2] = new Integer(0);
+ sendMessage(IMAGE_CHANGED, arg);
+
+ // If the user has set enable to true, then if the image is null
+ // turn off texture enable
+
+ if (userSpecifiedEnable) {
+ enable = userSpecifiedEnable;
+ if (image != null && level >= baseLevel && level <= maximumLevel) {
+ ImageComponentRetained img= (ImageComponentRetained)image.retained;
+ if (img.isByReference()) {
+ if (img.bImage[0] == null) {
+ enable = false;
+ }
+ }
+ else {
+ if (img.imageYup == null) {
+ enable = false;
+ }
+ }
+ if (!enable)
+ sendMessage(ENABLE_CHANGED, Boolean.FALSE);
+ }
+ }
+ }
+
+ void initImages(ImageComponent[] images) {
+
+ if (images.length != maxLevels)
+ throw new IllegalArgumentException(J3dI18N.getString("Texture20"));
+
+ for (int i = 0; i < images.length; i++) {
+ initImage(i, images[i]);
+ }
+ }
+
+ final void setImages(ImageComponent[] images) {
+
+ int i;
+ ImageComponentRetained[] imagesRet =
+ new ImageComponentRetained[images.length];
+ for (i = 0; i < images.length; i++) {
+ imagesRet[i] = (ImageComponentRetained)images[i].retained;
+ }
+ checkSizes(imagesRet);
+
+ initImages(images);
+
+ ImageComponent [] imgs = new ImageComponent[images.length];
+ for (i = 0; i < images.length; i++) {
+ imgs[i] = images[i];
+ }
+
+ Object arg[] = new Object[2];
+ arg[0] = imgs;
+ arg[1] = new Integer(0);
+
+ sendMessage(IMAGES_CHANGED, arg);
+
+ // If the user has set enable to true, then if the image is null
+ // turn off texture enable
+
+ if (userSpecifiedEnable) {
+ enable = userSpecifiedEnable;
+ for (i = baseLevel; i <= maximumLevel && enable; i++) {
+ if (images[i] != null) {
+ ImageComponentRetained img=
+ (ImageComponentRetained)images[i].retained;
+ if (img.isByReference()) {
+ if (img.bImage[0] == null) {
+ enable = false;
+ }
+ }
+ else {
+ if (img.imageYup == null) {
+ enable = false;
+ }
+ }
+ }
+ }
+ if (!enable) {
+ sendMessage(ENABLE_CHANGED, Boolean.FALSE);
+ }
+ }
+ }
+
+
+
+
+ /**
+ * Gets a specified mipmap level.
+ * @param level mipmap level to get: 0 is the base level
+ * @return the pixel array object containing the texture image
+ * @exception RestrictedAccessException if the method is called
+ * when this object is part of live or compiled scene graph.
+ */
+ final ImageComponent getImage(int level) {
+ return (((images != null) && (images[0][level] != null)) ?
+ (ImageComponent)images[0][level].source : null);
+ }
+
+ final ImageComponent[] getImages() {
+ if (images == null)
+ return null;
+
+ ImageComponent [] rImages = new ImageComponent[images[0].length];
+ for (int i = 0; i < images[0].length; i++) {
+ if (images[0][i] != null)
+ rImages[i] = (ImageComponent)images[0][i].source;
+ else
+ rImages[i] = null;
+ }
+ return rImages;
+ }
+
+ /**
+ * Sets mipmap mode for texture mapping for this texture object.
+ * @param mipMapMode the new mipmap mode for this object. One of:
+ * BASE_LEVEL or MULTI_LEVEL_MIPMAP.
+ * @exception RestrictedAccessException if the method is called
+ */
+ final void initMipMapMode(int mipmapMode) {
+
+ if (this.mipmapMode == mipmapMode)
+ return;
+
+
+ int prevMaxLevels = maxLevels; // previous maxLevels
+
+ this.mipmapMode = mipmapMode;
+
+ if (mipmapMode != Texture.BASE_LEVEL) {
+ maxLevels = maxMipMapLevels;
+ } else {
+ baseLevel = 0;
+ maximumLevel = 0;
+ maxLevels = 1;
+ }
+
+
+ ImageComponentRetained[][] newImages =
+ new ImageComponentRetained[numFaces][maxLevels];
+
+ if (prevMaxLevels < maxLevels) {
+ for (int f = 0; f < numFaces; f++) {
+ for (int i = 0; i < prevMaxLevels; i++) {
+ newImages[f][i] = images[f][i];
+ }
+
+ for (int j = prevMaxLevels; j < maxLevels; j++) {
+ newImages[f][j] = null;
+ }
+ }
+ } else {
+ for (int f = 0; f < numFaces; f++) {
+ for (int i = 0; i < maxLevels; i++)
+ newImages[f][i] = images[f][i];
+ }
+ }
+ images = newImages;
+ }
+
+ /**
+ * Retrieves current mipmap mode.
+ * @return current mipmap mode of this texture object.
+ * @exception RestrictedAccessException if the method is called
+ */
+ final int getMipMapMode() {
+ return this.mipmapMode;
+ }
+
+ /**
+ * Enables or disables texture mapping for this
+ * appearance component object.
+ * @param state true or false to enable or disable texture mapping
+ */
+ final void initEnable(boolean state) {
+ userSpecifiedEnable = state;
+ }
+
+ /**
+ * Enables or disables texture mapping for this
+ * appearance component object and sends a
+ * message notifying the interested structures of the change.
+ * @param state true or false to enable or disable texture mapping
+ */
+ final void setEnable(boolean state) {
+
+ initEnable(state);
+
+ if (state == enable) {
+ // if enable flag is same as user specified one
+ // this is only possible when enable is false
+ // because one of the images is null and user specifies false
+ return;
+ }
+
+ enable = state;
+
+ for (int j = 0; j < numFaces && enable; j++) {
+ for (int i = baseLevel; i <= maximumLevel && enable; i++) {
+ if (images[j][i].isByReference()) {
+ if (images[j][i].bImage[0] == null) {
+ enable = false;
+ }
+ } else {
+ if (images[j][i].imageYup == null) {
+ enable = false;
+ }
+ }
+ }
+ }
+ sendMessage(ENABLE_CHANGED, (enable ? Boolean.TRUE: Boolean.FALSE));
+ }
+
+ /**
+ * Retrieves the state of the texture enable flag.
+ * @return true if texture mapping is enabled,
+ * false if texture mapping is disabled
+ */
+ final boolean getEnable() {
+ return userSpecifiedEnable;
+ }
+
+
+ final void initBaseLevel(int level) {
+ if ((level < 0) || (level > maximumLevel)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture36"));
+ }
+ baseLevel = level;
+ }
+
+
+ final void setBaseLevel(int level) {
+
+ if (level == baseLevel)
+ return;
+
+ initBaseLevel(level);
+ sendMessage(BASE_LEVEL_CHANGED, new Integer(level));
+ }
+
+ final int getBaseLevel() {
+ return baseLevel;
+ }
+
+
+ final void initMaximumLevel(int level) {
+ if ((level < baseLevel) || (level >= maxMipMapLevels)) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture37"));
+ }
+ maximumLevel = level;
+ }
+
+ final void setMaximumLevel(int level) {
+
+ if (level == maximumLevel)
+ return;
+
+ initMaximumLevel(level);
+ sendMessage(MAX_LEVEL_CHANGED, new Integer(level));
+ }
+
+ final int getMaximumLevel() {
+ return maximumLevel;
+ }
+
+ final void initMinimumLOD(float lod) {
+ if (lod > maximumLod) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture42"));
+ }
+ minimumLod = lod;
+ }
+
+ final void setMinimumLOD(float lod) {
+ initMinimumLOD(lod);
+ sendMessage(MIN_LOD_CHANGED, new Float(lod));
+ }
+
+ final float getMinimumLOD() {
+ return minimumLod;
+ }
+
+
+ final void initMaximumLOD(float lod) {
+ if (lod < minimumLod) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("Texture42"));
+ }
+ maximumLod = lod;
+ }
+
+ final void setMaximumLOD(float lod) {
+ initMaximumLOD(lod);
+ sendMessage(MAX_LOD_CHANGED, new Float(lod));
+ }
+
+ final float getMaximumLOD() {
+ return maximumLod;
+ }
+
+
+ final void initLodOffset(float s, float t, float r) {
+ if (lodOffset == null) {
+ lodOffset = new Point3f(s, t, r);
+ } else {
+ lodOffset.set(s, t, r);
+ }
+ }
+
+ final void setLodOffset(float s, float t, float r) {
+ initLodOffset(s, t, r);
+ sendMessage(LOD_OFFSET_CHANGED, new Point3f(s, t, r));
+ }
+
+ final void getLodOffset(Tuple3f offset) {
+ if (lodOffset == null) {
+ offset.set(0.0f, 0.0f, 0.0f);
+ } else {
+ offset.set(lodOffset);
+ }
+ }
+
+
+ /**
+ * Sets the texture boundary color for this texture object. The
+ * texture boundary color is used when boundaryModeS or boundaryModeT
+ * is set to CLAMP.
+ * @param boundaryColor the new texture boundary color.
+ */
+ final void initBoundaryColor(Color4f boundaryColor) {
+ this.boundaryColor.set(boundaryColor);
+ }
+
+ /**
+ * Sets the texture boundary color for this texture object. The
+ * texture boundary color is used when boundaryModeS or boundaryModeT
+ * is set to CLAMP.
+ * @param r the red component of the color.
+ * @param g the green component of the color.
+ * @param b the blue component of the color.
+ * @param a the alpha component of the color.
+ */
+ final void initBoundaryColor(float r, float g, float b, float a) {
+ this.boundaryColor.set(r, g, b, a);
+ }
+
+ /**
+ * Retrieves the texture boundary color for this texture object.
+ * @param boundaryColor the vector that will receive the
+ * current texture boundary color.
+ */
+ final void getBoundaryColor(Color4f boundaryColor) {
+ boundaryColor.set(this.boundaryColor);
+ }
+
+
+ /**
+ * Set Anisotropic Filter
+ */
+ final void initAnisotropicFilterMode(int mode) {
+ anisotropicFilterMode = mode;
+ }
+
+ final int getAnisotropicFilterMode() {
+ return anisotropicFilterMode;
+ }
+
+ final void initAnisotropicFilterDegree(float degree) {
+ anisotropicFilterDegree = degree;
+ }
+
+ final float getAnisotropicFilterDegree() {
+ return anisotropicFilterDegree;
+ }
+
+ /**
+ * Set Sharpen Texture function
+ */
+ final void initSharpenTextureFunc(float[] lod, float[] pts) {
+ if (lod == null) { // pts will be null too.
+ sharpenTextureFuncPts = null;
+ numSharpenTextureFuncPts = 0;
+ } else {
+ numSharpenTextureFuncPts = lod.length;
+ if ((sharpenTextureFuncPts == null) ||
+ (sharpenTextureFuncPts.length != lod.length * 2)) {
+ sharpenTextureFuncPts = new float[lod.length * 2];
+ }
+ for (int i = 0, j = 0; i < lod.length; i++) {
+ sharpenTextureFuncPts[j++] = lod[i];
+ sharpenTextureFuncPts[j++] = pts[i];
+ }
+ }
+ }
+
+ final void initSharpenTextureFunc(Point2f[] pts) {
+ if (pts == null) {
+ sharpenTextureFuncPts = null;
+ numSharpenTextureFuncPts = 0;
+ } else {
+ numSharpenTextureFuncPts = pts.length;
+ if ((sharpenTextureFuncPts == null) ||
+ (sharpenTextureFuncPts.length != pts.length * 2)) {
+ sharpenTextureFuncPts = new float[pts.length * 2];
+ }
+ for (int i = 0, j = 0; i < pts.length; i++) {
+ sharpenTextureFuncPts[j++] = pts[i].x;
+ sharpenTextureFuncPts[j++] = pts[i].y;
+ }
+ }
+ }
+
+ final void initSharpenTextureFunc(float[] pts) {
+ if (pts == null) {
+ sharpenTextureFuncPts = null;
+ numSharpenTextureFuncPts = 0;
+ } else {
+ numSharpenTextureFuncPts = pts.length / 2;
+ if ((sharpenTextureFuncPts == null) ||
+ (sharpenTextureFuncPts.length != pts.length)) {
+ sharpenTextureFuncPts = new float[pts.length];
+ }
+ for (int i = 0; i < pts.length; i++) {
+ sharpenTextureFuncPts[i] = pts[i];
+ }
+ }
+ }
+
+ /**
+ * Get number of points in the sharpen texture LOD function
+ */
+ final int getSharpenTextureFuncPointsCount() {
+ return numSharpenTextureFuncPts;
+ }
+
+
+ /**
+ * Copies the array of sharpen texture LOD function points into the
+ * specified arrays
+ */
+ final void getSharpenTextureFunc(float[] lod, float[] pts) {
+ if (sharpenTextureFuncPts != null) {
+ for (int i = 0, j = 0; i < numSharpenTextureFuncPts; i++) {
+ lod[i] = sharpenTextureFuncPts[j++];
+ pts[i] = sharpenTextureFuncPts[j++];
+ }
+ }
+ }
+
+ final void getSharpenTextureFunc(Point2f[] pts) {
+ if (sharpenTextureFuncPts != null) {
+ for (int i = 0, j = 0; i < numSharpenTextureFuncPts; i++) {
+ pts[i].x = sharpenTextureFuncPts[j++];
+ pts[i].y = sharpenTextureFuncPts[j++]; } }
+ }
+
+
+ final void initFilter4Func(float[] weights) {
+ if (weights == null) {
+ filter4FuncPts = null;
+ } else {
+ if ((filter4FuncPts == null) ||
+ (filter4FuncPts.length != weights.length)) {
+ filter4FuncPts = new float[weights.length];
+ }
+ for (int i = 0; i < weights.length; i++) {
+ filter4FuncPts[i] = weights[i];
+ }
+ }
+ }
+
+
+ final int getFilter4FuncPointsCount() {
+ if (filter4FuncPts == null) {
+ return 0;
+ } else {
+ return filter4FuncPts.length;
+ }
+ }
+
+ final void getFilter4Func(float[] weights) {
+ if (filter4FuncPts != null) {
+ for (int i = 0; i < filter4FuncPts.length; i++) {
+ weights[i] = filter4FuncPts[i];
+ }
+ }
+ }
+
+
+ /**
+ * internal method only -- returns internal function points
+ */
+ final float[] getSharpenTextureFunc() {
+ return sharpenTextureFuncPts;
+ }
+
+ final float[] getFilter4Func(){
+ return filter4FuncPts;
+ }
+
+
+
+
+ void setLive(boolean backgroundGroup, int refCount) {
+
+ // check the sizes of the images
+ if (images != null) {
+ for (int j = 0; j < numFaces; j++) {
+ checkSizes(images[j]);
+ }
+ }
+
+ // This line should be assigned before calling doSetLive, so that
+ // the mirror object's enable is assigned correctly!
+ enable = userSpecifiedEnable;
+
+ super.doSetLive(backgroundGroup, refCount);
+
+ // TODO: for now, do setLive for all the defined images.
+ // But in theory, we only need to setLive those within the
+ // baseLevel and maximumLevel range. But then we'll need
+ // setLive and clearLive image when the range changes.
+
+ if (images != null) {
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = 0; i < maxLevels; i++){
+ if (images[j][i] == null) {
+ throw new IllegalArgumentException(
+ J3dI18N.getString("TextureRetained3") + i);
+ }
+ images[j][i].setLive(backgroundGroup, refCount);
+ }
+ }
+ }
+
+ // Send a message to Rendering Attr stucture to update the resourceMask
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.TEXTURE_CHANGED;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(UPDATE_IMAGE);
+ createMessage.args[2] = null;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ // If the user has set enable to true, then if the image is null
+ // turn off texture enable
+ if (userSpecifiedEnable) {
+ if (images != null) {
+ for (int j = 0; j < numFaces && enable; j++) {
+ for (int i = baseLevel; i <= maximumLevel && enable; i++){
+ if (images[j][i].isByReference()) {
+ if (images[j][i].bImage[0] == null) {
+ enable = false;
+ }
+ } else {
+ if (images[j][i].imageYup == null) {
+ enable = false;
+ }
+ }
+ }
+ }
+ } else {
+ enable = false;
+ }
+ if (!enable)
+ sendMessage(ENABLE_CHANGED, Boolean.FALSE);
+ }
+
+ super.markAsLive();
+ }
+
+ void clearLive(int refCount) {
+ super.clearLive(refCount);
+
+ if (images != null) {
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = 0; i < maxLevels; i++) {
+ images[j][i].clearLive(refCount);
+ images[j][i].removeUser(mirror);
+ }
+ }
+ }
+ }
+
+ // Simply pass along to the NodeComponents
+ /**
+ * This method updates the native context. The implementation for 2D
+ * texture mapping happens here. Texture3D implements its own version
+ * of this.
+ */
+ native void bindTexture(long ctx, int objectId, boolean enable);
+
+ native void updateTextureFilterModes(long ctx,
+ int minFilter, int magFilter);
+
+ native void updateTextureLodRange(long ctx,
+ int baseLevel, int maximumLevel,
+ float minimumLod, float maximumLod);
+
+ native void updateTextureLodOffset(long ctx,
+ float lodOffsetX, float lodOffsetY,
+ float lodOffsetZ);
+
+
+ native void updateTextureBoundary(long ctx,
+ int boundaryModeS, int boundaryModeT,
+ float boundaryRed, float boundaryGreen,
+ float boundaryBlue, float boundaryAlpha);
+
+ native void updateTextureSharpenFunc(long ctx,
+ int numSharpenTextureFuncPts,
+ float[] sharpenTextureFuncPts);
+
+ native void updateTextureFilter4Func(long ctx,
+ int numFilter4FuncPts,
+ float[] filter4FuncPts);
+
+ native void updateTextureAnisotropicFilter(long ctx, float degree);
+
+ native void updateTextureImage(long ctx,
+ int numLevels, int level,
+ int format, int storedFormat,
+ int width, int height,
+ int boundaryWidth, byte[] data);
+
+ native void updateTextureSubImage(long ctx, int level,
+ int xoffset, int yoffset, int format,
+ int storedFormat, int imgXOffset,
+ int imgYOffset, int tileWidth,
+ int width, int height, byte[] data);
+
+
+ // get an ID for Texture 2D
+ int getTextureId() {
+ return (VirtualUniverse.mc.getTexture2DId());
+ }
+
+
+ // free a Texture2D id
+ void freeTextureId(int id) {
+ synchronized (resourceLock) {
+ if (objectId == id) {
+ objectId = -1;
+ VirtualUniverse.mc.freeTexture2DId(id);
+ }
+ }
+ }
+
+
+ // bind a named texture to a texturing target
+
+ void bindTexture(Canvas3D cv) {
+ synchronized(resourceLock) {
+ if (objectId == -1) {
+ objectId = getTextureId();
+ }
+ cv.addTextureResource(objectId, this);
+ }
+ bindTexture(cv.ctx, objectId, enable);
+ }
+
+
+ /**
+ * load level 0 explicitly with null pointer to enable
+ * mipmapping when level 0 is not the base level
+ */
+ void updateTextureDimensions(Canvas3D cv) {
+ updateTextureImage(cv.ctx, maxLevels, 0,
+ format, ImageComponentRetained.BYTE_RGBA,
+ width, height, boundaryWidth, null);
+ }
+
+
+ void updateTextureLOD(Canvas3D cv) {
+
+ if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_LOD_RANGE) != 0 ) {
+ updateTextureLodRange(cv.ctx, baseLevel, maximumLevel,
+ minimumLod, maximumLod);
+ }
+
+ if ((lodOffset != null) &&
+ ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_LOD_OFFSET) != 0)) {
+ updateTextureLodOffset(cv.ctx,
+ lodOffset.x, lodOffset.y, lodOffset.z);
+ }
+ }
+
+
+ void updateTextureBoundary(Canvas3D cv) {
+ updateTextureBoundary(cv.ctx, boundaryModeS, boundaryModeT,
+ boundaryColor.x, boundaryColor.y,
+ boundaryColor.z, boundaryColor.w);
+ }
+
+
+ void updateTextureFields(Canvas3D cv) {
+
+ int magnificationFilter = magFilter;
+ int minificationFilter = minFilter;
+
+ // update sharpen texture function if applicable
+
+ if ((magFilter >= Texture.LINEAR_SHARPEN) &&
+ (magFilter <= Texture.LINEAR_SHARPEN_ALPHA)) {
+
+ if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_SHARPEN) != 0 ) {
+
+ // send down sharpen texture LOD function
+ //
+ updateTextureSharpenFunc(cv.ctx,
+ numSharpenTextureFuncPts, sharpenTextureFuncPts);
+
+ } else {
+
+ // sharpen texture is not supported by the underlying
+ // library, fallback to BASE_LEVEL_LINEAR
+
+ magnificationFilter = Texture.BASE_LEVEL_LINEAR;
+ }
+ } else if ((magFilter >= Texture2D.LINEAR_DETAIL) &&
+ (magFilter <= Texture2D.LINEAR_DETAIL_ALPHA)) {
+ if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_DETAIL) == 0) {
+
+ // detail texture is not supported by the underlying
+ // library, fallback to BASE_LEVEL_LINEAR
+
+ magnificationFilter = Texture.BASE_LEVEL_LINEAR;
+ }
+ }
+
+ if (minFilter == Texture.FILTER4 || magFilter == Texture.FILTER4) {
+
+ boolean noFilter4 = false;
+
+ if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_FILTER4) != 0) {
+
+ if (filter4FuncPts == null) {
+
+ // filter4 function is not defined,
+ // fallback to BASE_LEVEL_LINEAR
+
+ noFilter4 = true;
+ } else {
+ updateTextureFilter4Func(cv.ctx, filter4FuncPts.length,
+ filter4FuncPts);
+ }
+ } else {
+
+ // filter4 is not supported by the underlying
+ // library, fallback to BASE_LEVEL_LINEAR
+
+ noFilter4 = true;
+ }
+
+ if (noFilter4) {
+ if (minFilter == Texture.FILTER4) {
+ minificationFilter = Texture.BASE_LEVEL_LINEAR;
+ }
+ if (magFilter == Texture.FILTER4) {
+ magnificationFilter = Texture.BASE_LEVEL_LINEAR;
+ }
+ }
+ }
+
+ // update texture filtering modes
+
+ updateTextureFilterModes(cv.ctx, minificationFilter,
+ magnificationFilter);
+
+ if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_ANISOTROPIC_FILTER)
+ != 0) {
+ if (anisotropicFilterMode == Texture.ANISOTROPIC_NONE) {
+ updateTextureAnisotropicFilter(cv.ctx, 1.0f);
+ } else {
+ updateTextureAnisotropicFilter(cv.ctx, anisotropicFilterDegree);
+ }
+ }
+
+ // update texture boundary modes, boundary color
+
+ updateTextureBoundary(cv);
+
+ }
+
+
+
+ // wrapper of the native call
+
+ void updateTextureImage(Canvas3D cv, int face,
+ int numLevels, int level,
+ int format, int storedFormat,
+ int width, int height,
+ int boundaryWidth, byte[] data) {
+
+ updateTextureImage(cv.ctx, maxLevels, level,
+ format, storedFormat,
+ width, height, boundaryWidth, data);
+ }
+
+
+
+ // wrapper of the native call
+
+ void updateTextureSubImage(Canvas3D cv, int face, int level,
+ int xoffset, int yoffset, int format,
+ int storedFormat, int imgXOffset,
+ int imgYOffset, int tileWidth,
+ int width, int height, byte[] data) {
+
+ updateTextureSubImage(cv.ctx, level,
+ xoffset, yoffset, format,
+ storedFormat, imgXOffset,
+ imgYOffset, tileWidth,
+ width, height, data);
+ }
+
+
+
+ /**
+ * reloadTextureImage is used to load a particular level of image
+ * This method needs to take care of RenderedImage as well as
+ * BufferedImage
+ */
+ void reloadTextureImage(Canvas3D cv, int face, int level,
+ ImageComponentRetained image, int numLevels) {
+
+ //System.out.println("Texture.reloadTextureImage: face= " + face + " level= " + level);
+
+ //System.out.println("...image = "+image+" image.storedFormat = "+image.storedYupFormat+" image.imageYup = "+image.imageYup+" texture - "+this);
+
+ //System.out.println("....imageYupAllocated= " + image.imageYupAllocated);
+
+ updateTextureImage(cv, face, numLevels, level, format,
+ image.storedYupFormat,
+ image.width, image.height,
+ boundaryWidth, image.imageYup);
+
+ // Now take care of the RenderedImage case. Note, if image
+ // is a RenderedImage, then imageYup will be null
+
+ if (image.imageYupClass == ImageComponentRetained.RENDERED_IMAGE) {
+
+ // System.out.println("==========. subImage");
+ // Download all the tiles for this texture
+ int xoffset = 0, yoffset = 0;
+ int tmpw = image.width;
+ int tmph = image.height;
+ int endXTile = image.minTileX * image.tilew + image.tileGridXOffset+image.tilew;
+ int endYTile = image.minTileY * image.tileh + image.tileGridYOffset+image.tileh;
+ int curw = (endXTile - image.minX);
+ int curh = (endYTile - image.minY);
+
+ if (tmpw < curw) {
+ curw = tmpw;
+ }
+
+ if (tmph < curh) {
+ curh = tmph;
+ }
+
+ int startw = curw;
+ int imageXOffset = image.tilew - curw;
+ int imageYOffset = image.tileh - curh;
+ for (int m = image.minTileY; m < image.minTileY+image.numYTiles; m++) {
+ xoffset = 0;
+ tmpw = width;
+ curw = startw;
+ imageXOffset = image.tilew - curw;
+ for (int n = image.minTileX;
+ n < image.minTileX+image.numXTiles; n++) {
+ java.awt.image.Raster ras;
+ ras = image.bImage[0].getTile(n,m);
+ byte[] tmpImage = ((DataBufferByte)ras.getDataBuffer()).getData();
+ updateTextureSubImage(cv, face,
+ level, xoffset, yoffset, format,
+ image.storedYupFormat,
+ imageXOffset, imageYOffset,
+ image.tilew,
+ curw, curh,
+ tmpImage);
+ xoffset += curw;
+ imageXOffset = 0;
+ tmpw -= curw;
+ if (tmpw < image.tilew)
+ curw = tmpw;
+ else
+ curw = image.tilew;
+ }
+ yoffset += curh;
+ imageYOffset = 0;
+ tmph -= curh;
+ if (tmph < image.tileh)
+ curh = tmph;
+ else
+ curh = image.tileh;
+ }
+ }
+ }
+
+
+ /**
+ * update a subregion of the texture image
+ * This method needs to take care of RenderedImage as well as
+ * BufferedImage
+ */
+ void reloadTextureSubImage(Canvas3D cv, int face, int level,
+ ImageComponentUpdateInfo info,
+ ImageComponentRetained image) {
+
+ int x = info.x,
+ y = info.y,
+ width = info.width,
+ height = info.height;
+
+ //The x and y here specifies the subregion of the imageData of
+ //the associated RenderedImage.
+
+ //System.out.println("\nupdateTextureSubImage: x= " + x + " y= " + y +
+ // " width= " + width + " height= " + height +
+ // " format= " + format);
+
+
+ if (image.imageYupClass == ImageComponentRetained.BUFFERED_IMAGE) {
+
+ int xoffset = x - image.minX;
+ int yoffset = y - image.minY;
+
+ byte[] imageData;
+ if (image.imageYupAllocated) {
+ imageData = image.imageYup;
+ yoffset = image.height - yoffset - height;
+
+ } else {
+ imageData = ((DataBufferByte)
+ image.bImage[0].getData().getDataBuffer()).getData();
+
+ // based on the yUp flag in the associated ImageComponent,
+ // adjust the yoffset
+
+ if (!image.yUp) {
+ yoffset = image.height - yoffset - height;
+ }
+ }
+
+ updateTextureSubImage(cv, face, level,
+ xoffset, yoffset,
+ format, image.storedYupFormat,
+ xoffset, yoffset,
+ image.width, width, height, imageData);
+ } else {
+
+ // System.out.println("RenderedImage subImage update");
+
+ // determine the first tile of the image
+
+ float mt;
+ int xoff = image.tileGridXOffset;
+ int yoff = image.tileGridYOffset;
+ int minTileX, minTileY;
+
+ int rx = x + image.minX; // x offset in RenderedImage
+ int ry = y + image.minY; // y offset in RenderedImage
+
+ mt = (float)(rx - xoff) / (float)image.tilew;
+ if (mt < 0) {
+ minTileX = (int)(mt - 1);
+ } else {
+ minTileX = (int)mt;
+ }
+
+ mt = (float)(ry - yoff) / (float)image.tileh;
+ if (mt < 0) {
+ minTileY = (int)(mt - 1);
+ } else {
+ minTileY = (int)mt;
+ }
+
+ // determine the pixel offset of the upper-left corner of the
+ // first tile
+ int startXTile = minTileX * image.tilew + xoff;
+ int startYTile = minTileY * image.tilew + yoff;
+
+
+ // image dimension in the first tile
+
+ int curw = (startXTile + image.tilew - rx);
+ int curh = (startYTile + image.tileh - ry);
+
+ // check if the to-be-copied region is less than the tile image
+ // if so, update the to-be-copied dimension of this tile
+
+ if (curw > width) {
+ curw = width;
+ }
+
+ if (curh > height) {
+ curh = height;
+ }
+
+ // save the to-be-copied width of the left most tile
+
+ int startw = curw;
+
+
+ // temporary variable for dimension of the to-be-copied region
+
+ int tmpw = width;
+ int tmph = height;
+
+
+ // offset of the first pixel of the tile to be copied; offset is
+ // relative to the upper left corner of the title
+
+ int imgX = rx - startXTile;
+ int imgY = ry - startYTile;
+
+
+ // determine the number of tiles in each direction that the
+ // image spans
+
+ int numXTiles = (width + imgX) / image.tilew;
+ int numYTiles = (height + imgY) / image.tileh;
+
+ if (((float)(width + imgX ) % (float)image.tilew) > 0) {
+ numXTiles += 1;
+ }
+
+ if (((float)(height + imgY ) % (float)image.tileh) > 0) {
+ numYTiles += 1;
+ }
+
+ java.awt.image.Raster ras;
+ byte[] imageData;
+
+ int textureX = x; // x offset in the texture
+ int textureY = y; // y offset in the texture
+
+ for (int yTile = minTileY; yTile < minTileY + numYTiles;
+ yTile++) {
+
+ tmpw = width;
+ curw = startw;
+ imgX = rx - startXTile;
+
+ for (int xTile = minTileX; xTile < minTileX + numXTiles;
+ xTile++) {
+ ras = image.bImage[0].getTile(xTile, yTile);
+ imageData = ((DataBufferByte)ras.getDataBuffer()).getData();
+
+ updateTextureSubImage(cv, face, level,
+ textureX, textureY,
+ format, image.storedYupFormat,
+ imgX, imgY, image.tilew, curw, curh, imageData);
+
+
+ // move to the next tile in x direction
+
+ textureX += curw;
+ imgX = 0;
+
+ // determine the width of copy region of the next tile
+
+ tmpw -= curw;
+ if (tmpw < image.tilew) {
+ curw = tmpw;
+ } else {
+ curw = image.tilew;
+ }
+ }
+
+ // move to the next set of tiles in y direction
+ textureY += curh;
+ imgY = 0;
+
+ // determine the height of copy region for the next set
+ // of tiles
+ tmph -= curh;
+ if (tmph < image.tileh) {
+ curh = tmph;
+ } else {
+ curh = image.tileh;
+ }
+ }
+ }
+ }
+
+
+ // reload texture mipmap
+
+ void reloadTexture(Canvas3D cv) {
+
+
+ int blevel, mlevel;
+
+ //System.out.println("reloadTexture: baseLevel= " + baseLevel +
+ // " maximumLevel= " + maximumLevel);
+
+ if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_LOD_RANGE) == 0 ) {
+ blevel = 0;
+ mlevel = maxLevels - 1;
+ } else {
+ blevel = baseLevel;
+ mlevel = maximumLevel;
+ }
+
+ if (blevel != 0) {
+ // level 0 is not the base level, hence, need
+ // to load level 0 explicitly with a null pointer in order
+ // for mipmapping to be active.
+
+ updateTextureDimensions(cv);
+ }
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = blevel; i <= mlevel; i++) {
+
+ // it is possible to have null pointer if only a subset
+ // of mipmap levels is defined but the canvas does not
+ // support lod_range extension
+
+ if (images[j][i] != null) {
+ reloadTextureImage(cv, j, i, images[j][i], maxLevels);
+ }
+ }
+ }
+ }
+
+
+ // update texture mipmap based on the imageUpdateInfo
+
+ void updateTexture(Canvas3D cv, int resourceBit) {
+
+ //System.out.println("updateTexture\n");
+
+ ImageComponentUpdateInfo info;
+
+ for (int k = 0; k < numFaces; k++) {
+ for (int i = baseLevel; i <= maximumLevel; i++) {
+ if (imageUpdateInfo[k][i] != null) {
+ for (int j = 0; j < imageUpdateInfo[k][i].size(); j++) {
+
+ info = (ImageComponentUpdateInfo)
+ imageUpdateInfo[k][i].get(j);
+
+
+ synchronized(resourceLock) {
+
+ // if this info is updated, move on to the next one
+
+ if ((info.updateMask & resourceBit) == 0)
+ continue;
+
+
+ // check if all canvases have processed this update
+ info.updateMask &= ~resourceBit;
+
+ // all the current resources have updated this
+ // info, so this info can be removed from the
+ // update list
+ if ((info.updateMask & resourceCreationMask)
+ == 0) {
+ info.updateMask = 0; // mark this update as
+ // done
+
+ // mark the prune flag so as to prune the
+ // update list next time when the update
+ // list is to be modified.
+ // Don't want to clean up the list at
+ // rendering time because (1) MT issue,
+ // other renderer could be processing
+ // the update list now;
+ // (2) takes up rendering time.
+ if (imageUpdatePruneMask == null) {
+ imageUpdatePruneMask = new int[numFaces];
+ }
+ imageUpdatePruneMask[k] = 1 << i;
+ }
+ }
+
+ if (info.entireImage == true) {
+ reloadTextureImage(cv, k, i,
+ images[k][i], maxLevels);
+ } else {
+ reloadTextureSubImage(cv, k, i, info, images[k][i]);
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * reloadTextureSharedContext is called to reload texture
+ * on a shared context. It is invoked from the Renderer
+ * before traversing the RenderBin. The idea is to reload
+ * all necessary textures up front for all shared contexts
+ * in order to minimize the context switching overhead.
+ */
+ void reloadTextureSharedContext(Canvas3D cv) {
+
+ // if texture is not enabled, don't bother downloading the
+ // the texture state
+
+ if (enable == false) {
+ return;
+ }
+
+ bindTexture(cv);
+
+ // reload all levels of texture image
+
+ // update texture parameters such as boundary modes, filtering
+
+ updateTextureFields(cv);
+
+
+ // update texture Lod parameters
+
+ updateTextureLOD(cv);
+
+
+ // update all texture images
+
+ reloadTexture(cv);
+
+ synchronized(resourceLock) {
+ resourceCreationMask |= cv.screen.renderer.rendererBit;
+ resourceUpdatedMask |= cv.screen.renderer.rendererBit;
+ resourceLodUpdatedMask |= cv.screen.renderer.rendererBit;
+ resourceInReloadList &= ~cv.screen.renderer.rendererBit;
+ }
+ }
+
+
+ /**
+ * updateNative is called while traversing the RenderBin to
+ * update the texture state
+ */
+ void updateNative(Canvas3D cv) {
+ boolean reloadTexture = false; // true - reload all levels of texture
+ boolean updateTexture = false; // true - update a portion of texture
+ boolean updateTextureLod = false; // true - update texture Lod info
+
+ //System.out.println("Texture/updateNative: " + this + "object= " + objectId + " enable= " + enable);
+
+ bindTexture(cv);
+
+ // if texture is not enabled, don't bother downloading the
+ // the texture state
+
+ if (enable == false) {
+ return;
+ }
+
+ if (cv.useSharedCtx && cv.screen.renderer.sharedCtx != 0) {
+
+ if ((resourceCreationMask & cv.screen.renderer.rendererBit) == 0) {
+ reloadTexture = true;
+ } else {
+ if (((resourceUpdatedMask &
+ cv.screen.renderer.rendererBit) == 0) &&
+ (imageUpdateInfo != null)) {
+ updateTexture = true;
+ }
+
+ if ((resourceLodUpdatedMask &
+ cv.screen.renderer.rendererBit) == 0) {
+ updateTextureLod = true;
+ }
+ }
+ if (reloadTexture || updateTexture || updateTextureLod) {
+ cv.makeCtxCurrent(cv.screen.renderer.sharedCtx);
+ bindTexture(cv);
+ }
+ } else {
+ if ((resourceCreationMask & cv.canvasBit) == 0) {
+ reloadTexture = true;
+ } else {
+ if (((resourceUpdatedMask & cv.canvasBit) == 0) &&
+ (imageUpdateInfo != null)) {
+ updateTexture = true;
+ }
+
+ if ((resourceLodUpdatedMask & cv.canvasBit) == 0) {
+ updateTextureLod = true;
+ }
+ }
+ }
+
+
+ if (VirtualUniverse.mc.isD3D()) {
+ if (texTimestamp != VirtualUniverse.mc.resendTexTimestamp) {
+ texTimestamp = VirtualUniverse.mc.resendTexTimestamp;
+ reloadTexture = true;
+ }
+
+ if (!reloadTexture) {
+ // D3D didn't store texture properties during Texture binding
+ updateTextureFields(cv);
+ }
+ }
+
+
+//System.out.println("......... reloadTexture= " + reloadTexture +
+// " updateTexture= " + updateTexture +
+// " updateTextureLod= " + updateTextureLod);
+
+//System.out.println("......... resourceCreationMask= " + resourceCreationMask +
+// " resourceUpdatedMask= " + resourceUpdatedMask);
+
+ if (reloadTexture) {
+
+ // reload all levels of texture image
+
+ // update texture parameters such as boundary modes, filtering
+
+ updateTextureFields(cv);
+
+
+ // update texture Lod parameters
+
+ updateTextureLOD(cv);
+
+
+ // update all texture images
+
+ reloadTexture(cv);
+
+
+ if (cv.useSharedCtx) {
+ cv.makeCtxCurrent(cv.ctx);
+ synchronized(resourceLock) {
+ resourceCreationMask |= cv.screen.renderer.rendererBit;
+ resourceUpdatedMask |= cv.screen.renderer.rendererBit;
+ resourceLodUpdatedMask |= cv.screen.renderer.rendererBit;
+ }
+ }
+ else {
+ synchronized(resourceLock) {
+ resourceCreationMask |= cv.canvasBit;
+ resourceUpdatedMask |= cv.canvasBit;
+ resourceLodUpdatedMask |= cv.canvasBit;
+ }
+ }
+ } else if (updateTextureLod || updateTexture) {
+
+ if (updateTextureLod) {
+ updateTextureLOD(cv);
+ }
+
+ if (updateTexture) {
+
+ // update texture image
+
+ int resourceBit = 0;
+
+ if (cv.useSharedCtx) {
+ resourceBit = cv.screen.renderer.rendererBit;
+ } else {
+ resourceBit = cv.canvasBit;
+ }
+
+ // update texture based on the imageComponent update info
+
+ updateTexture(cv, resourceBit);
+ }
+
+ // set the appropriate bit in the resource update masks showing
+ // that the resource is up-to-date
+
+ if (cv.useSharedCtx) {
+ cv.makeCtxCurrent(cv.ctx);
+ synchronized(resourceLock) {
+ resourceUpdatedMask |= cv.screen.renderer.rendererBit;
+ resourceLodUpdatedMask |= cv.screen.renderer.rendererBit;
+ }
+ } else {
+ synchronized(resourceLock) {
+ resourceUpdatedMask |= cv.canvasBit;
+ resourceLodUpdatedMask |= cv.canvasBit;
+ }
+ }
+ }
+ }
+
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ if (this instanceof Texture3DRetained) {
+ Texture3DRetained t3d = (Texture3DRetained)this;
+ Texture3D tex = new Texture3D(t3d.mipmapMode,
+ t3d.format,
+ t3d.width,
+ t3d.height,
+ t3d.depth,
+ t3d.boundaryWidth);
+ mirror = (Texture3DRetained)tex.retained;;
+
+ } else if (this instanceof TextureCubeMapRetained) {
+ TextureCubeMap tex = new TextureCubeMap(mipmapMode,
+ format, width,
+ boundaryWidth);
+ mirror = (TextureCubeMapRetained)tex.retained;
+
+ } else {
+ Texture2D tex = new Texture2D(mipmapMode,
+ format,
+ width,
+ height,
+ boundaryWidth);
+ mirror = (Texture2DRetained)tex.retained;;
+ }
+
+ ((TextureRetained)mirror).objectId = -1;
+ }
+ initMirrorObject();
+ }
+
+
+ /**
+ * Initializes a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ mirror.source = source;
+ if (this instanceof Texture3DRetained) {
+ Texture3DRetained t3d = (Texture3DRetained)this;
+
+ ((Texture3DRetained)mirror).boundaryModeR = t3d.boundaryModeR;
+ ((Texture3DRetained)mirror).depth = t3d.depth;
+ }
+ TextureRetained mirrorTexture = (TextureRetained)mirror;
+
+ mirrorTexture.boundaryModeS = boundaryModeS;
+ mirrorTexture.boundaryModeT = boundaryModeT;
+ mirrorTexture.minFilter = minFilter;
+ mirrorTexture.magFilter = magFilter;
+ mirrorTexture.boundaryColor.set(boundaryColor);
+ mirrorTexture.enable = enable;
+ mirrorTexture.userSpecifiedEnable = enable;
+ mirrorTexture.imagesLoaded = imagesLoaded;
+ mirrorTexture.enable = enable;
+ mirrorTexture.numFaces = numFaces;
+ mirrorTexture.resourceCreationMask = 0x0;
+ mirrorTexture.resourceUpdatedMask = 0x0;
+ mirrorTexture.resourceLodUpdatedMask = 0x0;
+ mirrorTexture.resourceInReloadList = 0x0;
+
+ // LOD information
+ mirrorTexture.baseLevel = baseLevel;
+ mirrorTexture.maximumLevel = maximumLevel;
+ mirrorTexture.minimumLod = minimumLod;
+ mirrorTexture.maximumLod = maximumLod;
+ mirrorTexture.lodOffset = lodOffset;
+
+ // sharpen texture LOD function
+
+ mirrorTexture.numSharpenTextureFuncPts = numSharpenTextureFuncPts;
+ if (sharpenTextureFuncPts == null) {
+ mirrorTexture.sharpenTextureFuncPts = null;
+ } else {
+ if ((mirrorTexture.sharpenTextureFuncPts == null) ||
+ (mirrorTexture.sharpenTextureFuncPts.length !=
+ sharpenTextureFuncPts.length)) {
+ mirrorTexture.sharpenTextureFuncPts =
+ new float[sharpenTextureFuncPts.length];
+ }
+ for (int i = 0; i < sharpenTextureFuncPts.length; i++) {
+ mirrorTexture.sharpenTextureFuncPts[i] =
+ sharpenTextureFuncPts[i];
+ }
+ }
+
+ // filter4 function
+ if (filter4FuncPts == null) {
+ mirrorTexture.filter4FuncPts = null;
+ } else {
+ if ((mirrorTexture.filter4FuncPts == null) ||
+ (mirrorTexture.filter4FuncPts.length !=
+ filter4FuncPts.length)) {
+ mirrorTexture.filter4FuncPts =
+ new float[filter4FuncPts.length];
+ }
+ for (int i = 0; i < filter4FuncPts.length; i++) {
+ mirrorTexture.filter4FuncPts[i] =
+ filter4FuncPts[i];
+ }
+ }
+
+ // Anisotropic Filter
+ mirrorTexture.anisotropicFilterMode = anisotropicFilterMode;
+ mirrorTexture.anisotropicFilterDegree = anisotropicFilterDegree;
+
+ // implicit mipmap generation
+ if (mipmapMode == Texture.BASE_LEVEL &&
+ (minFilter == Texture.NICEST ||
+ minFilter == Texture.MULTI_LEVEL_POINT ||
+ minFilter == Texture.MULTI_LEVEL_LINEAR)) {
+ mirrorTexture.maxLevels = maxMipMapLevels;
+
+ if ((mirrorTexture.images == null) ||
+ (mirrorTexture.images.length < numFaces) ||
+ (mirrorTexture.images[0].length < mirrorTexture.maxLevels)) {
+ mirrorTexture.images =
+ new ImageComponentRetained[numFaces][mirrorTexture.maxLevels];
+ }
+
+ for (int j = 0; j < numFaces; j++) {
+ mirrorTexture.images[j][0] = images[j][0];
+
+ // add texture to the userList of the images
+ if (images[j][0] != null) {
+ images[j][0].addUser(mirrorTexture);
+ }
+
+ for (int i = 1; i < mirrorTexture.maxLevels; i++) {
+ mirrorTexture.images[j][i] = createNextLevelImage(
+ (mirrorTexture.images[j][i-1]));
+ }
+ }
+ }
+ else {
+ mirrorTexture.maxLevels = maxLevels;
+ if (images != null) {
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = 0; i < maxLevels; i++) {
+ mirrorTexture.images[j][i] = images[j][i];
+
+ // add texture to the userList of the images
+ if (images[j][i] != null) {
+ images[j][i].addUser(mirrorTexture);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Go through the image update info list
+ * and remove those that are already done
+ * by all the resources
+ */
+ void pruneImageUpdateInfo() {
+ ImageComponentUpdateInfo info;
+
+ //System.out.println("Texture.pruneImageUpdateInfo");
+
+ for (int k = 0; k < numFaces; k++) {
+ for (int i = baseLevel; i <= maximumLevel; i++) {
+ if ((imageUpdatePruneMask[k] & (1<<i)) != 0) {
+ if (imageUpdateInfo[k][i] != null) {
+ for (int j = 0; j < imageUpdateInfo[k][i].size(); j++) {
+ info = (ImageComponentUpdateInfo)
+ imageUpdateInfo[k][i].get(j);
+ if (info.updateMask == 0) {
+ // this update info is done, remove it
+ // from the update list
+ VirtualUniverse.mc.addFreeImageUpdateInfo(info);
+ imageUpdateInfo[k][i].remove(j);
+ }
+ }
+ }
+ imageUpdatePruneMask[k] &= ~(1<<i);
+ }
+ }
+ }
+ }
+
+ /**
+ * addImageUpdateInfo(int level) is to update a particular level.
+ * In this case, it supercedes all the subImage update for this level,
+ * and all those update info can be removed from the update list.
+ *
+ * Note: this method is called from mirror only
+ */
+ void addImageUpdateInfo(int level, int face, ImageComponentUpdateInfo arg) {
+
+ ImageComponentUpdateInfo info;
+
+ if (imageUpdateInfo == null) {
+ imageUpdateInfo = new ArrayList[numFaces][maxLevels];
+ }
+
+ if (imageUpdateInfo[face][level] == null) {
+ imageUpdateInfo[face][level] = new ArrayList();
+ }
+
+ //info = mirrorTa.getFreeImageUpdateInfo();
+ info = VirtualUniverse.mc.getFreeImageUpdateInfo();
+
+
+ if (arg == null) {
+ // no subimage info, so the entire image is to be updated
+ info.entireImage = true;
+
+ } else if ((arg.width >= width/2) && (arg.height >= height/2)) {
+
+ // if the subimage dimension is close to the complete dimension,
+ // use the full update (it's more efficient)
+ info.entireImage = true;
+ } else {
+ info.entireImage = false;
+ }
+
+ if (info.entireImage) {
+ // the entire image update supercedes all the subimage update;
+ // hence, remove all the existing updates from the list
+ VirtualUniverse.mc.addFreeImageUpdateInfo(
+ imageUpdateInfo[face][level]);
+ imageUpdateInfo[face][level].clear();
+
+ // reset the update prune mask for this level
+ if (imageUpdatePruneMask != null) {
+ imageUpdatePruneMask[face] &= ~(1 << level);
+ }
+
+ } else {
+ // subimage update, needs to save the subimage info
+ info.x = arg.x;
+ info.y = arg.y;
+ info.z = arg.z;
+ info.width = arg.width;
+ info.height = arg.height;
+ }
+
+ // save the mask which shows the canvases that have created resources
+ // for this image, aka, these are the resources that need to be
+ // updated.
+ info.updateMask = resourceCreationMask;
+
+ // add the image update to the list
+ imageUpdateInfo[face][level].add(info);
+
+ // check if the update list stills need to be pruned
+ if (imageUpdatePruneMask != null) {
+ pruneImageUpdateInfo();
+ }
+ }
+
+ void validate() {
+ enable = true;
+ for (int j = 0; j < numFaces && enable; j++) {
+ for (int i = baseLevel; i <= maximumLevel && enable; i++) {
+ if (images[j][i] == null) {
+ enable = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ TextureRetained mirrorTexture = (TextureRetained)mirror;
+
+ if ((component & ENABLE_CHANGED) != 0) {
+ mirrorTexture.enable = ((Boolean)value).booleanValue();
+
+ } else if ((component & IMAGE_CHANGED) != 0) {
+
+ Object [] arg = (Object []) value;
+ int level = ((Integer)arg[0]).intValue();
+ ImageComponent image = (ImageComponent)arg[1];
+ int face = ((Integer)arg[2]).intValue();
+
+ // first remove texture from the userList of the current
+ // referencing image and
+
+ if (mirrorTexture.images[face][level] != null) {
+ mirrorTexture.images[face][level].removeUser(mirror);
+ }
+
+ // assign the new image and add texture to the userList
+ if (image == null) {
+ mirrorTexture.images[face][level] = null;
+
+ } else {
+ mirrorTexture.images[face][level] =
+ (ImageComponentRetained)image.retained;
+ mirrorTexture.images[face][level].addUser(mirror);
+
+ }
+
+ // NOTE: the old image has to be removed from the
+ // renderBins' NodeComponentList and new image has to be
+ // added to the lists. This will be taken care of
+ // in the RenderBin itself in response to the
+ // IMAGE_CHANGED message
+
+
+ // mark that texture images need to be updated
+ mirrorTexture.resourceUpdatedMask = 0;
+
+ // add update info to the update list
+ mirrorTexture.addImageUpdateInfo(level, face, null);
+
+ } else if ((component & IMAGES_CHANGED) != 0) {
+
+ Object [] arg = (Object []) value;
+ ImageComponent [] images = (ImageComponent[])arg[0];
+ int face = ((Integer)arg[1]).intValue();
+
+ for (int i = 0; i < images.length; i++) {
+
+ // first remove texture from the userList of the current
+ // referencing image
+ if (mirrorTexture.images[face][i] != null) {
+ mirrorTexture.images[face][i].removeUser(mirror);
+ }
+
+ // assign the new image and add texture to the userList
+ if (images[i] == null) {
+ mirrorTexture.images[face][i] = null;
+ } else {
+ mirrorTexture.images[face][i] =
+ (ImageComponentRetained)images[i].retained;
+ mirrorTexture.images[face][i].addUser(mirror);
+ }
+ }
+ mirrorTexture.updateResourceCreationMask();
+
+ // NOTE: the old images have to be removed from the
+ // renderBins' NodeComponentList and new image have to be
+ // added to the lists. This will be taken care of
+ // in the RenderBin itself in response to the
+ // IMAGES_CHANGED message
+
+ } else if ((component & BASE_LEVEL_CHANGED) != 0) {
+ int level = ((Integer)value).intValue();
+
+ if (level < mirrorTexture.baseLevel) {
+
+ // add texture to the userList of those new levels of
+ // enabling images
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = level; i < mirrorTexture.baseLevel; i++) {
+
+ if (mirrorTexture.images[j][i] == null) {
+ mirrorTexture.enable = false;
+ } else {
+ mirrorTexture.addImageUpdateInfo(i, j, null);
+ }
+ }
+ }
+
+ mirrorTexture.baseLevel = level;
+
+ // mark that texture images need to be updated
+ mirrorTexture.resourceUpdatedMask = 0;
+
+
+ } else {
+
+ mirrorTexture.baseLevel = level;
+
+ if (userSpecifiedEnable && (mirrorTexture.enable == false)) {
+
+ // if texture is to be enabled but is currently
+ // disabled, it's probably disabled because
+ // some of the images are missing. Now that
+ // the baseLevel is modified, validate the
+ // texture images again
+
+ mirrorTexture.validate();
+ }
+ }
+
+ // mark that texture lod info needs to be updated
+ mirrorTexture.resourceLodUpdatedMask = 0;
+
+ } else if ((component & MAX_LEVEL_CHANGED) != 0) {
+ int level = ((Integer)value).intValue();
+
+ if (level > mirrorTexture.maximumLevel) {
+
+ // add texture to the userList of those new levels of
+ // enabling images
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = mirrorTexture.maximumLevel; i < level; i++) {
+
+ if (mirrorTexture.images[j][i] == null) {
+ mirrorTexture.enable = false;
+ } else {
+ mirrorTexture.addImageUpdateInfo(i, j, null);
+ }
+ }
+ }
+
+ mirrorTexture.maximumLevel = level;
+
+ // mark that texture images need to be updated
+ mirrorTexture.resourceUpdatedMask = 0;
+
+ } else {
+
+ mirrorTexture.maximumLevel = level;
+
+ if (userSpecifiedEnable && (mirrorTexture.enable == false)) {
+
+ // if texture is to be enabled but is currently
+ // disabled, it's probably disabled because
+ // some of the images are missing. Now that
+ // the baseLevel is modified, validate the
+ // texture images again
+
+ mirrorTexture.validate();
+ }
+ }
+
+ // mark that texture lod info needs to be updated
+ mirrorTexture.resourceLodUpdatedMask = 0;
+
+ } else if ((component & MIN_LOD_CHANGED) != 0) {
+ mirrorTexture.minimumLod = ((Float)value).floatValue();
+
+ // mark that texture lod info needs to be updated
+ mirrorTexture.resourceLodUpdatedMask = 0;
+
+ } else if ((component & MAX_LOD_CHANGED) != 0) {
+ mirrorTexture.maximumLod = ((Float)value).floatValue();
+
+ // mark that texture lod info needs to be updated
+ mirrorTexture.resourceLodUpdatedMask = 0;
+
+ } else if ((component & LOD_OFFSET_CHANGED) != 0) {
+ if ((mirrorTexture.lodOffset) == null) {
+ mirrorTexture.lodOffset =
+ new Point3f((Point3f)value);
+ } else {
+ mirrorTexture.lodOffset.set((Point3f)value);
+ }
+
+ // mark that texture lod info needs to be updated
+ mirrorTexture.resourceLodUpdatedMask = 0;
+
+ } else if ((component & UPDATE_IMAGE) != 0) {
+ mirrorTexture.updateResourceCreationMask();
+ }
+
+ }
+
+
+ // notifies the Texture mirror object that the image data in a referenced
+ // ImageComponent object is changed. Need to update the texture image
+ // accordingly.
+ // Note: this is called from mirror object only
+
+ void notifyImageComponentImageChanged(ImageComponentRetained image,
+ ImageComponentUpdateInfo value) {
+
+ //System.out.println("Texture.notifyImageComponentImageChanged");
+
+
+ // if this texture is to be reloaded, don't bother to keep
+ // the update info
+
+ if (resourceCreationMask == 0) {
+
+ if (imageUpdateInfo != null) {
+
+ //remove all the existing updates from the list
+
+ for (int face = 0; face < numFaces; face++) {
+ for (int level = 0; level < maxLevels; level++) {
+ if (imageUpdateInfo[face][level] != null) {
+ VirtualUniverse.mc.addFreeImageUpdateInfo(
+ imageUpdateInfo[face][level]);
+ imageUpdateInfo[face][level].clear();
+ }
+ }
+
+ // reset the update prune mask for this level
+ if (imageUpdatePruneMask != null) {
+ imageUpdatePruneMask[face] = 0;
+ }
+ }
+ }
+
+ return;
+ }
+
+
+ // first find which texture image is being affected
+
+ boolean done;
+
+ for (int j = 0; j < numFaces; j++) {
+
+ done = false;
+ for (int i = baseLevel; i <= maximumLevel && !done; i++) {
+ if (images[j][i] == image) {
+
+ // reset the resourceUpdatedMask to tell the
+ // rendering method to update the resource
+ resourceUpdatedMask = 0;
+
+ // add update info to the update list
+ addImageUpdateInfo(i, j, value);
+
+ // set done to true for this face because no two levels
+ // can reference the same ImageComponent object
+ done = true;
+ }
+ }
+ }
+ }
+
+
+ // reset the resourceCreationMask
+ // Note: called from the mirror object only
+
+ void updateResourceCreationMask() {
+ resourceCreationMask = 0x0;
+ }
+
+ final ImageComponentRetained createNextLevelImage(
+ ImageComponentRetained oImage) {
+
+ int xScale, yScale, nWidth, nHeight;
+ ImageComponentRetained nImage = null;
+
+ if (oImage.width > 1) {
+ nWidth = oImage.width >> 1;
+ xScale = 2;
+ } else {
+ nWidth = 1;
+ xScale = 1;
+ }
+ if (oImage.height > 1) {
+ nHeight = oImage.height >> 1;
+ yScale = 2;
+ } else {
+ nHeight = 1;
+ yScale = 1;
+ }
+
+ int bytesPerPixel = oImage.bytesPerYupPixelStored;
+
+ if (oImage instanceof ImageComponent2DRetained) {
+
+ nImage = new ImageComponent2DRetained();
+ nImage.processParams(oImage.format, nWidth, nHeight, 1);
+ nImage.imageYup = new byte[nWidth * nHeight * bytesPerPixel];
+ nImage.storedYupFormat = nImage.internalFormat;
+ nImage.bytesPerYupPixelStored = bytesPerPixel;
+ scaleImage(nWidth, nHeight, xScale, yScale, oImage.width, 0, 0,
+ bytesPerPixel, nImage.imageYup, oImage.imageYup);
+
+ } else { //oImage instanceof ImageComponent3DRetained
+
+ int depth = ((ImageComponent3DRetained)oImage).depth;
+ nImage = new ImageComponent3DRetained();
+ nImage.processParams(oImage.format, nWidth, nHeight, depth);
+ nImage.imageYup = new byte[nWidth * nHeight * bytesPerPixel];
+ nImage.storedYupFormat = nImage.internalFormat;
+ nImage.bytesPerYupPixelStored = bytesPerPixel;
+
+ for (int i = 0; i < depth; i++) {
+ scaleImage(nWidth, nHeight, xScale, yScale, oImage.width,
+ i * nWidth * nHeight * bytesPerPixel,
+ i * oImage.width * oImage.height * bytesPerPixel,
+ bytesPerPixel, nImage.imageYup, oImage.imageYup);
+ }
+ }
+ return nImage;
+ }
+
+ final void scaleImage(int nWidth, int nHeight, int xScale, int yScale,
+ int oWidth, int nStart, int oStart, int bytesPerPixel,
+ byte[] nData, byte[] oData) {
+
+ int nOffset = 0;
+ int oOffset = 0;
+ int oLineIncr = bytesPerPixel * oWidth;
+ int oPixelIncr = bytesPerPixel << 1;
+
+ if (yScale == 1) {
+ for (int x = 0; x < nWidth; x++) {
+ for (int k = 0; k < bytesPerPixel; k++) {
+ nData[nStart + nOffset + k] = (byte)
+ (((int)(oData[oStart + oOffset + k] & 0xff) +
+ (int)(oData[oStart + oOffset + k
+ + bytesPerPixel] & 0xff) + 1) >> 1);
+ }
+ nOffset += bytesPerPixel;
+ oOffset += oPixelIncr;
+ }
+ } else if (xScale == 1) {
+ for (int y = 0; y < nHeight; y++) {
+ for (int k = 0; k < bytesPerPixel; k++) {
+ nData[nStart + nOffset + k] = (byte)
+ (((int)(oData[oStart + oOffset + k] & 0xff) +
+ (int)(oData[oStart + oOffset + k
+ + oLineIncr] & 0xff) + 1) >> 1);
+ }
+ nOffset += bytesPerPixel;
+ oOffset += oLineIncr;
+ }
+ } else {
+ for (int y = 0; y < nHeight; y++) {
+ for (int x = 0; x < nWidth; x++) {
+ for (int k = 0; k < bytesPerPixel; k++) {
+ nData[nStart + nOffset + k] = (byte)
+ (((int)(oData[oStart + oOffset + k] & 0xff) +
+ (int)(oData[oStart + oOffset + k
+ + bytesPerPixel] & 0xff) +
+ (int)(oData[oStart + oOffset + k
+ + oLineIncr] & 0xff) +
+ (int)(oData[oStart + oOffset + k + oLineIncr +
+ + bytesPerPixel] & 0xff) + 2) >> 2);
+ }
+ nOffset += bytesPerPixel;
+ oOffset += oPixelIncr;
+ }
+ oOffset += oLineIncr;
+ }
+ }
+ }
+
+ void incTextureBinRefCount(TextureBin tb) {
+
+ ImageComponentRetained image;
+
+ textureBinRefCount++;
+
+ // check to see if there is any modifiable images,
+ // if yes, add those images to nodeComponentList in RenderBin
+ // so that RenderBin can acquire a lock before rendering
+ // to prevent updating of image data while rendering
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = 0; i < maxLevels; i++) {
+ image = images[j][i];
+
+ // it is possible that image.source == null because
+ // the mipmap could have been created by the library, and
+ // hence don't have source and therefore they are
+ // guaranteed not modifiable
+
+ if (image != null &&
+ (image.isByReference() ||
+ (image.source != null &&
+ image.source.getCapability(
+ ImageComponent.ALLOW_IMAGE_WRITE)))) {
+ tb.renderBin.addNodeComponent(image);
+ }
+ }
+ }
+ }
+
+ void decTextureBinRefCount(TextureBin tb) {
+
+ ImageComponentRetained image;
+
+ textureBinRefCount--;
+
+ // remove any modifiable images from RenderBin nodeComponentList
+
+ for (int j = 0; j < numFaces; j++) {
+ for (int i = 0; i < maxLevels; i++) {
+ image = images[j][i];
+ if (image != null &&
+ (image.isByReference() ||
+ (image.source != null &&
+ image.source.getCapability(
+ ImageComponent.ALLOW_IMAGE_WRITE)))) {
+ tb.renderBin.removeNodeComponent(image);
+ }
+ }
+ }
+ }
+
+
+ final void sendMessage(int attrMask, Object attr) {
+
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.TEXTURE_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1] = new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.TEXTURE_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ }
+
+ protected void finalize() {
+
+ if (objectId > 0) {
+ // memory not yet free
+ // send a message to the request renderer
+ synchronized (VirtualUniverse.mc.contextCreationLock) {
+ boolean found = false;
+
+ for (Enumeration e = Screen3D.deviceRendererMap.elements();
+ e.hasMoreElements(); ) {
+ Renderer rdr = (Renderer) e.nextElement();
+ J3dMessage renderMessage = VirtualUniverse.mc.getMessage();
+ renderMessage.threads = J3dThread.RENDER_THREAD;
+ renderMessage.type = J3dMessage.RENDER_IMMEDIATE;
+ renderMessage.universe = null;
+ renderMessage.view = null;
+ renderMessage.args[0] = null;
+ renderMessage.args[1] = new Integer(objectId);
+ renderMessage.args[2] = "2D";
+ rdr.rendererStructure.addMessage(renderMessage);
+ }
+ objectId = -1;
+ }
+
+ VirtualUniverse.mc.setWorkForRequestRenderer();
+ }
+
+ }
+
+ void handleFrequencyChange(int bit) {
+ switch (bit) {
+ case Texture.ALLOW_ENABLE_WRITE:
+ case Texture.ALLOW_IMAGE_WRITE:
+ case Texture.ALLOW_LOD_RANGE_WRITE: {
+ setFrequencyChangeMask(bit, bit);
+ }
+ default:
+ break;
+ }
+ }
+}
+
diff --git a/src/classes/share/javax/media/j3d/TextureUnitState.java b/src/classes/share/javax/media/j3d/TextureUnitState.java
new file mode 100644
index 0000000..d544f43
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureUnitState.java
@@ -0,0 +1,320 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+
+/**
+ * The TextureUnitState object defines all texture mapping state for a
+ * single texture unit. An appearance object contains an array of
+ * texture unit state objects to define the state for multiple texture
+ * mapping units. The texture unit state consists of the
+ * following:
+ *
+ * <p>
+ * <ul>
+ * <li>Texture - defines the texture image and filtering
+ * parameters used when texture mapping is enabled. These attributes
+ * are defined in a Texture object.</li><p>
+ *
+ * <li>Texture attributes - defines the attributes that apply to
+ * texture mapping, such as the texture mode, texture transform,
+ * blend color, and perspective correction mode. These attributes
+ * are defined in a TextureAttributes object.</li><p>
+ *
+ * <li>Texture coordinate generation - defines the attributes
+ * that apply to texture coordinate generation, such as whether
+ * texture coordinate generation is enabled, coordinate format
+ * (2D or 3D coordinates), coordinate generation mode (object
+ * linear, eye linear, or spherical reflection mapping), and the
+ * R, S, and T coordinate plane equations. These attributes
+ * are defined in a TexCoordGeneration object.</li><p>
+ * </ul>
+ *
+ * @see Appearance
+ * @see Texture
+ * @see TextureAttributes
+ * @see TexCoordGeneration
+ *
+ * @since Java 3D 1.2
+ */
+public class TextureUnitState extends NodeComponent {
+
+ /**
+ * Specifies that this TextureUnitState object allows reading its
+ * texture, texture attribute, or texture coordinate generation
+ * component information.
+ */
+ public static final int ALLOW_STATE_READ =
+ CapabilityBits.TEXTURE_UNIT_STATE_ALLOW_STATE_READ;
+
+ /**
+ * Specifies that this TextureUnitState object allows writing its
+ * texture, texture attribute, or texture coordinate generation
+ * component information.
+ */
+ public static final int ALLOW_STATE_WRITE =
+ CapabilityBits.TEXTURE_UNIT_STATE_ALLOW_STATE_WRITE;
+
+
+ /**
+ * Constructs a TextureUnitState component object using defaults for all
+ * state variables. All component object references are initialized
+ * to null.
+ */
+ public TextureUnitState() {
+ // Just use default values
+ }
+
+ /**
+ * Constructs a TextureUnitState component object using the specified
+ * component objects.
+ *
+ * @param texture object that specifies the desired texture
+ * map and texture parameters
+ * @param textureAttributes object that specifies the desired
+ * texture attributes
+ * @param texCoordGeneration object that specifies the texture coordinate
+ * generation parameters
+ */
+ public TextureUnitState(Texture texture,
+ TextureAttributes textureAttributes,
+ TexCoordGeneration texCoordGeneration) {
+
+ ((TextureUnitStateRetained)this.retained).initTexture(texture);
+ ((TextureUnitStateRetained)this.retained).initTextureAttributes(
+ textureAttributes);
+ ((TextureUnitStateRetained)this.retained).initTexCoordGeneration(
+ texCoordGeneration);
+ }
+
+ /**
+ * Creates the retained mode TextureUnitStateRetained object that this
+ * TextureUnitState component object will point to.
+ */
+ void createRetained() {
+ this.retained = new TextureUnitStateRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Sets the texture, texture attributes, and texture coordinate
+ * generation components in this TextureUnitState object to the
+ * specified component objects.
+ *
+ * @param texture object that specifies the desired texture
+ * map and texture parameters
+ * @param textureAttributes object that specifies the desired
+ * texture attributes
+ * @param texCoordGeneration object that specifies the texture coordinate
+ * generation parameters
+ */
+ public void set(Texture texture,
+ TextureAttributes textureAttributes,
+ TexCoordGeneration texCoordGeneration) {
+
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_STATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState0"));
+
+ ((TextureUnitStateRetained)this.retained).setTextureUnitState(
+ texture, textureAttributes, texCoordGeneration);
+ }
+
+ /**
+ * Sets the texture object to the specified object.
+ * Setting it to null disables texture mapping for the
+ * texture unit corresponding to this TextureUnitState object.
+ * @param texture object that specifies the desired texture
+ * map and texture parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setTexture(Texture texture) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_STATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState0"));
+
+ ((TextureUnitStateRetained)this.retained).setTexture(texture);
+ }
+
+ /**
+ * Retrieves the current texture object.
+ * @return the texture object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Texture getTexture() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_STATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState1"));
+
+ return ((TextureUnitStateRetained)this.retained).getTexture();
+ }
+
+ /**
+ * Sets the textureAttributes object to the specified object.
+ * Setting it to null will result in default attribute usage for the.
+ * texture unit corresponding to this TextureUnitState object.
+ * @param textureAttributes object that specifies the desired
+ * texture attributes
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setTextureAttributes(TextureAttributes textureAttributes) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_STATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState2"));
+
+ ((TextureUnitStateRetained)this.retained).setTextureAttributes(textureAttributes);
+ }
+
+ /**
+ * Retrieves the current textureAttributes object.
+ * @return the textureAttributes object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public TextureAttributes getTextureAttributes() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_STATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState3"));
+
+ return ((TextureUnitStateRetained)this.retained).getTextureAttributes();
+ }
+
+ /**
+ * Sets the texCoordGeneration object to the specified object.
+ * Setting it to null disables texture coordinate generation for the
+ * texture unit corresponding to this TextureUnitState object.
+ * @param texCoordGeneration object that specifies the texture coordinate
+ * generation parameters
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setTexCoordGeneration(TexCoordGeneration texCoordGeneration) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_STATE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState4"));
+
+ ((TextureUnitStateRetained)this.retained).setTexCoordGeneration(texCoordGeneration);
+ }
+
+ /**
+ * Retrieves the current texCoordGeneration object.
+ * @return the texCoordGeneration object
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public TexCoordGeneration getTexCoordGeneration() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_STATE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState5"));
+
+ return ((TextureUnitStateRetained)this.retained).getTexCoordGeneration();
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ TextureUnitState ts = new TextureUnitState();
+ ts.duplicateNodeComponent(this);
+ return ts;
+ }
+
+ /**
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneNode method.
+ *
+ * @deprecated replaced with duplicateNodeComponent(
+ * NodeComponent originalNodeComponent, boolean forceDuplicate)
+ */
+ public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+ checkDuplicateNodeComponent(originalNodeComponent);
+ }
+
+ /**
+ * Copies all TextureUnitState information from
+ * <code>originalNodeComponent</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 originalNodeComponent 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#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ Hashtable hashtable = originalNodeComponent.nodeHashtable;
+
+ TextureUnitStateRetained app = (TextureUnitStateRetained) originalNodeComponent.retained;
+
+ TextureUnitStateRetained rt = (TextureUnitStateRetained) retained;
+
+ rt.setTexture((Texture) getNodeComponent(app.getTexture(),
+ forceDuplicate,
+ hashtable));
+
+ rt.setTextureAttributes((TextureAttributes) getNodeComponent(
+ app.getTextureAttributes(),
+ forceDuplicate,
+ hashtable));
+
+ rt.setTexCoordGeneration((TexCoordGeneration) getNodeComponent(
+ app.getTexCoordGeneration(),
+ forceDuplicate,
+ hashtable));
+ }
+
+ /**
+ * This function is called from getNodeComponent() to see if any of
+ * the sub-NodeComponents duplicateOnCloneTree flag is true.
+ * If it is the case, current NodeComponent needs to
+ * duplicate also even though current duplicateOnCloneTree flag is false.
+ * This should be overwrite by NodeComponent which contains sub-NodeComponent.
+ */
+ boolean duplicateChild() {
+ if (getDuplicateOnCloneTree())
+ return true;
+
+ TextureUnitStateRetained rt = (TextureUnitStateRetained) retained;
+
+ NodeComponent nc = rt.getTexture();
+ if ((nc != null) && nc.duplicateChild())
+ return true;
+
+ nc = rt.getTextureAttributes();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ nc = rt.getTexCoordGeneration();
+ if ((nc != null) && nc.getDuplicateOnCloneTree())
+ return true;
+
+ return false;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TextureUnitStateRetained.java b/src/classes/share/javax/media/j3d/TextureUnitStateRetained.java
new file mode 100644
index 0000000..cecb193
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TextureUnitStateRetained.java
@@ -0,0 +1,615 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.Color4f;
+import java.util.ArrayList;
+
+class TextureUnitStateRetained extends NodeComponentRetained {
+
+ static final int TEXTURE_CHANGED = 0x0001;
+ static final int TEXTURE_ATTRS_CHANGED = 0x0002;
+ static final int TEXCOORD_GEN_CHANGED = 0x0004;
+ static final int ALL_STATE_CHANGED = 0x0008;
+
+ TextureRetained texture = null;
+ TextureAttributesRetained texAttrs = null;
+ TexCoordGenerationRetained texGen = null;
+
+ /**
+ * An abstract method to validate the texture unit state component
+ */
+ final void setTextureUnitStateComponent(NodeComponent comp,
+ NodeComponentRetained thisComp,
+ int messageOp) {
+ if (source.isLive()) {
+
+ if ((comp == null && thisComp == null) ||
+ (comp != null && comp.retained == thisComp))
+ return;
+
+ if (thisComp != null) {
+ thisComp.clearLive(refCount);
+ thisComp.removeMirrorUsers(this);
+ }
+
+ if (comp != null) {
+ ((NodeComponentRetained)comp.retained).setLive(inBackgroundGroup, refCount);
+ // If texture unit is live, then copy all the users of this
+ // texture unit state as users of this texture component
+ ((NodeComponentRetained)comp.retained).copyMirrorUsers(this);
+ }
+
+ if (messageOp != -1) {
+ sendMessage(messageOp,
+ (comp == null ? null :
+ ((NodeComponentRetained)comp.retained).mirror));
+ }
+
+ }
+ }
+
+ final void initTextureUnitState(Texture texture,
+ TextureAttributes texAttrs,
+ TexCoordGeneration texGen) {
+
+ initTexture(texture);
+ initTextureAttributes(texAttrs);
+ initTexCoordGeneration(texGen);
+ }
+
+ final void setTextureUnitState(Texture texture,
+ TextureAttributes texAttrs,
+ TexCoordGeneration texGen) {
+
+ setTextureUnitStateComponent(texture, this.texture, -1);
+ setTextureUnitStateComponent(texAttrs, this.texAttrs, -1);
+ setTextureUnitStateComponent(texGen, this.texGen, -1);
+
+
+ // send all changes to the target threads in one
+ // message to avoid modifying the renderBin repeatedly
+
+ Object args[] = new Object[3];
+ args[0] = (texture == null ? null :
+ ((TextureRetained)texture.retained).mirror);
+ args[1] = (texAttrs == null ? null :
+ ((TextureAttributesRetained)texAttrs.retained).mirror);
+ args[2] = (texGen == null ? null :
+ ((TexCoordGenerationRetained)texGen.retained).mirror);
+
+ sendMessage(ALL_STATE_CHANGED, args);
+
+ initTextureUnitState(texture, texAttrs, texGen);
+ }
+
+ final void initTexture(Texture texture) {
+ if (texture == null)
+ this.texture = null;
+ else
+ this.texture = (TextureRetained)texture.retained;
+ }
+
+ final void setTexture(Texture texture) {
+ setTextureUnitStateComponent(texture, this.texture, TEXTURE_CHANGED);
+ initTexture(texture);
+ }
+
+ final void initTextureAttributes(TextureAttributes texAttrs) {
+ if (texAttrs == null)
+ this.texAttrs = null;
+ else
+ this.texAttrs = (TextureAttributesRetained)texAttrs.retained;
+ }
+
+ final void setTextureAttributes(TextureAttributes texAttrs) {
+ setTextureUnitStateComponent(texAttrs, this.texAttrs,
+ TEXTURE_ATTRS_CHANGED);
+ initTextureAttributes(texAttrs);
+ }
+
+ final void initTexCoordGeneration(TexCoordGeneration texGen) {
+ if (texGen == null)
+ this.texGen = null;
+ else
+ this.texGen = (TexCoordGenerationRetained)texGen.retained;
+ }
+
+ final void setTexCoordGeneration(TexCoordGeneration texGen) {
+ setTextureUnitStateComponent(texGen, this.texGen, TEXCOORD_GEN_CHANGED);
+ initTexCoordGeneration(texGen);
+ }
+
+ Texture getTexture() {
+ return (texture == null ? null : (Texture)texture.source);
+ }
+
+ TextureAttributes getTextureAttributes() {
+ return (texAttrs == null ? null : (TextureAttributes)texAttrs.source);
+ }
+
+ TexCoordGeneration getTexCoordGeneration() {
+ return (texGen == null ? null : (TexCoordGeneration)texGen.source);
+ }
+
+ native void updateTextureUnitState(long ctx, int unitIndex, boolean enableFlag);
+
+ void updateNative(int unitIndex, Canvas3D cv,
+ boolean reload, boolean simulate) {
+
+ //System.out.println("TextureUnitState/updateNative: unitIndex= " + unitIndex + " reload= " + reload + " simulate= " + simulate);
+
+ // unitIndex can be -1 for the single texture case, so
+ // can't use unitIndex to index into the cv.texUnitState;
+ // in this case, use index 0
+
+ int index = unitIndex;
+
+ if (index < 0)
+ index = 0;
+
+
+ boolean dirty = ((cv.canvasDirty & (Canvas3D.TEXTUREATTRIBUTES_DIRTY|Canvas3D.TEXTUREBIN_DIRTY)) != 0);
+
+ if (this.texture == null) {
+ // if texture is null, then texture mapped is
+ // disabled for this texture unit; and no more
+ // state update is needed
+
+ //System.out.println("texture is null");
+
+ if (cv.texUnitState[index].texture != null) {
+ cv.resetTexture(cv.ctx, unitIndex);
+ cv.texUnitState[index].texture = null;
+ }
+ cv.canvasDirty &= ~Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ return;
+ } else {
+
+ updateTextureUnitState(cv.ctx, unitIndex, true);
+ }
+
+ // reload is needed in a multi-texture case to bind the
+ // texture parameters to the texture unit state
+
+ if (reload || dirty || cv.texUnitState[index].texture != this.texture) {
+
+ // texture cannot be null at this point because it is
+ // already checked above
+ this.texture.updateNative(cv);
+
+ cv.texUnitState[index].texture = this.texture;
+
+ }
+
+ if (this.texAttrs == null) {
+ if (reload || dirty || cv.texUnitState[index].texAttrs != null) {
+ cv.resetTextureAttributes(cv.ctx);
+ if (VirtualUniverse.mc.isD3D() &&
+ (texGen != null) &&
+ ((texGen.genMode == TexCoordGeneration.EYE_LINEAR) ||
+ ((texGen.genMode == TexCoordGeneration.SPHERE_MAP)))) {
+ // We need to reload tex since eye linear
+ // and sphere map in D3D will change the
+ // texture transform matrix also.
+ dirty = true;
+ }
+ cv.setBlendFunc(cv.ctx, TransparencyAttributes.BLEND_ONE,
+ TransparencyAttributes.BLEND_ZERO);
+ cv.texUnitState[index].texAttrs = null;
+ }
+ } else {
+
+ TextureAttributesRetained mTexAttrs;
+ if (this.texAttrs.mirror == null) {
+ mTexAttrs = this.texAttrs;
+ } else {
+ mTexAttrs = (TextureAttributesRetained) this.texAttrs.mirror;
+ }
+
+
+ if (mTexAttrs.mirrorCompDirty) {
+ // This happen when canvas reference is same as
+ // texUnitState.texAttrs and we update the later without
+ // notify cache.
+ cv.texUnitState[index].texAttrs = null;
+ mTexAttrs.mirrorCompDirty = false;
+ }
+
+ if (reload || dirty || cv.texUnitState[index].texAttrs != mTexAttrs) {
+ this.texAttrs.updateNative(cv, simulate, texture.format);
+ if (VirtualUniverse.mc.isD3D() &&
+ (texGen != null) &&
+ ((texGen.genMode == TexCoordGeneration.EYE_LINEAR) ||
+ ((texGen.genMode == TexCoordGeneration.SPHERE_MAP)))) {
+ dirty = true;
+ }
+ cv.texUnitState[index].texAttrs = mTexAttrs;
+ }
+ }
+
+ if (this.texGen == null) {
+ if (reload || dirty || cv.texUnitState[index].texGen != null) {
+ cv.resetTexCoordGeneration(cv.ctx);
+ cv.texUnitState[index].texGen = null;
+ }
+ } else {
+ TexCoordGenerationRetained mTexGen;
+
+ if (this.texGen.mirror == null) {
+ mTexGen = this.texGen;
+ } else {
+ mTexGen = (TexCoordGenerationRetained)this.texGen.mirror;
+ }
+
+ if (mTexGen.mirrorCompDirty) {
+ // This happen when canvas reference is same as
+ // texUnitState.texGen and we update the later without
+ // notify cache.
+ cv.texUnitState[index].texGen = null;
+ mTexGen.mirrorCompDirty = false;
+ }
+
+ if (reload || dirty || cv.texUnitState[index].texGen != mTexGen) {
+ this.texGen.updateNative(cv);
+ cv.texUnitState[index].texGen = mTexGen;
+ }
+ }
+ cv.canvasDirty &= ~Canvas3D.TEXTUREATTRIBUTES_DIRTY;
+ }
+
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+
+ if (mirror == null) {
+ TextureUnitStateRetained mirrorTus =
+ new TextureUnitStateRetained();
+ mirror = mirrorTus;
+ }
+ mirror.source = source;
+ initMirrorObject();
+
+ }
+
+ synchronized void initMirrorObject() {
+
+ TextureUnitStateRetained mirrorTus =
+ (TextureUnitStateRetained)mirror;
+
+ if (this.texture != null)
+ mirrorTus.texture = (TextureRetained)this.texture.mirror;
+ else
+ mirrorTus.texture = null;
+
+ if (this.texAttrs != null)
+ mirrorTus.texAttrs =
+ (TextureAttributesRetained)this.texAttrs.mirror;
+ else
+ mirrorTus.texAttrs = null;
+
+ if (this.texGen != null)
+ mirrorTus.texGen = (TexCoordGenerationRetained)this.texGen.mirror;
+ else
+ mirrorTus.texGen = null;
+ }
+
+
+ /** Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ TextureUnitStateRetained mirrorTus = (TextureUnitStateRetained)mirror;
+
+ if ((component & TEXTURE_CHANGED) != 0) {
+ mirrorTus.texture = (TextureRetained)value;
+ }
+ else if ((component & TEXTURE_ATTRS_CHANGED) != 0) {
+ mirrorTus.texAttrs = (TextureAttributesRetained)value;
+ }
+ else if ((component & TEXCOORD_GEN_CHANGED) != 0) {
+ mirrorTus.texGen = (TexCoordGenerationRetained)value;
+ }
+ else if ((component & ALL_STATE_CHANGED) != 0) {
+ Object [] args = (Object []) value;
+ mirrorTus.texture = (TextureRetained)args[0];
+ mirrorTus.texAttrs = (TextureAttributesRetained)args[1];
+ mirrorTus.texGen = (TexCoordGenerationRetained)args[2];
+ }
+ }
+
+
+ boolean equivalent(TextureUnitStateRetained tr) {
+
+ if (tr == null) {
+ return (false);
+
+ } else if ((this.changedFrequent != 0) || (tr.changedFrequent != 0)) {
+ return (this.mirror == tr);
+
+ } else {
+
+ if (this.texture != tr.texture) {
+ return false;
+ }
+
+ if (this.texAttrs != null &&
+ !this.texAttrs.equivalent(tr.texAttrs)) {
+ return false;
+ }
+
+ if (this.texGen != null &&
+ !this.texGen.equivalent(tr.texGen)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected Object clone() {
+ TextureUnitStateRetained tr = (TextureUnitStateRetained)super.clone();
+
+ // the cloned object is used for RenderBin only.
+ // In most cases, it will duplicate all attributes in the RenderBin
+ // so that updating a mirror object in one level will not affect the
+ // entire structure of the RenderBin, but will affect only those bins
+ // that got affected by the modified mirror object
+ if (this.texAttrs != null)
+ tr.texAttrs = (TextureAttributesRetained)this.texAttrs.clone();
+
+ if (this.texGen != null)
+ tr.texGen = (TexCoordGenerationRetained)this.texGen.clone();
+
+ return tr;
+ }
+
+
+ /**
+ * set the texture unit state according to the specified texture
+ * unit state
+ */
+ protected void set(TextureUnitStateRetained tr) {
+ super.set(tr);
+ this.texture = tr.texture;
+
+ if (tr.texAttrs == null) {
+ this.texAttrs = null;
+ } else {
+ if (this.texAttrs == null) {
+ this.texAttrs = (TextureAttributesRetained)tr.texAttrs.clone();
+ } else {
+ this.texAttrs.set(tr.texAttrs);
+ }
+ }
+
+ if (tr.texGen == null) {
+ this.texGen = null;
+ } else {
+ if (this.texGen == null) {
+ this.texGen = (TexCoordGenerationRetained)tr.texGen.clone();
+ } else {
+ this.texGen.set(tr.texGen);
+ }
+ }
+ }
+
+ protected void set(TextureRetained texture,
+ TextureAttributesRetained texAttrs,
+ TexCoordGenerationRetained texGen) {
+ this.texture = texture;
+ this.texAttrs = texAttrs;
+ this.texGen = texGen;
+ }
+
+ synchronized void addAMirrorUser(Shape3DRetained shape) {
+
+ super.addAMirrorUser(shape);
+
+ if (texture != null)
+ texture.addAMirrorUser(shape);
+ if (texAttrs != null)
+ texAttrs.addAMirrorUser(shape);
+ if (texGen != null)
+ texGen.addAMirrorUser(shape);
+ }
+
+ synchronized void removeAMirrorUser(Shape3DRetained shape) {
+ super.removeAMirrorUser(shape);
+
+ if (texture != null)
+ texture.removeAMirrorUser(shape);
+ if (texAttrs != null)
+ texAttrs.removeAMirrorUser(shape);
+ if (texGen != null)
+ texGen.removeAMirrorUser(shape);
+ }
+
+ synchronized void removeMirrorUsers(NodeComponentRetained node) {
+ super.removeMirrorUsers(node);
+
+ if (texture != null)
+ texture.removeMirrorUsers(node);
+ if (texAttrs != null)
+ texAttrs.removeMirrorUsers(node);
+ if (texGen != null)
+ texGen.removeMirrorUsers(node);
+ }
+
+ synchronized void copyMirrorUsers(NodeComponentRetained node) {
+ super.copyMirrorUsers(node);
+
+ if (texture != null)
+ texture.copyMirrorUsers(node);
+ if (texAttrs != null)
+ texAttrs.copyMirrorUsers(node);
+ if (texGen != null)
+ texGen.copyMirrorUsers(node);
+ }
+
+
+ void setLive(boolean backgroundGroup, int refCount) {
+ if (texture != null)
+ texture.setLive(backgroundGroup, refCount);
+
+ if (texAttrs != null)
+ texAttrs.setLive(backgroundGroup, refCount);
+
+ if (texGen != null)
+ texGen.setLive(backgroundGroup, refCount);
+
+ // Increment the reference count and initialize the textureUnitState
+ // mirror object
+ super.doSetLive(backgroundGroup, refCount);
+ super.markAsLive();
+
+ }
+
+
+ void clearLive(int refCount) {
+ super.clearLive(refCount);
+
+ if (texture != null)
+ texture.clearLive(refCount);
+ if (texAttrs != null)
+ texAttrs.clearLive(refCount);
+ if (texGen != null)
+ texGen.clearLive(refCount);
+ }
+
+ boolean isStatic() {
+
+ return (source.capabilityBitsEmpty() &&
+ ((texture == null) || (texture.isStatic())) &&
+ ((texAttrs == null) || (texAttrs.isStatic())) &&
+ ((texGen == null) || (texGen.isStatic())));
+ }
+
+ /*
+ // Simply pass along to the NodeComponent
+
+ void compile (CompileState compState) {
+ setCompiled();
+
+ if (texture != null)
+ texture.compile(compState);
+ if (texAttrs != null)
+ texAttrs.compile(compState);
+ if (texGen != null)
+ texGen.compile(compState);
+ }
+ */
+
+ boolean equals(TextureUnitStateRetained ts) {
+ return ((ts == this) ||
+ (ts != null) &&
+ ((texture == ts.texture) ||
+ ((texture != null) && (texture.equals(ts.texture)))) &&
+ ((texAttrs == ts.texAttrs) ||
+ ((texAttrs != null) && (texAttrs.equals(ts.texAttrs)))) &&
+ ((texGen == ts.texGen) ||
+ ((texGen != null) && (texGen.equals(ts.texGen)))));
+ }
+
+
+ void setInImmCtx(boolean flag) {
+ if (texture != null)
+ texture.setInImmCtx(flag);
+ if (texAttrs != null)
+ texAttrs.setInImmCtx(flag);
+ if (texGen != null)
+ texGen.setInImmCtx(flag);
+ }
+
+ boolean getInImmCtx() {
+ return (inImmCtx ||
+ ((texture != null) && (texture.getInImmCtx())) ||
+ ((texAttrs != null) && (texAttrs.getInImmCtx())) ||
+ ((texGen != null) && (texGen.getInImmCtx())));
+ }
+
+
+ boolean isLive() {
+ return (source.isLive() ||
+ ((texture != null) && (texture.source.isLive())) ||
+ ((texAttrs != null) && (texAttrs.source.isLive())) ||
+ ((texGen != null) && (texGen.source.isLive())));
+ }
+
+ final void sendMessage(int attrMask, Object attr) {
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.TEXTURE_UNIT_STATE_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.TEXTURE_UNIT_STATE_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ }
+
+ boolean isTextureEnabled() {
+ // Check the internal enable , instead of userSpecifiedEnable
+ return (texture != null && texture.enable);
+ }
+
+ // use by D3D to simulate OGL blend mode using multi-pass
+ final boolean needBlend2Pass(Canvas3D cv) {
+ return ((texAttrs != null) &&
+ VirtualUniverse.mc.isD3D() &&
+ ((cv.textureExtendedFeatures &
+ Canvas3D.TEXTURE_LERP) == 0) &&
+ (texAttrs.textureMode == TextureAttributes.BLEND) &&
+ (texture.format != Texture.ALPHA) &&
+ (texture.format != Texture.INTENSITY));
+ }
+
+ void handleFrequencyChange(int bit) {
+ switch (bit) {
+ case TextureUnitState.ALLOW_STATE_WRITE: {
+ setFrequencyChangeMask(bit, bit);
+ }
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TimerThread.java b/src/classes/share/javax/media/j3d/TimerThread.java
new file mode 100644
index 0000000..b89922b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TimerThread.java
@@ -0,0 +1,144 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The TimerThread is thread that handle WakeupOnElapsedTime call.
+ * There is only one thread for the whole system.
+ */
+
+class TimerThread extends Thread {
+
+ // action flag for runMonitor
+ private static final int WAIT = 0;
+ private static final int NOTIFY = 1;
+ private static final int STOP = 2;
+
+ private WakeupOnElapsedTimeHeap heap = new WakeupOnElapsedTimeHeap();
+
+ // Wakeup InputDeviceScheduler for every sample time reach
+ private WakeupOnElapsedTime inputDeviceSchedCond =
+ new WakeupOnElapsedTime(InputDeviceScheduler.samplingTime);
+
+ // Wakeup {all?} Sound Scheduler{s} for every sample time reach
+ // QUESTION: this sampling time is set to a very large value so Sound
+ // Schedulers are not pinged often unless explicitly requested
+ // TODO: need a way to remove/null this condition when all
+ // soundschedulers are halted
+ private WakeupOnElapsedTime soundSchedCond =
+ new WakeupOnElapsedTime(120000); // every 2 minutes
+
+ private boolean running = true;
+ private volatile boolean waiting = false;
+
+ TimerThread(ThreadGroup t) {
+ super(t, "J3D-TimerThread");
+ }
+
+ // call from UserThread
+ void add(WakeupOnElapsedTime wakeup) {
+ synchronized (heap) {
+ heap.insert(wakeup);
+ }
+ runMonitor(NOTIFY, 0);
+ }
+
+ void addInputDeviceSchedCond() {
+ inputDeviceSchedCond.triggeredTime =
+ InputDeviceScheduler.samplingTime +
+ System.currentTimeMillis();
+ add(inputDeviceSchedCond);
+ }
+
+ void addSoundSchedCond(long wakeupTime) {
+ // TODO: there are potentially multiple sound schedulers.
+ // this code will force a wait up on ALL sound schedulers
+ // even though only one needs to process the sound that
+ // this wakeup condition is triggered by.
+ soundSchedCond.triggeredTime = wakeupTime;
+ add(soundSchedCond);
+ }
+
+ // call from MasterThread
+ void finish() {
+ runMonitor(STOP, 0);
+ }
+
+ void remove(WakeupOnElapsedTime w) {
+ synchronized (heap) {
+ heap.extract(w);
+ }
+ }
+
+ public void run() {
+ long waitTime = -1;
+ long time;
+ WakeupOnElapsedTime cond;
+
+ while (running) {
+ runMonitor(WAIT, waitTime);
+ time = System.currentTimeMillis();
+
+ while (true) {
+ cond = null;
+ waitTime = -1;
+ synchronized (heap) {
+ if (!heap.isEmpty()) {
+ waitTime = heap.getMin().triggeredTime - time;
+ if (waitTime <= 0) {
+ cond = heap.extractMin();
+ }
+ }
+ }
+ if (cond == null) {
+ break;
+ } else if (cond == inputDeviceSchedCond) {
+ VirtualUniverse.mc.sendRunMessage(
+ J3dThread.INPUT_DEVICE_SCHEDULER);
+ } else if (cond == soundSchedCond) {
+ VirtualUniverse.mc.sendRunMessage(
+ J3dThread.SOUND_SCHEDULER);
+ } else {
+ cond.setTriggered();
+ }
+ }
+ }
+ }
+
+
+ synchronized void runMonitor(int action, long waitTime) {
+ switch (action) {
+ case WAIT:
+ try {
+ if (running) {
+ waiting = true;
+ if (waitTime < 0) {
+ wait();
+ } else {
+ wait(waitTime);
+ }
+ }
+ } catch (InterruptedException e) {}
+ waiting = false;
+ break;
+ case NOTIFY:
+ notify();
+ break;
+ case STOP:
+ running = false;
+ notify();
+ break;
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/Transform3D.java b/src/classes/share/javax/media/j3d/Transform3D.java
new file mode 100644
index 0000000..4b1d69f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Transform3D.java
@@ -0,0 +1,5641 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.*;
+
+/**
+ * A generalized transform object represented internally as a 4x4
+ * double-precision floating point matrix. The mathematical
+ * representation is
+ * row major, as in traditional matrix mathematics.
+ * A Transform3D is used to perform translations, rotations, and
+ * scaling and shear effects.<P>
+ *
+ * A transform has an associated type, and
+ * all type classification is left to the Transform3D object.
+ * A transform will typically have multiple types, unless it is a
+ * general, unclassifiable matrix, in which case it won't be assigned
+ * a type. <P>
+ *
+ * The Transform3D type is internally computed when the transform
+ * object is constructed and updated any time it is modified. A
+ * matrix will typically have multiple types. For example, the type
+ * associated with an identity matrix is the result of ORing all of
+ * the types, except for ZERO and NEGATIVE_DETERMINANT, together.
+ * There are public methods available to get the ORed type of the
+ * transformation, the sign of the determinant, and the least
+ * general matrix type. The matrix type flags are defined as
+ * follows:<P>
+ * <UL>
+ * <LI>ZERO - zero matrix. All of the elements in the matrix
+ * have the value 0.</LI><P>
+ * <LI>IDENTITY - identity matrix. A matrix with ones on its
+ * main diagonal and zeros every where else.</LI><P>
+ * <LI>SCALE - the matrix is a uniform scale matrix - there are
+ * no rotational or translation components.</LI><P>
+ * <LI>ORTHOGONAL - the four row vectors that make up an orthogonal
+ * matrix form a basis, meaning that they are mutually orthogonal.
+ * The scale is unity and there are no translation components.</LI><P>
+ * <LI>RIGID - the upper 3 X 3 of the matrix is orthogonal, and
+ * there is a translation component-the scale is unity.</LI><P>
+ * <LI>CONGRUENT - this is an angle- and length-preserving matrix,
+ * meaning that it can translate, rotate, and reflect about an axis,
+ * and scale by an amount that is uniform in all directions. These
+ * operations preserve the distance between any two points, and the
+ * angle between any two intersecting lines.</LI><P>
+ * <LI>AFFINE - an affine matrix can translate, rotate, reflect,
+ * scale anisotropically, and shear. Lines remain straight, and parallel
+ * lines remain parallel, but the angle between intersecting lines can
+ * change.</LI><P>
+ * </UL>
+ * A matrix is also classified by the sign of its determinant:<P>
+ * <UL>
+ * NEGATIVE_DETERMINANT - this matrix has a negative determinant.
+ * An orthogonal matrix with a positive determinant is a rotation
+ * matrix. An orthogonal matrix with a negative determinant is a
+ * reflection and rotation matrix.<P></UL>
+ * The Java 3D model for 4 X 4 transformations is:<P>
+ * <UL><pre>
+ * [ m00 m01 m02 m03 ] [ x ] [ x' ]
+ * [ m10 m11 m12 m13 ] . [ y ] = [ y' ]
+ * [ m20 m21 m22 m23 ] [ z ] [ z' ]
+ * [ m30 m31 m32 m33 ] [ w ] [ w' ]
+ *
+ * x' = m00 . x+m01 . y+m02 . z+m03 . w
+ * y' = m10 . x+m11 . y+m12 . z+m13 . w
+ * z' = m20 . x+m21 . y+m22 . z+m23 . w
+ * w' = m30 . x+m31 . y+m32 . z+m33 . w
+ * </pre></ul><P>
+ * Note: When transforming a Point3f or a Point3d, the input w is set to
+ * 1. When transforming a Vector3f or Vector3d, the input w is set to 0.
+ */
+
+public class Transform3D {
+
+ double[] mat = new double[16];
+ //double[] rot = new double[9];
+ //double[] scales = new double[3];
+ // allocate the memory only when it is needed. Following three places will allocate the memory,
+ // void setScaleTranslation(), void computeScales() and void computeScaleRotation()
+ double[] rot = null;
+ double[] scales = null;
+
+ // Unknown until lazy classification is done
+ private int type = 0;
+
+ // Dirty bit for classification, this is used
+ // for classify()
+ private static final int AFFINE_BIT = 0x01;
+ private static final int ORTHO_BIT = 0x02;
+ private static final int CONGRUENT_BIT = 0x04;
+ private static final int RIGID_BIT = 0x08;
+ private static final int CLASSIFY_BIT = 0x10;
+
+ // this is used for scales[], rot[]
+ private static final int SCALE_BIT = 0x20;
+ private static final int ROTATION_BIT = 0x40;
+ // set when SVD renormalization is necessary
+ private static final int SVD_BIT = 0x80;
+
+ private static final int CLASSIFY_ALL_DIRTY = AFFINE_BIT |
+ ORTHO_BIT |
+ CONGRUENT_BIT |
+ RIGID_BIT |
+ CLASSIFY_BIT;
+ private static final int ROTSCALESVD_DIRTY = SCALE_BIT |
+ ROTATION_BIT |
+ SVD_BIT;
+ private int dirtyBits;
+
+ boolean autoNormalize = false; // Don't auto normalize by default
+ /*
+ // reused temporaries for compute_svd
+ private boolean svdAllocd =false;
+ private double[] u1 = null;
+ private double[] v1 = null;
+ private double[] t1 = null; // used by both compute_svd and compute_qr
+ private double[] t2 = null; // used by both compute_svd and compute_qr
+ private double[] ts = null;
+ private double[] svdTmp = null;
+ private double[] svdRot = null;
+ private double[] single_values = null;
+ private double[] e = null;
+ private double[] svdScales = null;
+ // from svrReorder
+ private int[] svdOut = null;
+ private double[] svdMag = null;
+
+ // from compute_qr
+ private double[] cosl = null;
+ private double[] cosr = null;
+ private double[] sinl = null;
+ private double[] sinr = null;
+ private double[] qr_m = null;
+ */
+
+ private static final double EPS = 1.110223024E-16;
+
+ static final double EPSILON = 1.0e-10;
+ static final double EPSILON_ABSOLUTE = 1.0e-5;
+ static final double EPSILON_RELATIVE = 1.0e-4;
+ /**
+ * A zero matrix.
+ */
+ public static final int ZERO = 0x01;
+
+ /**
+ * An identity matrix.
+ */
+ public static final int IDENTITY = 0x02;
+
+
+ /**
+ * A Uniform scale matrix with no translation or other
+ * off-diagonal components.
+ */
+ public static final int SCALE = 0x04;
+
+ /**
+ * A translation-only matrix with ones on the diagonal.
+ *
+ */
+ public static final int TRANSLATION = 0x08;
+
+ /**
+ * The four row vectors that make up an orthogonal matrix form a basis,
+ * meaning that they are mutually orthogonal; an orthogonal matrix with
+ * positive determinant is a pure rotation matrix; a negative
+ * determinant indicates a rotation and a reflection.
+ */
+ public static final int ORTHOGONAL = 0x10;
+
+ /**
+ * This matrix is a rotation and a translation with unity scale;
+ * The upper 3x3 of the matrix is orthogonal, and there is a
+ * translation component.
+ */
+ public static final int RIGID = 0x20;
+
+ /**
+ * This is an angle and length preserving matrix, meaning that it
+ * can translate, rotate, and reflect
+ * about an axis, and scale by an amount that is uniform in all directions.
+ * These operations preserve the distance between any two points and the
+ * angle between any two intersecting lines.
+ */
+ public static final int CONGRUENT = 0x40;
+
+ /**
+ * An affine matrix can translate, rotate, reflect, scale anisotropically,
+ * and shear. Lines remain straight, and parallel lines remain parallel,
+ * but the angle between intersecting lines can change. In order for a
+ * transform to be classified as affine, the 4th row must be: [0, 0, 0, 1].
+ */
+ public static final int AFFINE = 0x80;
+
+ /**
+ * This matrix has a negative determinant; an orthogonal matrix with
+ * a positive determinant is a rotation matrix; an orthogonal matrix
+ * with a negative determinant is a reflection and rotation matrix.
+ */
+ public static final int NEGATIVE_DETERMINANT = 0x100;
+
+ /**
+ * The upper 3x3 column vectors that make up an orthogonal
+ * matrix form a basis meaning that they are mutually orthogonal.
+ * It can have non-uniform or zero x/y/z scale as long as
+ * the dot product of any two column is zero.
+ * This one is used by Java3D internal only and should not
+ * expose to the user.
+ */
+ private static final int ORTHO = 0x40000000;
+
+ /**
+ * Constructs and initializes a transform from the 4 x 4 matrix. The
+ * type of the constructed transform will be classified automatically.
+ * @param m1 the 4 x 4 transformation matrix
+ */
+ public Transform3D(Matrix4f m1) {
+ set(m1);
+ }
+
+ /**
+ * Constructs and initializes a transform from the 4 x 4 matrix. The
+ * type of the constructed transform will be classified automatically.
+ * @param m1 the 4 x 4 transformation matrix
+ */
+ public Transform3D(Matrix4d m1) {
+ set(m1);
+ }
+
+ /**
+ * Constructs and initializes a transform from the Transform3D object.
+ * @param t1 the transformation object to be copied
+ */
+ public Transform3D(Transform3D t1) {
+ set(t1);
+ }
+
+ /**
+ * Constructs and initializes a transform to the identity matrix.
+ */
+ public Transform3D() {
+ setIdentity(); // this will also classify the matrix
+ }
+
+ /**
+ * Constructs and initializes a transform from the float array of
+ * length 16; the top row of the matrix is initialized to the first
+ * four elements of the array, and so on. The type of the transform
+ * object is classified internally.
+ * @param matrix a float array of 16
+ */
+ public Transform3D(float[] matrix) {
+ set(matrix);
+ }
+
+ /**
+ * Constructs and initializes a transform from the double precision array
+ * of length 16; the top row of the matrix is initialized to the first
+ * four elements of the array, and so on. The type of the transform is
+ * classified internally.
+ * @param matrix a float array of 16
+ */
+ public Transform3D(double[] matrix) {
+ set(matrix);
+ }
+
+ /**
+ * Constructs and initializes a transform from the quaternion,
+ * translation, and scale values. The scale is applied only to the
+ * rotational components of the matrix (upper 3 x 3) and not to the
+ * translational components of the matrix.
+ * @param q1 the quaternion value representing the rotational component
+ * @param t1 the translational component of the matrix
+ * @param s the scale value applied to the rotational components
+ */
+ public Transform3D(Quat4d q1, Vector3d t1, double s) {
+ set(q1, t1, s);
+ }
+
+ /**
+ * Constructs and initializes a transform from the quaternion,
+ * translation, and scale values. The scale is applied only to the
+ * rotational components of the matrix (upper 3 x 3) and not to the
+ * translational components of the matrix.
+ * @param q1 the quaternion value representing the rotational component
+ * @param t1 the translational component of the matrix
+ * @param s the scale value applied to the rotational components
+ */
+ public Transform3D(Quat4f q1, Vector3d t1, double s) {
+ set(q1, t1, s);
+ }
+
+ /**
+ * Constructs and initializes a transform from the quaternion,
+ * translation, and scale values. The scale is applied only to the
+ * rotational components of the matrix (upper 3 x 3) and not to the
+ * translational components of the matrix.
+ * @param q1 the quaternion value representing the rotational component
+ * @param t1 the translational component of the matrix
+ * @param s the scale value applied to the rotational components
+ */
+ public Transform3D(Quat4f q1, Vector3f t1, float s) {
+ set(q1, t1, s);
+ }
+
+ /**
+ * Constructs a transform and initializes it to the upper 4 x 4
+ * of the GMatrix argument. If the parameter matrix is
+ * smaller than 4 x 4, the remaining elements in the transform matrix are
+ * assigned to zero.
+ * @param m1 the GMatrix
+ */
+ public Transform3D(GMatrix m1) {
+ set(m1);
+ }
+
+ /**
+ * Constructs and initializes a transform from the rotation matrix,
+ * translation, and scale values. The scale is applied only to the
+ * rotational component of the matrix (upper 3x3) and not to the
+ * translational component of the matrix.
+ * @param m1 the rotation matrix representing the rotational component
+ * @param t1 the translational component of the matrix
+ * @param s the scale value applied to the rotational components
+ */
+ public Transform3D(Matrix3f m1, Vector3d t1, double s) {
+ set(m1, t1, s);
+ }
+
+ /**
+ * Constructs and initializes a transform from the rotation matrix,
+ * translation, and scale values. The scale is applied only to the
+ * rotational components of the matrix (upper 3x3) and not to the
+ * translational components of the matrix.
+ * @param m1 the rotation matrix representing the rotational component
+ * @param t1 the translational component of the matrix
+ * @param s the scale value applied to the rotational components
+ */
+ public Transform3D(Matrix3d m1, Vector3d t1, double s) {
+ set(m1, t1, s);
+ }
+
+
+ /**
+ * Constructs and initializes a transform from the rotation matrix,
+ * translation, and scale values. The scale is applied only to the
+ * rotational components of the matrix (upper 3x3) and not to the
+ * translational components of the matrix.
+ * @param m1 the rotation matrix representing the rotational component
+ * @param t1 the translational component of the matrix
+ * @param s the scale value applied to the rotational components
+ */
+ public Transform3D(Matrix3f m1, Vector3f t1, float s) {
+ set(m1, t1, s);
+ }
+
+ /**
+ * Returns the type of this matrix as an or'ed bitmask of
+ * of all of the type classifications to which it belongs.
+ * @return or'ed bitmask of all of the type classifications
+ * of this transform
+ */
+ public final int getType() {
+ if ((dirtyBits & CLASSIFY_BIT) != 0) {
+ classify();
+ }
+ // clear ORTHO bit which only use internally
+ return (type & ~ORTHO);
+ }
+
+ // True if type is ORTHO
+ // Since ORTHO didn't take into account the last row.
+ final boolean isOrtho() {
+ if ((dirtyBits & ORTHO_BIT) != 0) {
+ if ((almostZero(mat[0]*mat[2] + mat[4]*mat[6] +
+ mat[8]*mat[10]) &&
+ almostZero(mat[0]*mat[1] + mat[4]*mat[5] +
+ mat[8]*mat[9]) &&
+ almostZero(mat[1]*mat[2] + mat[5]*mat[6] +
+ mat[9]*mat[10]))) {
+ type |= ORTHO;
+ dirtyBits &= ~ORTHO_BIT;
+ return true;
+ } else {
+ type &= ~ORTHO;
+ dirtyBits &= ~ORTHO_BIT;
+ return false;
+ }
+ }
+ return ((type & ORTHO) != 0);
+ }
+
+ final boolean isCongruent() {
+ if ((dirtyBits & CONGRUENT_BIT) != 0) {
+ // This will also classify AFFINE
+ classifyRigid();
+ }
+ return ((type & CONGRUENT) != 0);
+ }
+
+ final boolean isAffine() {
+ if ((dirtyBits & AFFINE_BIT) != 0) {
+ classifyAffine();
+ }
+ return ((type & AFFINE) != 0);
+ }
+
+ final boolean isRigid() {
+ if ((dirtyBits & RIGID_BIT) != 0) {
+
+
+ // This will also classify AFFINE & CONGRUENT
+ if ((dirtyBits & CONGRUENT_BIT) != 0) {
+ classifyRigid();
+ } else {
+
+ if ((type & CONGRUENT) != 0) {
+ // Matrix is Congruent, need only
+ // to check scale
+ double s;
+ if ((dirtyBits & SCALE_BIT) != 0){
+ s = mat[0]*mat[0] + mat[4]*mat[4] +
+ mat[8]*mat[8];
+ // Note that
+ // scales[0] = sqrt(s);
+ // but since sqrt(1) = 1,
+ // we don't need to do s = sqrt(s) here.
+ } else {
+ if(scales == null)
+ scales = new double[3];
+ s = scales[0];
+ }
+ if (almostOne(s)) {
+ type |= RIGID;
+ } else {
+ type &= ~RIGID;
+ }
+ } else {
+ // Not even congruent, so isRigid must be false
+ type &= ~RIGID;
+ }
+ dirtyBits &= ~RIGID_BIT;
+ }
+ }
+ return ((type & RIGID) != 0);
+ }
+
+
+ /**
+ * Returns the least general type of this matrix; the order of
+ * generality from least to most is: ZERO, IDENTITY,
+ * SCALE/TRANSLATION, ORTHOGONAL, RIGID, CONGRUENT, AFFINE.
+ * If the matrix is ORTHOGONAL, calling the method
+ * getDeterminantSign() will yield more information.
+ * @return the least general matrix type
+ */
+ public final int getBestType() {
+ getType(); // force classify if necessary
+
+ if ((type & ZERO) != 0 ) return ZERO;
+ if ((type & IDENTITY) != 0 ) return IDENTITY;
+ if ((type & SCALE) != 0 ) return SCALE;
+ if ((type & TRANSLATION) != 0 ) return TRANSLATION;
+ if ((type & ORTHOGONAL) != 0 ) return ORTHOGONAL;
+ if ((type & RIGID) != 0 ) return RIGID;
+ if ((type & CONGRUENT) != 0 ) return CONGRUENT;
+ if ((type & AFFINE) != 0 ) return AFFINE;
+ if ((type & NEGATIVE_DETERMINANT) != 0 ) return NEGATIVE_DETERMINANT;
+ return 0;
+ }
+
+ /*
+ private void print_type() {
+ if ((type & ZERO) > 0 ) System.out.print(" ZERO");
+ if ((type & IDENTITY) > 0 ) System.out.print(" IDENTITY");
+ if ((type & SCALE) > 0 ) System.out.print(" SCALE");
+ if ((type & TRANSLATION) > 0 ) System.out.print(" TRANSLATION");
+ if ((type & ORTHOGONAL) > 0 ) System.out.print(" ORTHOGONAL");
+ if ((type & RIGID) > 0 ) System.out.print(" RIGID");
+ if ((type & CONGRUENT) > 0 ) System.out.print(" CONGRUENT");
+ if ((type & AFFINE) > 0 ) System.out.print(" AFFINE");
+ if ((type & NEGATIVE_DETERMINANT) > 0 ) System.out.print(" NEGATIVE_DETERMINANT");
+ }
+ */
+
+ /**
+ * Returns the sign of the determinant of this matrix; a return value
+ * of true indicates a positive determinant; a return value of false
+ * indicates a negative determinant. In general, an orthogonal matrix
+ * with a positive determinant is a pure rotation matrix; an orthogonal
+ * matrix with a negative determinant is a both a rotation and a
+ * reflection matrix.
+ * @return determinant sign : true means positive, false means negative
+ */
+ public final boolean getDeterminantSign() {
+ return (determinant() >= 0);
+ }
+
+ /**
+ * Sets a flag that enables or disables automatic SVD
+ * normalization. If this flag is enabled, an automatic SVD
+ * normalization of the rotational components (upper 3x3) of this
+ * matrix is done after every subsequent matrix operation that
+ * modifies this matrix. This is functionally equivalent to
+ * calling normalize() after every subsequent call, but may be
+ * less computationally expensive.
+ * The default value for this parameter is false.
+ * @param autoNormalize the boolean state of auto normalization
+ */
+ public final void setAutoNormalize(boolean autoNormalize) {
+ this.autoNormalize = autoNormalize;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+ /**
+ * Returns the state of auto-normalization.
+ * @return boolean state of auto-normalization
+ */
+ public final boolean getAutoNormalize() {
+ return this.autoNormalize;
+ }
+
+ private static final boolean almostZero(double a) {
+ return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE));
+ }
+
+ private static final boolean almostOne(double a) {
+ return ((a < 1+EPSILON_ABSOLUTE) && (a > 1-EPSILON_ABSOLUTE));
+ }
+
+ private static final boolean almostEqual(double a, double b) {
+ double diff = a-b;
+
+ if (diff >= 0) {
+ if (diff < EPSILON) {
+ return true;
+ }
+ // a > b
+ if ((b > 0) || (a > -b)) {
+ return (diff < EPSILON_RELATIVE*a);
+ } else {
+ return (diff < -EPSILON_RELATIVE*b);
+ }
+
+ } else {
+ if (diff > -EPSILON) {
+ return true;
+ }
+ // a < b
+ if ((b < 0) || (-a > b)) {
+ return (diff > EPSILON_RELATIVE*a);
+ } else {
+ return (diff > -EPSILON_RELATIVE*b);
+ }
+ }
+ }
+
+ private final void classifyAffine() {
+ if (almostZero(mat[12]) &&
+ almostZero(mat[13]) &&
+ almostZero(mat[14]) &&
+ almostOne(mat[15])) {
+ type |= AFFINE;
+ } else {
+ type &= ~AFFINE;
+ }
+ dirtyBits &= ~AFFINE_BIT;
+ }
+
+ // same amount of work to classify rigid and congruent
+ private final void classifyRigid() {
+
+ if ((dirtyBits & AFFINE_BIT) != 0) {
+ // should not touch ORTHO bit
+ type &= ORTHO;
+ classifyAffine();
+ } else {
+ // keep the affine bit if there is one
+ // and clear the others (CONGRUENT/RIGID) bit
+ type &= (ORTHO | AFFINE);
+ }
+
+ if ((type & AFFINE) != 0) {
+ // checking orthogonal condition
+ if (isOrtho()) {
+ if ((dirtyBits & SCALE_BIT) != 0) {
+ double s0 = mat[0]*mat[0] + mat[4]*mat[4] +
+ mat[8]*mat[8];
+ double s1 = mat[1]*mat[1] + mat[5]*mat[5] +
+ mat[9]*mat[9];
+ if (almostEqual(s0, s1)) {
+ double s2 = mat[2]*mat[2] + mat[6]*mat[6] +
+ mat[10]*mat[10];
+ if (almostEqual(s2, s0)) {
+ type |= CONGRUENT;
+ // Note that scales[0] = sqrt(s0);
+ if (almostOne(s0)) {
+ type |= RIGID;
+ }
+ }
+ }
+ } else {
+ if(scales == null)
+ scales = new double[3];
+
+ double s = scales[0];
+ if (almostEqual(s, scales[1]) &&
+ almostEqual(s, scales[2])) {
+ type |= CONGRUENT;
+ if (almostOne(s)) {
+ type |= RIGID;
+ }
+ }
+ }
+ }
+ }
+ dirtyBits &= (~RIGID_BIT | ~CONGRUENT_BIT);
+ }
+
+ /**
+ * Classifies a matrix.
+ */
+ private final void classify() {
+
+ if ((dirtyBits & (RIGID_BIT|AFFINE_BIT|CONGRUENT_BIT)) != 0) {
+ // Test for RIGID, CONGRUENT, AFFINE.
+ classifyRigid();
+ }
+
+ // Test for ZERO, IDENTITY, SCALE, TRANSLATION,
+ // ORTHOGONAL, NEGATIVE_DETERMINANT
+ if ((type & AFFINE) != 0) {
+ if ((type & CONGRUENT) != 0) {
+ if ((type & RIGID) != 0) {
+ if (zeroTranslation()) {
+ type |= ORTHOGONAL;
+ if (rotateZero()) {
+ // mat[0], mat[5], mat[10] can be only be
+ // 1 or -1 when reach here
+ if ((mat[0] > 0) &&
+ (mat[5] > 0) &&
+ (mat[10] > 0)) {
+ type |= IDENTITY|SCALE|TRANSLATION;
+ }
+ }
+ } else {
+ if (rotateZero()) {
+ type |= TRANSLATION;
+ }
+ }
+ } else {
+ // uniform scale
+ if (zeroTranslation() && rotateZero()) {
+ type |= SCALE;
+ }
+ }
+
+ }
+ } else {
+ // last row is not (0, 0, 0, 1)
+ if (almostZero(mat[12]) &&
+ almostZero(mat[13]) &&
+ almostZero(mat[14]) &&
+ almostZero(mat[15]) &&
+ zeroTranslation() &&
+ rotateZero() &&
+ almostZero(mat[0]) &&
+ almostZero(mat[5]) &&
+ almostZero(mat[10])) {
+ type |= ZERO;
+ }
+ }
+
+ if (!getDeterminantSign()) {
+ type |= NEGATIVE_DETERMINANT;
+ }
+ dirtyBits &= ~CLASSIFY_BIT;
+ }
+
+ final boolean zeroTranslation() {
+ return (almostZero(mat[3]) &&
+ almostZero(mat[7]) &&
+ almostZero(mat[11]));
+ }
+
+ final boolean rotateZero() {
+ return (almostZero(mat[1]) && almostZero(mat[2]) &&
+ almostZero(mat[4]) && almostZero(mat[6]) &&
+ almostZero(mat[8]) && almostZero(mat[9]));
+ }
+
+ /**
+ * Returns the matrix elements of this transform as a string.
+ * @return the matrix elements of this transform
+ */
+ public String toString() {
+ // also, print classification?
+ return
+ mat[0] + ", " + mat[1] + ", " + mat[2] + ", " + mat[3] + "\n" +
+ mat[4] + ", " + mat[5] + ", " + mat[6] + ", " + mat[7] + "\n" +
+ mat[8] + ", " + mat[9] + ", " + mat[10] + ", " + mat[11] + "\n" +
+ mat[12] + ", " + mat[13] + ", " + mat[14] + ", " + mat[15]
+ + "\n";
+ }
+
+ /**
+ * Sets this transform to the identity matrix.
+ */
+ public final void setIdentity() {
+ mat[0] = 1.0; mat[1] = 0.0; mat[2] = 0.0; mat[3] = 0.0;
+ mat[4] = 0.0; mat[5] = 1.0; mat[6] = 0.0; mat[7] = 0.0;
+ mat[8] = 0.0; mat[9] = 0.0; mat[10] = 1.0; mat[11] = 0.0;
+ mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0;
+ type = IDENTITY | SCALE | ORTHOGONAL | RIGID | CONGRUENT |
+ AFFINE | TRANSLATION | ORTHO;
+ dirtyBits = SCALE_BIT | ROTATION_BIT;
+ // No need to set SVD_BIT
+ }
+
+ /**
+ * Sets this transform to all zeros.
+ */
+ public final void setZero() {
+ mat[0] = 0.0; mat[1] = 0.0; mat[2] = 0.0; mat[3] = 0.0;
+ mat[4] = 0.0; mat[5] = 0.0; mat[6] = 0.0; mat[7] = 0.0;
+ mat[8] = 0.0; mat[9] = 0.0; mat[10] = 0.0; mat[11] = 0.0;
+ mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 0.0;
+
+ type = ZERO | ORTHO;
+ dirtyBits = SCALE_BIT | ROTATION_BIT;
+ }
+
+
+ /**
+ * Adds this transform to transform t1 and places the result into
+ * this: this = this + t1.
+ * @param t1 the transform to be added to this transform
+ */
+ public final void add(Transform3D t1) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] += t1.mat[i];
+ }
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+ /**
+ * Adds transforms t1 and t2 and places the result into this transform.
+ * @param t1 the transform to be added
+ * @param t2 the transform to be added
+ */
+ public final void add(Transform3D t1, Transform3D t2) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] = t1.mat[i] + t2.mat[i];
+ }
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+ /**
+ * Subtracts transform t1 from this transform and places the result
+ * into this: this = this - t1.
+ * @param t1 the transform to be subtracted from this transform
+ */
+ public final void sub(Transform3D t1) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] -= t1.mat[i];
+ }
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Subtracts transform t2 from transform t1 and places the result into
+ * this: this = t1 - t2.
+ * @param t1 the left transform
+ * @param t2 the right transform
+ */
+ public final void sub(Transform3D t1, Transform3D t2) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] = t1.mat[i] - t2.mat[i];
+ }
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+
+ /**
+ * Transposes this matrix in place.
+ */
+ public final void transpose() {
+ double temp;
+
+ temp = mat[4];
+ mat[4] = mat[1];
+ mat[1] = temp;
+
+ temp = mat[8];
+ mat[8] = mat[2];
+ mat[2] = temp;
+
+ temp = mat[12];
+ mat[12] = mat[3];
+ mat[3] = temp;
+
+ temp = mat[9];
+ mat[9] = mat[6];
+ mat[6] = temp;
+
+ temp = mat[13];
+ mat[13] = mat[7];
+ mat[7] = temp;
+
+ temp = mat[14];
+ mat[14] = mat[11];
+ mat[11] = temp;
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+ /**
+ * Transposes transform t1 and places the value into this transform.
+ * The transform t1 is not modified.
+ * @param t1 the transform whose transpose is placed into this transform
+ */
+ public final void transpose(Transform3D t1) {
+
+ if (this != t1) {
+ mat[0] = t1.mat[0];
+ mat[1] = t1.mat[4];
+ mat[2] = t1.mat[8];
+ mat[3] = t1.mat[12];
+ mat[4] = t1.mat[1];
+ mat[5] = t1.mat[5];
+ mat[6] = t1.mat[9];
+ mat[7] = t1.mat[13];
+ mat[8] = t1.mat[2];
+ mat[9] = t1.mat[6];
+ mat[10] = t1.mat[10];
+ mat[11] = t1.mat[14];
+ mat[12] = t1.mat[3];
+ mat[13] = t1.mat[7];
+ mat[14] = t1.mat[11];
+ mat[15] = t1.mat[15];
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ } else {
+ this.transpose();
+ }
+
+ }
+
+ /**
+ * Sets the value of this transform to the matrix conversion of the
+ * single precision quaternion argument; the non-rotational
+ * components are set as if this were an identity matrix.
+ * @param q1 the quaternion to be converted
+ */
+ public final void set(Quat4f q1) {
+
+ mat[0] = (1.0f - 2.0f*q1.y*q1.y - 2.0f*q1.z*q1.z);
+ mat[4] = (2.0f*(q1.x*q1.y + q1.w*q1.z));
+ mat[8] = (2.0f*(q1.x*q1.z - q1.w*q1.y));
+
+ mat[1] = (2.0f*(q1.x*q1.y - q1.w*q1.z));
+ mat[5] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.z*q1.z);
+ mat[9] = (2.0f*(q1.y*q1.z + q1.w*q1.x));
+
+ mat[2] = (2.0f*(q1.x*q1.z + q1.w*q1.y));
+ mat[6] = (2.0f*(q1.y*q1.z - q1.w*q1.x));
+ mat[10] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.y*q1.y);
+
+ mat[3] = 0.0;
+ mat[7] = 0.0;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+
+ dirtyBits = CLASSIFY_BIT | SCALE_BIT | ROTATION_BIT;
+ type = RIGID | CONGRUENT | AFFINE | ORTHO;
+ }
+
+ /**
+ * Sets the value of this transform to the matrix conversion of the
+ * double precision quaternion argument; the non-rotational
+ * components are set as if this were an identity matrix.
+ * @param q1 the quaternion to be converted
+ */
+ public final void set(Quat4d q1) {
+
+ mat[0] = (1.0 - 2.0*q1.y*q1.y - 2.0*q1.z*q1.z);
+ mat[4] = (2.0*(q1.x*q1.y + q1.w*q1.z));
+ mat[8] = (2.0*(q1.x*q1.z - q1.w*q1.y));
+
+ mat[1] = (2.0*(q1.x*q1.y - q1.w*q1.z));
+ mat[5] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.z*q1.z);
+ mat[9] = (2.0*(q1.y*q1.z + q1.w*q1.x));
+
+ mat[2] = (2.0*(q1.x*q1.z + q1.w*q1.y));
+ mat[6] = (2.0*(q1.y*q1.z - q1.w*q1.x));
+ mat[10] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.y*q1.y);
+
+ mat[3] = 0.0;
+ mat[7] = 0.0;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ dirtyBits = CLASSIFY_BIT | SCALE_BIT | ROTATION_BIT;
+ type = RIGID | CONGRUENT | AFFINE | ORTHO;
+ }
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix values in the double precision Matrix3d argument; the other
+ * elements of this transform are unchanged; any pre-existing scale
+ * will be preserved; the argument matrix m1 will be checked for proper
+ * normalization when this transform is internally classified.
+ * @param m1 the double precision 3x3 matrix
+ */
+ public final void setRotation(Matrix3d m1) {
+
+ if ((dirtyBits & SCALE_BIT)!= 0) {
+ computeScales(false);
+ }
+
+ mat[0] = m1.m00*scales[0];
+ mat[1] = m1.m01*scales[1];
+ mat[2] = m1.m02*scales[2];
+ mat[4] = m1.m10*scales[0];
+ mat[5] = m1.m11*scales[1];
+ mat[6] = m1.m12*scales[2];
+ mat[8] = m1.m20*scales[0];
+ mat[9] = m1.m21*scales[1];
+ mat[10]= m1.m22*scales[2];
+
+ // only affine bit is preserved
+ // SCALE_BIT is clear in the above computeScales() so
+ // there is no need to set it dirty again.
+ dirtyBits |= (RIGID_BIT | CONGRUENT_BIT| ORTHO_BIT|
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY);
+
+ if (autoNormalize) {
+ // the matrix pass in may not normalize
+ normalize();
+ }
+ }
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix values in the single precision Matrix3f argument; the other
+ * elements of this transform are unchanged; any pre-existing scale
+ * will be preserved; the argument matrix m1 will be checked for proper
+ * normalization when this transform is internally classified.
+ * @param m1 the single precision 3x3 matrix
+ */
+ public final void setRotation(Matrix3f m1) {
+
+ if ((dirtyBits & SCALE_BIT)!= 0) {
+ computeScales(false);
+ }
+
+ mat[0] = m1.m00*scales[0];
+ mat[1] = m1.m01*scales[1];
+ mat[2] = m1.m02*scales[2];
+ mat[4] = m1.m10*scales[0];
+ mat[5] = m1.m11*scales[1];
+ mat[6] = m1.m12*scales[2];
+ mat[8] = m1.m20*scales[0];
+ mat[9] = m1.m21*scales[1];
+ mat[10]= m1.m22*scales[2];
+
+ dirtyBits |= (RIGID_BIT | CONGRUENT_BIT| ORTHO_BIT|
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY);
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix equivalent values of the quaternion argument; the other
+ * elements of this transform are unchanged; any pre-existing scale
+ * in the transform is preserved.
+ * @param q1 the quaternion that specifies the rotation
+ */
+ public final void setRotation(Quat4f q1) {
+
+ if ((dirtyBits & SCALE_BIT)!= 0) {
+ computeScales(false);
+ }
+
+ mat[0] = (1.0 - 2.0*q1.y*q1.y - 2.0*q1.z*q1.z)*scales[0];
+ mat[4] = (2.0*(q1.x*q1.y + q1.w*q1.z))*scales[0];
+ mat[8] = (2.0*(q1.x*q1.z - q1.w*q1.y))*scales[0];
+
+ mat[1] = (2.0*(q1.x*q1.y - q1.w*q1.z))*scales[1];
+ mat[5] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.z*q1.z)*scales[1];
+ mat[9] = (2.0*(q1.y * q1.z + q1.w * q1.x))*scales[1];
+
+ mat[2] = (2.0*(q1.x*q1.z + q1.w*q1.y))*scales[2];
+ mat[6] = (2.0*(q1.y*q1.z - q1.w*q1.x))*scales[2];
+ mat[10] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.y*q1.y)*scales[2];
+
+ dirtyBits |= CLASSIFY_BIT | ROTATION_BIT;
+ dirtyBits &= ~ORTHO_BIT;
+ type |= ORTHO;
+ type &= ~(ORTHOGONAL|IDENTITY|SCALE|TRANSLATION|SCALE|ZERO);
+ }
+
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix equivalent values of the quaternion argument; the other
+ * elements of this transform are unchanged; any pre-existing scale
+ * in the transform is preserved.
+ * @param q1 the quaternion that specifies the rotation
+ */
+ public final void setRotation(Quat4d q1) {
+
+ if ((dirtyBits & SCALE_BIT)!= 0) {
+ computeScales(false);
+ }
+
+ mat[0] = (1.0 - 2.0*q1.y*q1.y - 2.0*q1.z*q1.z)*scales[0];
+ mat[4] = (2.0*(q1.x*q1.y + q1.w*q1.z))*scales[0];
+ mat[8] = (2.0*(q1.x*q1.z - q1.w*q1.y))*scales[0];
+
+ mat[1] = (2.0*(q1.x*q1.y - q1.w*q1.z))*scales[1];
+ mat[5] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.z*q1.z)*scales[1];
+ mat[9] = (2.0*(q1.y * q1.z + q1.w * q1.x))*scales[1];
+
+ mat[2] = (2.0*(q1.x*q1.z + q1.w*q1.y))*scales[2];
+ mat[6] = (2.0*(q1.y*q1.z - q1.w*q1.x))*scales[2];
+ mat[10] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.y*q1.y)*scales[2];
+
+ dirtyBits |= CLASSIFY_BIT | ROTATION_BIT;
+ dirtyBits &= ~ORTHO_BIT;
+ type |= ORTHO;
+ type &= ~(ORTHOGONAL|IDENTITY|SCALE|TRANSLATION|SCALE|ZERO);
+ }
+
+
+ /**
+ * Sets the value of this transform to the matrix conversion
+ * of the single precision axis-angle argument; all of the matrix
+ * values are modified.
+ * @param a1 the axis-angle to be converted (x, y, z, angle)
+ */
+ public final void set(AxisAngle4f a1) {
+
+ double mag = Math.sqrt( a1.x*a1.x + a1.y*a1.y + a1.z*a1.z);
+
+ if (almostZero(mag)) {
+ setIdentity();
+ } else {
+ mag = 1.0/mag;
+ double ax = a1.x*mag;
+ double ay = a1.y*mag;
+ double az = a1.z*mag;
+
+ double sinTheta = Math.sin((double)a1.angle);
+ double cosTheta = Math.cos((double)a1.angle);
+ double t = 1.0 - cosTheta;
+
+ double xz = ax * az;
+ double xy = ax * ay;
+ double yz = ay * az;
+
+ mat[0] = t * ax * ax + cosTheta;
+ mat[1] = t * xy - sinTheta * az;
+ mat[2] = t * xz + sinTheta * ay;
+ mat[3] = 0.0;
+
+ mat[4] = t * xy + sinTheta * az;
+ mat[5] = t * ay * ay + cosTheta;
+ mat[6] = t * yz - sinTheta * ax;
+ mat[7] = 0.0;
+
+ mat[8] = t * xz - sinTheta * ay;
+ mat[9] = t * yz + sinTheta * ax;
+ mat[10] = t * az * az + cosTheta;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+ }
+
+
+ /**
+ * Sets the value of this transform to the matrix conversion
+ * of the double precision axis-angle argument; all of the matrix
+ * values are modified.
+ * @param a1 the axis-angle to be converted (x, y, z, angle)
+ */
+ public final void set(AxisAngle4d a1) {
+
+ double mag = Math.sqrt( a1.x*a1.x + a1.y*a1.y + a1.z*a1.z);
+
+ if (almostZero(mag)) {
+ setIdentity();
+ } else {
+ mag = 1.0/mag;
+ double ax = a1.x*mag;
+ double ay = a1.y*mag;
+ double az = a1.z*mag;
+
+ double sinTheta = Math.sin(a1.angle);
+ double cosTheta = Math.cos(a1.angle);
+ double t = 1.0 - cosTheta;
+
+ double xz = ax * az;
+ double xy = ax * ay;
+ double yz = ay * az;
+
+ mat[0] = t * ax * ax + cosTheta;
+ mat[1] = t * xy - sinTheta * az;
+ mat[2] = t * xz + sinTheta * ay;
+ mat[3] = 0.0;
+
+ mat[4] = t * xy + sinTheta * az;
+ mat[5] = t * ay * ay + cosTheta;
+ mat[6] = t * yz - sinTheta * ax;
+ mat[7] = 0.0;
+
+ mat[8] = t * xz - sinTheta * ay;
+ mat[9] = t * yz + sinTheta * ax;
+ mat[10] = t * az * az + cosTheta;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+ }
+
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix equivalent values of the axis-angle argument; the other
+ * elements of this transform are unchanged; any pre-existing scale
+ * in the transform is preserved.
+ * @param a1 the axis-angle to be converted (x, y, z, angle)
+ */
+ public final void setRotation(AxisAngle4d a1) {
+
+ if ((dirtyBits & SCALE_BIT)!= 0) {
+ computeScales(false);
+ }
+
+ double mag = Math.sqrt( a1.x*a1.x + a1.y*a1.y + a1.z*a1.z);
+
+ if (almostZero(mag)) {
+ mat[0] = scales[0];
+ mat[1] = 0.0;
+ mat[2] = 0.0;
+ mat[4] = 0.0;
+ mat[5] = scales[1];
+ mat[6] = 0.0;
+ mat[8] = 0.0;
+ mat[9] = 0.0;
+ mat[10] = scales[2];
+ } else {
+ mag = 1.0/mag;
+ double ax = a1.x*mag;
+ double ay = a1.y*mag;
+ double az = a1.z*mag;
+
+ double sinTheta = Math.sin(a1.angle);
+ double cosTheta = Math.cos(a1.angle);
+ double t = 1.0 - cosTheta;
+
+ double xz = ax * az;
+ double xy = ax * ay;
+ double yz = ay * az;
+
+ mat[0] = (t * ax * ax + cosTheta)*scales[0];
+ mat[1] = (t * xy - sinTheta * az)*scales[1];
+ mat[2] = (t * xz + sinTheta * ay)*scales[2];
+
+ mat[4] = (t * xy + sinTheta * az)*scales[0];
+ mat[5] = (t * ay * ay + cosTheta)*scales[1];
+ mat[6] = (t * yz - sinTheta * ax)*scales[2];
+
+ mat[8] = (t * xz - sinTheta * ay)*scales[0];
+ mat[9] = (t * yz + sinTheta * ax)*scales[1];
+ mat[10] = (t * az * az + cosTheta)*scales[2];
+ }
+
+
+ // Rigid remain rigid, congruent remain congruent after
+ // set rotation
+ dirtyBits |= CLASSIFY_BIT | ROTATION_BIT;
+ dirtyBits &= ~ORTHO_BIT;
+ type |= ORTHO;
+ type &= ~(ORTHOGONAL|IDENTITY|SCALE|TRANSLATION|SCALE|ZERO);
+ }
+
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix equivalent values of the axis-angle argument; the other
+ * elements of this transform are unchanged; any pre-existing scale
+ * in the transform is preserved.
+ * @param a1 the axis-angle to be converted (x, y, z, angle)
+ */
+ public final void setRotation(AxisAngle4f a1) {
+
+ if ((dirtyBits & SCALE_BIT)!= 0) {
+ computeScales(false);
+ }
+
+ double mag = Math.sqrt( a1.x*a1.x + a1.y*a1.y + a1.z*a1.z);
+
+ if (almostZero(mag)) {
+ mat[0] = scales[0];
+ mat[1] = 0.0;
+ mat[2] = 0.0;
+ mat[4] = 0.0;
+ mat[5] = scales[1];
+ mat[6] = 0.0;
+ mat[8] = 0.0;
+ mat[9] = 0.0;
+ mat[10] = scales[2];
+ } else {
+ mag = 1.0/mag;
+ double ax = a1.x*mag;
+ double ay = a1.y*mag;
+ double az = a1.z*mag;
+
+ double sinTheta = Math.sin(a1.angle);
+ double cosTheta = Math.cos(a1.angle);
+ double t = 1.0 - cosTheta;
+
+ double xz = ax * az;
+ double xy = ax * ay;
+ double yz = ay * az;
+
+ mat[0] = (t * ax * ax + cosTheta)*scales[0];
+ mat[1] = (t * xy - sinTheta * az)*scales[1];
+ mat[2] = (t * xz + sinTheta * ay)*scales[2];
+
+ mat[4] = (t * xy + sinTheta * az)*scales[0];
+ mat[5] = (t * ay * ay + cosTheta)*scales[1];
+ mat[6] = (t * yz - sinTheta * ax)*scales[2];
+
+ mat[8] = (t * xz - sinTheta * ay)*scales[0];
+ mat[9] = (t * yz + sinTheta * ax)*scales[1];
+ mat[10] = (t * az * az + cosTheta)*scales[2];
+ }
+
+
+ // Rigid remain rigid, congruent remain congruent after
+ // set rotation
+ dirtyBits |= CLASSIFY_BIT | ROTATION_BIT;
+ dirtyBits &= (~ORTHO_BIT | ~SVD_BIT);
+ type |= ORTHO;
+ type &= ~(ORTHOGONAL|IDENTITY|SCALE|TRANSLATION|SCALE|ZERO);
+ }
+
+
+ /**
+ * Sets the value of this transform to a counter clockwise rotation
+ * about the x axis. All of the non-rotational components are set as
+ * if this were an identity matrix.
+ * @param angle the angle to rotate about the X axis in radians
+ */
+ public void rotX(double angle) {
+ double sinAngle = Math.sin(angle);
+ double cosAngle = Math.cos(angle);
+
+ mat[0] = 1.0;
+ mat[1] = 0.0;
+ mat[2] = 0.0;
+ mat[3] = 0.0;
+
+ mat[4] = 0.0;
+ mat[5] = cosAngle;
+ mat[6] = -sinAngle;
+ mat[7] = 0.0;
+
+ mat[8] = 0.0;
+ mat[9] = sinAngle;
+ mat[10] = cosAngle;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+
+ /**
+ * Sets the value of this transform to a counter clockwise rotation about
+ * the y axis. All of the non-rotational components are set as if this
+ * were an identity matrix.
+ * @param angle the angle to rotate about the Y axis in radians
+ */
+ public void rotY(double angle) {
+ double sinAngle = Math.sin(angle);
+ double cosAngle = Math.cos(angle);
+
+ mat[0] = cosAngle;
+ mat[1] = 0.0;
+ mat[2] = sinAngle;
+ mat[3] = 0.0;
+
+ mat[4] = 0.0;
+ mat[5] = 1.0;
+ mat[6] = 0.0;
+ mat[7] = 0.0;
+
+ mat[8] = -sinAngle;
+ mat[9] = 0.0;
+ mat[10] = cosAngle;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+
+
+ /**
+ * Sets the value of this transform to a counter clockwise rotation
+ * about the z axis. All of the non-rotational components are set
+ * as if this were an identity matrix.
+ * @param angle the angle to rotate about the Z axis in radians
+ */
+ public void rotZ(double angle) {
+ double sinAngle = Math.sin(angle);
+ double cosAngle = Math.cos(angle);
+
+ mat[0] = cosAngle;
+ mat[1] = -sinAngle;
+ mat[2] = 0.0;
+ mat[3] = 0.0;
+
+ mat[4] = sinAngle;
+ mat[5] = cosAngle;
+ mat[6] = 0.0;
+ mat[7] = 0.0;
+
+ mat[8] = 0.0;
+ mat[9] = 0.0;
+ mat[10] = 1.0;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+
+
+ /**
+ * Sets the translational value of this matrix to the Vector3f parameter
+ * values, and sets the other components of the matrix as if this
+ * transform were an identity matrix.
+ * @param trans the translational component
+ */
+ public final void set(Vector3f trans) {
+ mat[0] = 1.0; mat[1] = 0.0; mat[2] = 0.0; mat[3] = trans.x;
+ mat[4] = 0.0; mat[5] = 1.0; mat[6] = 0.0; mat[7] = trans.y;
+ mat[8] = 0.0; mat[9] = 0.0; mat[10] = 1.0; mat[11] = trans.z;
+ mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+
+ /**
+ * Sets the translational value of this matrix to the Vector3d paramter
+ * values, and sets the other components of the matrix as if this
+ * transform were an identity matrix.
+ * @param trans the translational component
+ */
+ public final void set(Vector3d trans) {
+ mat[0] = 1.0; mat[1] = 0.0; mat[2] = 0.0; mat[3] = trans.x;
+ mat[4] = 0.0; mat[5] = 1.0; mat[6] = 0.0; mat[7] = trans.y;
+ mat[8] = 0.0; mat[9] = 0.0; mat[10] = 1.0; mat[11] = trans.z;
+ mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0;
+
+
+ type = CONGRUENT | AFFINE | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+
+ /**
+ * Sets the scale component of the current transform; any existing
+ * scale is first factored out of the existing transform before
+ * the new scale is applied.
+ * @param scale the new scale amount
+ */
+ public final void setScale(double scale) {
+ if ((dirtyBits & ROTATION_BIT)!= 0) {
+ computeScaleRotation(false);
+ }
+
+ scales[0] = scales[1] = scales[2] = scale;
+ mat[0] = rot[0]*scale;
+ mat[1] = rot[1]*scale;
+ mat[2] = rot[2]*scale;
+ mat[4] = rot[3]*scale;
+ mat[5] = rot[4]*scale;
+ mat[6] = rot[5]*scale;
+ mat[8] = rot[6]*scale;
+ mat[9] = rot[7]*scale;
+ mat[10] = rot[8]*scale;
+
+ dirtyBits |= (CLASSIFY_BIT | RIGID_BIT | CONGRUENT_BIT | SVD_BIT);
+ dirtyBits &= ~SCALE_BIT;
+ }
+
+
+ /**
+ * Sets the possibly non-uniform scale component of the current
+ * transform; any existing scale is first factored out of the
+ * existing transform before the new scale is applied.
+ * @param scale the new x,y,z scale values
+ */
+ public final void setScale(Vector3d scale) {
+
+ if ((dirtyBits & ROTATION_BIT)!= 0) {
+ computeScaleRotation(false);
+ }
+
+ scales[0] = scale.x;
+ scales[1] = scale.y;
+ scales[2] = scale.z;
+
+ mat[0] = rot[0]*scale.x;
+ mat[1] = rot[1]*scale.y;
+ mat[2] = rot[2]*scale.z;
+ mat[4] = rot[3]*scale.x;
+ mat[5] = rot[4]*scale.y;
+ mat[6] = rot[5]*scale.z;
+ mat[8] = rot[6]*scale.x;
+ mat[9] = rot[7]*scale.y;
+ mat[10] = rot[8]*scale.z;
+ dirtyBits |= (CLASSIFY_BIT | RIGID_BIT | CONGRUENT_BIT | SVD_BIT);
+ dirtyBits &= ~SCALE_BIT;
+ }
+
+
+ /**
+ * Replaces the current transform with a non-uniform scale transform.
+ * All values of the existing transform are replaced.
+ * @param xScale the new X scale amount
+ * @param yScale the new Y scale amount
+ * @param zScale the new Z scale amount
+ * @deprecated Use setScale(Vector3d) instead of setNonUniformScale;
+ * note that the setScale only modifies the scale component
+ */
+ public final void setNonUniformScale(double xScale,
+ double yScale,
+ double zScale) {
+ if(scales == null)
+ scales = new double[3];
+
+ scales[0] = xScale;
+ scales[1] = yScale;
+ scales[2] = zScale;
+ mat[0] = xScale;
+ mat[1] = 0.0;
+ mat[2] = 0.0;
+ mat[3] = 0.0;
+ mat[4] = 0.0;
+ mat[5] = yScale;
+ mat[6] = 0.0;
+ mat[7] = 0.0;
+ mat[8] = 0.0;
+ mat[9] = 0.0;
+ mat[10] = zScale;
+ mat[11] = 0.0;
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = AFFINE | ORTHO;
+ dirtyBits = CLASSIFY_BIT | CONGRUENT_BIT | RIGID_BIT |
+ ROTATION_BIT;
+ }
+
+ /**
+ * Replaces the translational components of this transform to the values
+ * in the Vector3f argument; the other values of this transform are not
+ * modified.
+ * @param trans the translational component
+ */
+ public final void setTranslation(Vector3f trans) {
+ mat[3] = trans.x;
+ mat[7] = trans.y;
+ mat[11] = trans.z;
+ // Only preserve CONGRUENT, RIGID, ORTHO
+ type &= ~(ORTHOGONAL|IDENTITY|SCALE|TRANSLATION|SCALE|ZERO);
+ dirtyBits |= CLASSIFY_BIT;
+ }
+
+
+ /**
+ * Replaces the translational components of this transform to the values
+ * in the Vector3d argument; the other values of this transform are not
+ * modified.
+ * @param trans the translational component
+ */
+ public final void setTranslation(Vector3d trans) {
+ mat[3] = trans.x;
+ mat[7] = trans.y;
+ mat[11] = trans.z;
+ type &= ~(ORTHOGONAL|IDENTITY|SCALE|TRANSLATION|SCALE|ZERO);
+ dirtyBits |= CLASSIFY_BIT;
+ }
+
+
+ /**
+ * Sets the value of this matrix from the rotation expressed
+ * by the quaternion q1, the translation t1, and the scale s.
+ * @param q1 the rotation expressed as a quaternion
+ * @param t1 the translation
+ * @param s the scale value
+ */
+ public final void set(Quat4d q1, Vector3d t1, double s) {
+ if(scales == null)
+ scales = new double[3];
+
+ scales[0] = scales[1] = scales[2] = s;
+
+ mat[0] = (1.0 - 2.0*q1.y*q1.y - 2.0*q1.z*q1.z)*s;
+ mat[4] = (2.0*(q1.x*q1.y + q1.w*q1.z))*s;
+ mat[8] = (2.0*(q1.x*q1.z - q1.w*q1.y))*s;
+
+ mat[1] = (2.0*(q1.x*q1.y - q1.w*q1.z))*s;
+ mat[5] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.z*q1.z)*s;
+ mat[9] = (2.0*(q1.y*q1.z + q1.w*q1.x))*s;
+
+ mat[2] = (2.0*(q1.x*q1.z + q1.w*q1.y))*s;
+ mat[6] = (2.0*(q1.y*q1.z - q1.w*q1.x))*s;
+ mat[10] = (1.0 - 2.0*q1.x*q1.x - 2.0*q1.y*q1.y)*s;
+
+ mat[3] = t1.x;
+ mat[7] = t1.y;
+ mat[11] = t1.z;
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+ type = CONGRUENT | AFFINE | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | RIGID_BIT;
+ }
+
+ /**
+ * Sets the value of this matrix from the rotation expressed
+ * by the quaternion q1, the translation t1, and the scale s.
+ * @param q1 the rotation expressed as a quaternion
+ * @param t1 the translation
+ * @param s the scale value
+ */
+ public final void set(Quat4f q1, Vector3d t1, double s) {
+ if(scales == null)
+ scales = new double[3];
+
+ scales[0] = scales[1] = scales[2] = s;
+
+ mat[0] = (1.0f - 2.0f*q1.y*q1.y - 2.0f*q1.z*q1.z)*s;
+ mat[4] = (2.0f*(q1.x*q1.y + q1.w*q1.z))*s;
+ mat[8] = (2.0f*(q1.x*q1.z - q1.w*q1.y))*s;
+
+ mat[1] = (2.0f*(q1.x*q1.y - q1.w*q1.z))*s;
+ mat[5] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.z*q1.z)*s;
+ mat[9] = (2.0f*(q1.y*q1.z + q1.w*q1.x))*s;
+
+ mat[2] = (2.0f*(q1.x*q1.z + q1.w*q1.y))*s;
+ mat[6] = (2.0f*(q1.y*q1.z - q1.w*q1.x))*s;
+ mat[10] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.y*q1.y)*s;
+
+ mat[3] = t1.x;
+ mat[7] = t1.y;
+ mat[11] = t1.z;
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT| RIGID_BIT;
+ }
+
+ /**
+ * Sets the value of this matrix from the rotation expressed
+ * by the quaternion q1, the translation t1, and the scale s.
+ * @param q1 the rotation expressed as a quaternion
+ * @param t1 the translation
+ * @param s the scale value
+ */
+ public final void set(Quat4f q1, Vector3f t1, float s) {
+ if(scales == null)
+ scales = new double[3];
+
+ scales[0] = scales[1] = scales[2] = s;
+
+ mat[0] = (1.0f - 2.0f*q1.y*q1.y - 2.0f*q1.z*q1.z)*s;
+ mat[4] = (2.0f*(q1.x*q1.y + q1.w*q1.z))*s;
+ mat[8] = (2.0f*(q1.x*q1.z - q1.w*q1.y))*s;
+
+ mat[1] = (2.0f*(q1.x*q1.y - q1.w*q1.z))*s;
+ mat[5] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.z*q1.z)*s;
+ mat[9] = (2.0f*(q1.y*q1.z + q1.w*q1.x))*s;
+
+ mat[2] = (2.0f*(q1.x*q1.z + q1.w*q1.y))*s;
+ mat[6] = (2.0f*(q1.y*q1.z - q1.w*q1.x))*s;
+ mat[10] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.y*q1.y)*s;
+
+ mat[3] = t1.x;
+ mat[7] = t1.y;
+ mat[11] = t1.z;
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = CONGRUENT | AFFINE | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | RIGID_BIT;
+ }
+
+ /**
+ * Sets the value of this matrix from the rotation expressed
+ * by the rotation matrix m1, the translation t1, and the scale s.
+ * The scale is only applied to the
+ * rotational component of the matrix (upper 3x3) and not to the
+ * translational component of the matrix.
+ * @param m1 the rotation matrix
+ * @param t1 the translation
+ * @param s the scale value
+ */
+ public final void set(Matrix3f m1, Vector3f t1, float s) {
+ mat[0]=m1.m00*s;
+ mat[1]=m1.m01*s;
+ mat[2]=m1.m02*s;
+ mat[3]=t1.x;
+ mat[4]=m1.m10*s;
+ mat[5]=m1.m11*s;
+ mat[6]=m1.m12*s;
+ mat[7]=t1.y;
+ mat[8]=m1.m20*s;
+ mat[9]=m1.m21*s;
+ mat[10]=m1.m22*s;
+ mat[11]=t1.z;
+ mat[12]=0.0;
+ mat[13]=0.0;
+ mat[14]=0.0;
+ mat[15]=1.0;
+
+ type = AFFINE;
+ dirtyBits = ORTHO_BIT | CONGRUENT_BIT | RIGID_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ // input matrix may not normalize
+ normalize();
+ }
+ }
+
+ /**
+ * Sets the value of this matrix from the rotation expressed
+ * by the rotation matrix m1, the translation t1, and the scale s.
+ * The scale is only applied to the
+ * rotational component of the matrix (upper 3x3) and not to the
+ * translational component of the matrix.
+ * @param m1 the rotation matrix
+ * @param t1 the translation
+ * @param s the scale value
+ */
+ public final void set(Matrix3f m1, Vector3d t1, double s) {
+ mat[0]=m1.m00*s;
+ mat[1]=m1.m01*s;
+ mat[2]=m1.m02*s;
+ mat[3]=t1.x;
+ mat[4]=m1.m10*s;
+ mat[5]=m1.m11*s;
+ mat[6]=m1.m12*s;
+ mat[7]=t1.y;
+ mat[8]=m1.m20*s;
+ mat[9]=m1.m21*s;
+ mat[10]=m1.m22*s;
+ mat[11]=t1.z;
+ mat[12]=0.0;
+ mat[13]=0.0;
+ mat[14]=0.0;
+ mat[15]=1.0;
+
+ type = AFFINE;
+ dirtyBits = ORTHO_BIT | CONGRUENT_BIT | RIGID_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+ /**
+ * Sets the value of this matrix from the rotation expressed
+ * by the rotation matrix m1, the translation t1, and the scale s.
+ * The scale is only applied to the
+ * rotational component of the matrix (upper 3x3) and not to the
+ * translational component of the matrix.
+ * @param m1 the rotation matrix
+ * @param t1 the translation
+ * @param s the scale value
+ */
+ public final void set(Matrix3d m1, Vector3d t1, double s) {
+ mat[0]=m1.m00*s;
+ mat[1]=m1.m01*s;
+ mat[2]=m1.m02*s;
+ mat[3]=t1.x;
+ mat[4]=m1.m10*s;
+ mat[5]=m1.m11*s;
+ mat[6]=m1.m12*s;
+ mat[7]=t1.y;
+ mat[8]=m1.m20*s;
+ mat[9]=m1.m21*s;
+ mat[10]=m1.m22*s;
+ mat[11]=t1.z;
+ mat[12]=0.0;
+ mat[13]=0.0;
+ mat[14]=0.0;
+ mat[15]=1.0;
+
+ type = AFFINE;
+ dirtyBits = ORTHO_BIT | CONGRUENT_BIT | RIGID_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+ /**
+ * Sets the matrix values of this transform to the matrix values in the
+ * upper 4x4 corner of the GMatrix parameter. If the parameter matrix is
+ * smaller than 4x4, the remaining elements in the transform matrix are
+ * assigned to zero. The transform matrix type is classified
+ * internally by the Transform3D class.
+ * @param matrix the general matrix from which the Transform3D matrix is derived
+ */
+ public final void set(GMatrix matrix) {
+ int i,j, k;
+ int numRows = matrix.getNumRow();
+ int numCol = matrix.getNumCol();
+
+ for(i=0 ; i<4 ; i++) {
+ k = i*4;
+ for(j=0 ; j<4 ; j++) {
+ if(i>=numRows || j>=numCol)
+ mat[k+j] = 0.0;
+ else
+ mat[k+j] = matrix.getElement(i,j);
+ }
+ }
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+ /**
+ * Sets the matrix, type, and state of this transform to the matrix,
+ * type, and state of transform t1.
+ * @param t1 the transform to be copied
+ */
+ public final void set(Transform3D t1){
+ mat[0] = t1.mat[0];
+ mat[1] = t1.mat[1];
+ mat[2] = t1.mat[2];
+ mat[3] = t1.mat[3];
+ mat[4] = t1.mat[4];
+ mat[5] = t1.mat[5];
+ mat[6] = t1.mat[6];
+ mat[7] = t1.mat[7];
+ mat[8] = t1.mat[8];
+ mat[9] = t1.mat[9];
+ mat[10] = t1.mat[10];
+ mat[11] = t1.mat[11];
+ mat[12] = t1.mat[12];
+ mat[13] = t1.mat[13];
+ mat[14] = t1.mat[14];
+ mat[15] = t1.mat[15];
+ type = t1.type;
+
+ // don't copy rot[] and scales[]
+ dirtyBits = t1.dirtyBits | ROTATION_BIT | SCALE_BIT;
+ autoNormalize = t1.autoNormalize;
+ }
+
+ // This version gets a lock before doing the set. It is used internally
+ synchronized void setWithLock(Transform3D t1) {
+ this.set(t1);
+ }
+
+ // This version gets a lock before doing the get. It is used internally
+ synchronized void getWithLock(Transform3D t1) {
+ t1.set(this);
+ }
+
+ /**
+ * Sets the matrix values of this transform to the matrix values in the
+ * double precision array parameter. The matrix type is classified
+ * internally by the Transform3D class.
+ * @param matrix the double precision array of length 16 in row major format
+ */
+ public final void set(double[] matrix) {
+ mat[0] = matrix[0];
+ mat[1] = matrix[1];
+ mat[2] = matrix[2];
+ mat[3] = matrix[3];
+ mat[4] = matrix[4];
+ mat[5] = matrix[5];
+ mat[6] = matrix[6];
+ mat[7] = matrix[7];
+ mat[8] = matrix[8];
+ mat[9] = matrix[9];
+ mat[10] = matrix[10];
+ mat[11] = matrix[11];
+ mat[12] = matrix[12];
+ mat[13] = matrix[13];
+ mat[14] = matrix[14];
+ mat[15] = matrix[15];
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+ /**
+ * Sets the matrix values of this transform to the matrix values in the
+ * single precision array parameter. The matrix type is classified
+ * internally by the Transform3D class.
+ * @param matrix the single precision array of length 16 in row major format
+ */
+ public final void set(float[] matrix) {
+ mat[0] = matrix[0];
+ mat[1] = matrix[1];
+ mat[2] = matrix[2];
+ mat[3] = matrix[3];
+ mat[4] = matrix[4];
+ mat[5] = matrix[5];
+ mat[6] = matrix[6];
+ mat[7] = matrix[7];
+ mat[8] = matrix[8];
+ mat[9] = matrix[9];
+ mat[10] = matrix[10];
+ mat[11] = matrix[11];
+ mat[12] = matrix[12];
+ mat[13] = matrix[13];
+ mat[14] = matrix[14];
+ mat[15] = matrix[15];
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+ /**
+ * Sets the matrix values of this transform to the matrix values in the
+ * double precision Matrix4d argument. The transform type is classified
+ * internally by the Transform3D class.
+ * @param m1 the double precision 4x4 matrix
+ */
+ public final void set(Matrix4d m1) {
+ mat[0] = m1.m00;
+ mat[1] = m1.m01;
+ mat[2] = m1.m02;
+ mat[3] = m1.m03;
+ mat[4] = m1.m10;
+ mat[5] = m1.m11;
+ mat[6] = m1.m12;
+ mat[7] = m1.m13;
+ mat[8] = m1.m20;
+ mat[9] = m1.m21;
+ mat[10] = m1.m22;
+ mat[11] = m1.m23;
+ mat[12] = m1.m30;
+ mat[13] = m1.m31;
+ mat[14] = m1.m32;
+ mat[15] = m1.m33;
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Sets the matrix values of this transform to the matrix values in the
+ * single precision Matrix4f argument. The transform type is classified
+ * internally by the Transform3D class.
+ * @param m1 the single precision 4x4 matrix
+ */
+ public final void set(Matrix4f m1) {
+ mat[0] = m1.m00;
+ mat[1] = m1.m01;
+ mat[2] = m1.m02;
+ mat[3] = m1.m03;
+ mat[4] = m1.m10;
+ mat[5] = m1.m11;
+ mat[6] = m1.m12;
+ mat[7] = m1.m13;
+ mat[8] = m1.m20;
+ mat[9] = m1.m21;
+ mat[10] = m1.m22;
+ mat[11] = m1.m23;
+ mat[12] = m1.m30;
+ mat[13] = m1.m31;
+ mat[14] = m1.m32;
+ mat[15] = m1.m33;
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix values in the single precision Matrix3f argument; the other
+ * elements of this transform are initialized as if this were an identity
+ * matrix (i.e., affine matrix with no translational component).
+ * @param m1 the single precision 3x3 matrix
+ */
+ public final void set(Matrix3f m1) {
+ mat[0] = m1.m00;
+ mat[1] = m1.m01;
+ mat[2] = m1.m02;
+ mat[3] = 0.0;
+ mat[4] = m1.m10;
+ mat[5] = m1.m11;
+ mat[6] = m1.m12;
+ mat[7] = 0.0;
+ mat[8] = m1.m20;
+ mat[9] = m1.m21;
+ mat[10] = m1.m22;
+ mat[11] = 0.0;
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = AFFINE;
+ dirtyBits = ORTHO_BIT | CONGRUENT_BIT | RIGID_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * matrix values in the double precision Matrix3d argument; the other
+ * elements of this transform are initialized as if this were an identity
+ * matrix (ie, affine matrix with no translational component).
+ * @param m1 the double precision 3x3 matrix
+ */
+ public final void set(Matrix3d m1) {
+ mat[0] = m1.m00;
+ mat[1] = m1.m01;
+ mat[2] = m1.m02;
+ mat[3] = 0.0;
+ mat[4] = m1.m10;
+ mat[5] = m1.m11;
+ mat[6] = m1.m12;
+ mat[7] = 0.0;
+ mat[8] = m1.m20;
+ mat[9] = m1.m21;
+ mat[10] = m1.m22;
+ mat[11] = 0.0;
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = AFFINE;
+ dirtyBits = ORTHO_BIT | CONGRUENT_BIT | RIGID_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+
+ /**
+ * Sets the rotational component (upper 3x3) of this transform to the
+ * rotation matrix converted from the Euler angles provided; the other
+ * non-rotational elements are set as if this were an identity matrix.
+ * The euler parameter is a Vector3d consisting of three rotation angles
+ * applied first about the X, then Y then Z axis.
+ * These rotations are applied using a static frame of reference. In
+ * other words, the orientation of the Y rotation axis is not affected
+ * by the X rotation and the orientation of the Z rotation axis is not
+ * affected by the X or Y rotation.
+ * @param euler the Vector3d consisting of three rotation angles about X,Y,Z
+ *
+ */
+ public final void setEuler(Vector3d euler) {
+ double sina, sinb, sinc;
+ double cosa, cosb, cosc;
+
+ sina = Math.sin(euler.x);
+ sinb = Math.sin(euler.y);
+ sinc = Math.sin(euler.z);
+ cosa = Math.cos(euler.x);
+ cosb = Math.cos(euler.y);
+ cosc = Math.cos(euler.z);
+
+ mat[0] = cosb * cosc;
+ mat[1] = -(cosa * sinc) + (sina * sinb * cosc);
+ mat[2] = (sina * sinc) + (cosa * sinb *cosc);
+ mat[3] = 0.0;
+
+ mat[4] = cosb * sinc;
+ mat[5] = (cosa * cosc) + (sina * sinb * sinc);
+ mat[6] = -(sina * cosc) + (cosa * sinb *sinc);
+ mat[7] = 0.0;
+
+ mat[8] = -sinb;
+ mat[9] = sina * cosb;
+ mat[10] = cosa * cosb;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ type = AFFINE | CONGRUENT | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | SCALE_BIT | ROTATION_BIT;
+ }
+
+
+ /**
+ * Places the values of this transform into the double precision array
+ * of length 16. The first four elements of the array will contain
+ * the top row of the transform matrix, etc.
+ * @param matrix the double precision array of length 16
+ */
+ public final void get(double[] matrix) {
+ matrix[0] = mat[0];
+ matrix[1] = mat[1];
+ matrix[2] = mat[2];
+ matrix[3] = mat[3];
+ matrix[4] = mat[4];
+ matrix[5] = mat[5];
+ matrix[6] = mat[6];
+ matrix[7] = mat[7];
+ matrix[8] = mat[8];
+ matrix[9] = mat[9];
+ matrix[10] = mat[10];
+ matrix[11] = mat[11];
+ matrix[12] = mat[12];
+ matrix[13] = mat[13];
+ matrix[14] = mat[14];
+ matrix[15] = mat[15];
+ }
+
+
+ /**
+ * Places the values of this transform into the single precision array
+ * of length 16. The first four elements of the array will contain
+ * the top row of the transform matrix, etc.
+ * @param matrix the single precision array of length 16
+ */
+ public final void get(float[] matrix) {
+ matrix[0] = (float) mat[0];
+ matrix[1] = (float) mat[1];
+ matrix[2] = (float) mat[2];
+ matrix[3] = (float) mat[3];
+ matrix[4] = (float) mat[4];
+ matrix[5] = (float) mat[5];
+ matrix[6] = (float) mat[6];
+ matrix[7] = (float) mat[7];
+ matrix[8] = (float) mat[8];
+ matrix[9] = (float) mat[9];
+ matrix[10] = (float) mat[10];
+ matrix[11] = (float) mat[11];
+ matrix[12] = (float) mat[12];
+ matrix[13] = (float) mat[13];
+ matrix[14] = (float) mat[14];
+ matrix[15] = (float) mat[15];
+ }
+
+
+ /**
+ * Places the normalized rotational component of this transform
+ * into the 3x3 matrix argument.
+ * @param m1 the matrix into which the rotational component is placed
+ */
+ public final void get(Matrix3d m1) {
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ }
+ m1.m00 = rot[0];
+ m1.m01 = rot[1];
+ m1.m02 = rot[2];
+ m1.m10 = rot[3];
+ m1.m11 = rot[4];
+ m1.m12 = rot[5];
+ m1.m20 = rot[6];
+ m1.m21 = rot[7];
+ m1.m22 = rot[8];
+ }
+
+ /**
+ * Places the normalized rotational component of this transform
+ * into the 3x3 matrix argument.
+ * @param m1 the matrix into which the rotational component is placed
+ */
+ public final void get(Matrix3f m1) {
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ }
+ m1.m00 = (float)rot[0];
+ m1.m01 = (float)rot[1];
+ m1.m02 = (float)rot[2];
+ m1.m10 = (float)rot[3];
+ m1.m11 = (float)rot[4];
+ m1.m12 = (float)rot[5];
+ m1.m20 = (float)rot[6];
+ m1.m21 = (float)rot[7];
+ m1.m22 = (float)rot[8];
+ }
+
+ /**
+ * Places the quaternion equivalent of the normalized rotational
+ * component of this transform into the quaternion parameter.
+ * @param q1 the quaternion into which the rotation component is placed
+ */
+ public final void get(Quat4f q1) {
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ }
+
+ double ww = 0.25*(1.0 + rot[0] + rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < 1.0e-10)) {
+ q1.w = (float)Math.sqrt(ww);
+ ww = 0.25/q1.w;
+ q1.x = (float)((rot[7] - rot[5])*ww);
+ q1.y = (float)((rot[2] - rot[6])*ww);
+ q1.z = (float)((rot[3] - rot[1])*ww);
+ return;
+ }
+
+ q1.w = 0.0f;
+ ww = -0.5*(rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < 1.0e-10)) {
+ q1.x = (float)Math.sqrt(ww);
+ ww = 0.5/q1.x;
+ q1.y = (float)(rot[3]*ww);
+ q1.z = (float)(rot[6]*ww);
+ return;
+ }
+
+ q1.x = 0.0f;
+ ww = 0.5*(1.0 - rot[8]);
+ if (!((ww < 0 ? -ww : ww) < 1.0e-10)) {
+ q1.y = (float)Math.sqrt(ww);
+ q1.z = (float)(rot[7]/(2.0*q1.y));
+ return;
+ }
+
+ q1.y = 0.0f;
+ q1.z = 1.0f;
+ }
+
+ /**
+ * Places the quaternion equivalent of the normalized rotational
+ * component of this transform into the quaternion parameter.
+ * @param q1 the quaternion into which the rotation component is placed
+ */
+ public final void get(Quat4d q1) {
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ }
+
+ double ww = 0.25*(1.0 + rot[0] + rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < 1.0e-10)) {
+ q1.w = Math.sqrt(ww);
+ ww = 0.25/q1.w;
+ q1.x = (rot[7] - rot[5])*ww;
+ q1.y = (rot[2] - rot[6])*ww;
+ q1.z = (rot[3] - rot[1])*ww;
+ return;
+ }
+
+ q1.w = 0.0;
+ ww = -0.5*(rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < 1.0e-10)) {
+ q1.x = Math.sqrt(ww);
+ ww = 0.5/q1.x;
+ q1.y = rot[3]*ww;
+ q1.z = rot[6]*ww;
+ return;
+ }
+
+ q1.x = 0.0;
+ ww = 0.5*(1.0 - rot[8]);
+ if (!((ww < 0 ? -ww : ww) < 1.0e-10)) {
+ q1.y = Math.sqrt(ww);
+ q1.z = rot[7]/(2.0*q1.y);
+ return;
+ }
+
+ q1.y = 0.0;
+ q1.z = 1.0;
+ }
+
+ /**
+ * Places the values of this transform into the double precision
+ * matrix argument.
+ * @param matrix the double precision matrix
+ */
+ public final void get(Matrix4d matrix) {
+ matrix.m00 = mat[0];
+ matrix.m01 = mat[1];
+ matrix.m02 = mat[2];
+ matrix.m03 = mat[3];
+ matrix.m10 = mat[4];
+ matrix.m11 = mat[5];
+ matrix.m12 = mat[6];
+ matrix.m13 = mat[7];
+ matrix.m20 = mat[8];
+ matrix.m21 = mat[9];
+ matrix.m22 = mat[10];
+ matrix.m23 = mat[11];
+ matrix.m30 = mat[12];
+ matrix.m31 = mat[13];
+ matrix.m32 = mat[14];
+ matrix.m33 = mat[15];
+ }
+
+ /**
+ * Places the values of this transform into the single precision matrix
+ * argument.
+ * @param matrix the single precision matrix
+ */
+ public final void get(Matrix4f matrix) {
+ matrix.m00 = (float) mat[0];
+ matrix.m01 = (float) mat[1];
+ matrix.m02 = (float) mat[2];
+ matrix.m03 = (float) mat[3];
+ matrix.m10 = (float) mat[4];
+ matrix.m11 = (float) mat[5];
+ matrix.m12 = (float) mat[6];
+ matrix.m13 = (float) mat[7];
+ matrix.m20 = (float) mat[8];
+ matrix.m21 = (float) mat[9];
+ matrix.m22 = (float) mat[10];
+ matrix.m23 = (float) mat[11];
+ matrix.m30 = (float) mat[12];
+ matrix.m31 = (float) mat[13];
+ matrix.m32 = (float) mat[14];
+ matrix.m33 = (float) mat[15];
+ }
+
+ /**
+ * Places the quaternion equivalent of the normalized rotational
+ * component of this transform into the quaternion parameter;
+ * places the translational component into the Vector parameter.
+ * @param q1 the quaternion representing the rotation
+ * @param t1 the translation component
+ * @return the scale component of this transform
+ */
+ public final double get(Quat4d q1, Vector3d t1) {
+
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ } else if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+
+ t1.x = mat[3];
+ t1.y = mat[7];
+ t1.z = mat[11];
+
+ double maxScale = max3(scales);
+
+ double ww = 0.25*(1.0 + rot[0] + rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.w = Math.sqrt(ww);
+ ww = 0.25/q1.w;
+ q1.x = (rot[7] - rot[5])*ww;
+ q1.y = (rot[2] - rot[6])*ww;
+ q1.z = (rot[3] - rot[1])*ww;
+ return maxScale;
+ }
+
+ q1.w = 0.0;
+ ww = -0.5*(rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.x = Math.sqrt(ww);
+ ww = 0.5/q1.x;
+ q1.y = rot[3]*ww;
+ q1.z = rot[6]*ww;
+ return maxScale;
+ }
+
+ q1.x = 0.0;
+ ww = 0.5*(1.0 - rot[8]);
+ if (!((ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.y = Math.sqrt(ww);
+ q1.z = rot[7]/(2.0*q1.y);
+ return maxScale;
+ }
+
+ q1.y = 0.0;
+ q1.z = 1.0;
+ return maxScale;
+ }
+
+
+ /**
+ * Places the quaternion equivalent of the normalized rotational
+ * component of this transform into the quaternion parameter;
+ * places the translational component into the Vector parameter.
+ * @param q1 the quaternion representing the rotation
+ * @param t1 the translation component
+ * @return the scale component of this transform
+ */
+ public final float get(Quat4f q1, Vector3f t1) {
+
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ } else if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+
+ double maxScale = max3(scales);
+ t1.x = (float)mat[3];
+ t1.y = (float)mat[7];
+ t1.z = (float)mat[11];
+
+ double ww = 0.25*(1.0 + rot[0] + rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.w = (float)Math.sqrt(ww);
+ ww = 0.25/q1.w;
+ q1.x = (float)((rot[7] - rot[5])*ww);
+ q1.y = (float)((rot[2] - rot[6])*ww);
+ q1.z = (float)((rot[3] - rot[1])*ww);
+ return (float) maxScale;
+ }
+
+ q1.w = 0.0f;
+ ww = -0.5*(rot[4] + rot[8]);
+ if (!((ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.x = (float)Math.sqrt(ww);
+ ww = 0.5/q1.x;
+ q1.y = (float)(rot[3]*ww);
+ q1.z = (float)(rot[6]*ww);
+ return (float) maxScale;
+ }
+
+ q1.x = 0.0f;
+ ww = 0.5*(1.0 - rot[8]);
+ if (!((ww < 0? -ww : ww) < EPSILON)) {
+ q1.y = (float)Math.sqrt(ww);
+ q1.z = (float)(rot[7]/(2.0*q1.y));
+ return (float) maxScale;
+ }
+
+ q1.y = 0.0f;
+ q1.z = 1.0f;
+ return (float) maxScale;
+ }
+
+ /**
+ * Places the quaternion equivalent of the normalized rotational
+ * component of this transform into the quaternion parameter;
+ * places the translational component into the Vector parameter.
+ * @param q1 the quaternion representing the rotation
+ * @param t1 the translation component
+ * @return the scale component of this transform
+ */
+ public final double get(Quat4f q1, Vector3d t1) {
+
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ } else if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+
+ double maxScale = max3(scales);
+
+ t1.x = mat[3];
+ t1.y = mat[7];
+ t1.z = mat[11];
+
+ double ww = 0.25*(1.0 + rot[0] + rot[4] + rot[8]);
+ if (!(( ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.w = (float)Math.sqrt(ww);
+ ww = 0.25/q1.w;
+ q1.x = (float)((rot[7] - rot[5])*ww);
+ q1.y = (float)((rot[2] - rot[6])*ww);
+ q1.z = (float)((rot[3] - rot[1])*ww);
+ return maxScale;
+ }
+
+ q1.w = 0.0f;
+ ww = -0.5*(rot[4] + rot[8]);
+ if (!(( ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.x = (float)Math.sqrt(ww);
+ ww = 0.5/q1.x;
+ q1.y = (float)(rot[3]*ww);
+ q1.z = (float)(rot[6]*ww);
+ return maxScale;
+ }
+
+ q1.x = 0.0f;
+ ww = 0.5*(1.0 - rot[8]);
+ if (!(( ww < 0 ? -ww : ww) < EPSILON)) {
+ q1.y = (float)Math.sqrt(ww);
+ q1.z = (float)(rot[7]/(2.0*q1.y));
+ return maxScale;
+ }
+
+ q1.y = 0.0f;
+ q1.z = 1.0f;
+ return maxScale;
+ }
+
+ /**
+ * Places the normalized rotational component of this transform
+ * into the matrix parameter; place the translational component
+ * into the vector parameter.
+ * @param m1 the normalized matrix representing the rotation
+ * @param t1 the translation component
+ * @return the scale component of this transform
+ */
+ public final double get(Matrix3d m1, Vector3d t1) {
+
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ } else if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+
+ t1.x = mat[3];
+ t1.y = mat[7];
+ t1.z = mat[11];
+
+ m1.m00 = rot[0];
+ m1.m01 = rot[1];
+ m1.m02 = rot[2];
+
+ m1.m10 = rot[3];
+ m1.m11 = rot[4];
+ m1.m12 = rot[5];
+
+ m1.m20 = rot[6];
+ m1.m21 = rot[7];
+ m1.m22 = rot[8];
+
+ return max3(scales);
+ }
+
+
+ /**
+ * Places the normalized rotational component of this transform
+ * into the matrix parameter; place the translational component
+ * into the vector parameter.
+ * @param m1 the normalized matrix representing the rotation
+ * @param t1 the translation component
+ * @return the scale component of this transform
+ */
+ public final float get(Matrix3f m1, Vector3f t1) {
+
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ } else if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+
+ t1.x = (float)mat[3];
+ t1.y = (float)mat[7];
+ t1.z = (float)mat[11];
+
+ m1.m00 = (float)rot[0];
+ m1.m01 = (float)rot[1];
+ m1.m02 = (float)rot[2];
+
+ m1.m10 = (float)rot[3];
+ m1.m11 = (float)rot[4];
+ m1.m12 = (float)rot[5];
+
+ m1.m20 = (float)rot[6];
+ m1.m21 = (float)rot[7];
+ m1.m22 = (float)rot[8];
+
+ return (float) max3(scales);
+ }
+
+
+ /**
+ * Places the normalized rotational component of this transform
+ * into the matrix parameter; place the translational component
+ * into the vector parameter.
+ * @param m1 the normalized matrix representing the rotation
+ * @param t1 the translation component
+ * @return the scale component of this transform
+ */
+ public final double get(Matrix3f m1, Vector3d t1) {
+ if ((dirtyBits & ROTATION_BIT) != 0) {
+ computeScaleRotation(false);
+ } else if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+
+ t1.x = mat[3];
+ t1.y = mat[7];
+ t1.z = mat[11];
+
+ m1.m00 = (float)rot[0];
+ m1.m01 = (float)rot[1];
+ m1.m02 = (float)rot[2];
+
+ m1.m10 = (float)rot[3];
+ m1.m11 = (float)rot[4];
+ m1.m12 = (float)rot[5];
+
+ m1.m20 = (float)rot[6];
+ m1.m21 = (float)rot[7];
+ m1.m22 = (float)rot[8];
+
+ return max3(scales);
+ }
+
+
+ /**
+ * Returns the uniform scale factor of this matrix.
+ * If the matrix has non-uniform scale factors, the largest of the
+ * x, y, and z scale factors will be returned.
+ * @return the scale factor of this matrix
+ */
+ public final double getScale() {
+ if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+ return max3(scales);
+ }
+
+
+ /**
+ * Gets the possibly non-uniform scale components of the current
+ * transform and places them into the scale vector.
+ * @param scale the vector into which the x,y,z scale values will be placed
+ */
+ public final void getScale(Vector3d scale) {
+ if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+ scale.x = scales[0];
+ scale.y = scales[1];
+ scale.z = scales[2];
+ }
+
+
+ /**
+ * Retrieves the translational components of this transform.
+ * @param trans the vector that will receive the translational component
+ */
+ public final void get(Vector3f trans) {
+ trans.x = (float)mat[3];
+ trans.y = (float)mat[7];
+ trans.z = (float)mat[11];
+ }
+
+
+ /**
+ * Retrieves the translational components of this transform.
+ * @param trans the vector that will receive the translational component
+ */
+ public final void get(Vector3d trans) {
+ trans.x = mat[3];
+ trans.y = mat[7];
+ trans.z = mat[11];
+ }
+
+ /**
+ * Sets the value of this transform to the inverse of the passed
+ * Transform3D parameter. This method uses the transform type
+ * to determine the optimal algorithm for inverting transform t1.
+ * @param t1 the transform to be inverted
+ * @exception SingularMatrixException thrown if transform t1 is
+ * not invertible
+ */
+ public final void invert(Transform3D t1) {
+ if (t1 == this) {
+ invert();
+ } else if (t1.isAffine()) {
+ // We can't use invertOrtho() because of numerical
+ // instability unless we set tolerance of ortho test to 0
+ invertAffine(t1);
+ } else {
+ invertGeneral(t1);
+ }
+ }
+
+ /**
+ * Inverts this transform in place. This method uses the transform
+ * type to determine the optimal algorithm for inverting this transform.
+ * @exception SingularMatrixException thrown if this transform is
+ * not invertible
+ */
+ public final void invert() {
+ if (isAffine()) {
+ invertAffine();
+ } else {
+ invertGeneral(this);
+ }
+ }
+
+ /**
+ * Congruent invert routine.
+ *
+ * if uniform scale s
+ *
+ * [R | t] => [R^T/s*s | -R^T * t/s*s]
+ *
+ */
+
+ /*
+ final void invertOrtho() {
+ double tmp, s1, s2, s3;
+
+ // do not force classifyRigid()
+ if (((dirtyBits & CONGRUENT_BIT) == 0) &&
+ ((type & CONGRUENT) != 0)) {
+ s1 = mat[0]*mat[0] + mat[4]*mat[4] + mat[8]*mat[8];
+ if (s1 == 0) {
+ throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
+ }
+ s1 = s2 = s3 = 1/s1;
+ dirtyBits |= ROTSCALESVD_DIRTY;
+ } else {
+ // non-uniform scale matrix
+ s1 = mat[0]*mat[0] + mat[4]*mat[4] + mat[8]*mat[8];
+ s2 = mat[1]*mat[1] + mat[5]*mat[5] + mat[9]*mat[9];
+ s3 = mat[2]*mat[2] + mat[6]*mat[6] + mat[10]*mat[10];
+ if ((s1 == 0) || (s2 == 0) || (s3 == 0)) {
+ throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
+ }
+ s1 = 1/s1;
+ s2 = 1/s2;
+ s3 = 1/s3;
+ dirtyBits |= ROTSCALESVD_DIRTY | ORTHO_BIT | CONGRUENT_BIT
+ | RIGID_BIT | CLASSIFY_BIT;
+ }
+ // multiple by 1/s will cause loss in numerical value
+ tmp = mat[1];
+ mat[1] = mat[4]*s1;
+ mat[4] = tmp*s2;
+ tmp = mat[2];
+ mat[2] = mat[8]*s1;
+ mat[8] = tmp*s3;
+ tmp = mat[6];
+ mat[6] = mat[9]*s2;
+ mat[9] = tmp*s3;
+ mat[0] *= s1;
+ mat[5] *= s2;
+ mat[10] *= s3;
+
+ tmp = mat[3];
+ s1 = mat[7];
+ mat[3] = -(tmp * mat[0] + s1 * mat[1] + mat[11] * mat[2]);
+ mat[7] = -(tmp * mat[4] + s1 * mat[5] + mat[11] * mat[6]);
+ mat[11]= -(tmp * mat[8] + s1 * mat[9] + mat[11] * mat[10]);
+ mat[12] = mat[13] = mat[14] = 0.0;
+ mat[15] = 1.0;
+ }
+ */
+
+ /**
+ * Orthogonal matrix invert routine.
+ * Inverts t1 and places the result in "this".
+ */
+ /*
+ final void invertOrtho(Transform3D t1) {
+ double s1, s2, s3;
+
+ // do not force classifyRigid()
+ if (((t1.dirtyBits & CONGRUENT_BIT) == 0) &&
+ ((t1.type & CONGRUENT) != 0)) {
+ s1 = t1.mat[0]*t1.mat[0] + t1.mat[4]*t1.mat[4] +
+ t1.mat[8]*t1.mat[8];
+ if (s1 == 0) {
+ throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
+ }
+ s1 = s2 = s3 = 1/s1;
+ dirtyBits = t1.dirtyBits | ROTSCALESVD_DIRTY;
+ } else {
+ // non-uniform scale matrix
+ s1 = t1.mat[0]*t1.mat[0] + t1.mat[4]*t1.mat[4] +
+ t1.mat[8]*t1.mat[8];
+ s2 = t1.mat[1]*t1.mat[1] + t1.mat[5]*t1.mat[5] +
+ t1.mat[9]*t1.mat[9];
+ s3 = t1.mat[2]*t1.mat[2] + t1.mat[6]*t1.mat[6] +
+ t1.mat[10]*t1.mat[10];
+
+ if ((s1 == 0) || (s2 == 0) || (s3 == 0)) {
+ throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
+ }
+ s1 = 1/s1;
+ s2 = 1/s2;
+ s3 = 1/s3;
+ dirtyBits = t1.dirtyBits | ROTSCALESVD_DIRTY | ORTHO_BIT |
+ CONGRUENT_BIT | RIGID_BIT;
+ }
+
+ mat[0] = t1.mat[0]*s1;
+ mat[1] = t1.mat[4]*s1;
+ mat[2] = t1.mat[8]*s1;
+ mat[4] = t1.mat[1]*s2;
+ mat[5] = t1.mat[5]*s2;
+ mat[6] = t1.mat[9]*s2;
+ mat[8] = t1.mat[2]*s3;
+ mat[9] = t1.mat[6]*s3;
+ mat[10] = t1.mat[10]*s3;
+
+ mat[3] = -(t1.mat[3] * mat[0] + t1.mat[7] * mat[1] +
+ t1.mat[11] * mat[2]);
+ mat[7] = -(t1.mat[3] * mat[4] + t1.mat[7] * mat[5] +
+ t1.mat[11] * mat[6]);
+ mat[11]= -(t1.mat[3] * mat[8] + t1.mat[7] * mat[9] +
+ t1.mat[11] * mat[10]);
+ mat[12] = mat[13] = mat[14] = 0.0;
+ mat[15] = 1.0;
+ type = t1.type;
+ }
+ */
+
+ /**
+ * Affine invert routine. Inverts t1 and places the result in "this".
+ */
+ final void invertAffine(Transform3D t1) {
+ double determinant = t1.affineDeterminant();
+
+ if (determinant == 0.0)
+ throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
+
+
+ double s = (t1.mat[0]*t1.mat[0] + t1.mat[1]*t1.mat[1] +
+ t1.mat[2]*t1.mat[2] + t1.mat[3]*t1.mat[3])*
+ (t1.mat[4]*t1.mat[4] + t1.mat[5]*t1.mat[5] +
+ t1.mat[6]*t1.mat[6] + t1.mat[7]*t1.mat[7])*
+ (t1.mat[8]*t1.mat[8] + t1.mat[9]*t1.mat[9] +
+ t1.mat[10]*t1.mat[10] + t1.mat[11]*t1.mat[11]);
+
+ if ((determinant*determinant) < (EPS * s)) {
+ // using invertGeneral is numerically more stable for
+ //this case see bug 4227733
+ invertGeneral(t1);
+ return;
+ }
+ s = 1.0 / determinant;
+
+ mat[0] = (t1.mat[5]*t1.mat[10] - t1.mat[9]*t1.mat[6]) * s;
+ mat[1] = -(t1.mat[1]*t1.mat[10] - t1.mat[9]*t1.mat[2]) * s;
+ mat[2] = (t1.mat[1]*t1.mat[6] - t1.mat[5]*t1.mat[2]) * s;
+ mat[4] = -(t1.mat[4]*t1.mat[10] - t1.mat[8]*t1.mat[6]) * s;
+ mat[5] = (t1.mat[0]*t1.mat[10] - t1.mat[8]*t1.mat[2]) * s;
+ mat[6] = -(t1.mat[0]*t1.mat[6] - t1.mat[4]*t1.mat[2]) * s;
+ mat[8] = (t1.mat[4]*t1.mat[9] - t1.mat[8]*t1.mat[5]) * s;
+ mat[9] = -(t1.mat[0]*t1.mat[9] - t1.mat[8]*t1.mat[1]) * s;
+ mat[10]= (t1.mat[0]*t1.mat[5] - t1.mat[4]*t1.mat[1]) * s;
+ mat[3] = -(t1.mat[3] * mat[0] + t1.mat[7] * mat[1] +
+ t1.mat[11] * mat[2]);
+ mat[7] = -(t1.mat[3] * mat[4] + t1.mat[7] * mat[5] +
+ t1.mat[11] * mat[6]);
+ mat[11]= -(t1.mat[3] * mat[8] + t1.mat[7] * mat[9] +
+ t1.mat[11] * mat[10]);
+
+ mat[12] = mat[13] = mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ dirtyBits = t1.dirtyBits | ROTSCALESVD_DIRTY | CLASSIFY_BIT | ORTHO_BIT;
+ type = t1.type;
+ }
+
+ /**
+ * Affine invert routine. Inverts "this" matrix in place.
+ */
+ final void invertAffine() {
+ double determinant = affineDeterminant();
+
+ if (determinant == 0.0)
+ throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
+
+ double s = (mat[0]*mat[0] + mat[1]*mat[1] +
+ mat[2]*mat[2] + mat[3]*mat[3])*
+ (mat[4]*mat[4] + mat[5]*mat[5] +
+ mat[6]*mat[6] + mat[7]*mat[7])*
+ (mat[8]*mat[8] + mat[9]*mat[9] +
+ mat[10]*mat[10] + mat[11]*mat[11]);
+
+ if ((determinant*determinant) < (EPS * s)) {
+ invertGeneral(this);
+ return;
+ }
+ s = 1.0 / determinant;
+ double tmp0 = (mat[5]*mat[10] - mat[9]*mat[6]) * s;
+ double tmp1 = -(mat[1]*mat[10] - mat[9]*mat[2]) * s;
+ double tmp2 = (mat[1]*mat[6] - mat[5]*mat[2]) * s;
+ double tmp4 = -(mat[4]*mat[10] - mat[8]*mat[6]) * s;
+ double tmp5 = (mat[0]*mat[10] - mat[8]*mat[2]) * s;
+ double tmp6 = -(mat[0]*mat[6] - mat[4]*mat[2]) * s;
+ double tmp8 = (mat[4]*mat[9] - mat[8]*mat[5]) * s;
+ double tmp9 = -(mat[0]*mat[9] - mat[8]*mat[1]) * s;
+ double tmp10= (mat[0]*mat[5] - mat[4]*mat[1]) * s;
+ double tmp3 = -(mat[3] * tmp0 + mat[7] * tmp1 + mat[11] * tmp2);
+ double tmp7 = -(mat[3] * tmp4 + mat[7] * tmp5 + mat[11] * tmp6);
+ mat[11]= -(mat[3] * tmp8 + mat[7] * tmp9 + mat[11] * tmp10);
+
+ mat[0]=tmp0; mat[1]=tmp1; mat[2]=tmp2; mat[3]=tmp3;
+ mat[4]=tmp4; mat[5]=tmp5; mat[6]=tmp6; mat[7]=tmp7;
+ mat[8]=tmp8; mat[9]=tmp9; mat[10]=tmp10;
+ mat[12] = mat[13] = mat[14] = 0.0;
+ mat[15] = 1.0;
+ dirtyBits |= ROTSCALESVD_DIRTY | CLASSIFY_BIT | ORTHO_BIT;
+ }
+
+ /**
+ * General invert routine. Inverts t1 and places the result in "this".
+ * Note that this routine handles both the "this" version and the
+ * non-"this" version.
+ *
+ * Also note that since this routine is slow anyway, we won't worry
+ * about allocating a little bit of garbage.
+ */
+ final void invertGeneral(Transform3D t1) {
+ double tmp[] = new double[16];
+ int row_perm[] = new int[4];
+ int i, r, c;
+
+ // Use LU decomposition and backsubstitution code specifically
+ // for floating-point 4x4 matrices.
+
+ // Copy source matrix to tmp
+ System.arraycopy(t1.mat, 0, tmp, 0, tmp.length);
+
+ // Calculate LU decomposition: Is the matrix singular?
+ if (!luDecomposition(tmp, row_perm)) {
+ // Matrix has no inverse
+ throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
+ }
+
+ // Perform back substitution on the identity matrix
+ // luDecomposition will set rot[] & scales[] for use
+ // in luBacksubstituation
+ mat[0] = 1.0; mat[1] = 0.0; mat[2] = 0.0; mat[3] = 0.0;
+ mat[4] = 0.0; mat[5] = 1.0; mat[6] = 0.0; mat[7] = 0.0;
+ mat[8] = 0.0; mat[9] = 0.0; mat[10] = 1.0; mat[11] = 0.0;
+ mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0;
+ luBacksubstitution(tmp, row_perm, this.mat);
+
+ type = 0;
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+ }
+
+
+ /**
+ * Given a 4x4 array "matrix0", this function replaces it with the
+ * LU decomposition of a row-wise permutation of itself. The input
+ * parameters are "matrix0" and "dimen". The array "matrix0" is also
+ * an output parameter. The vector "row_perm[4]" is an output
+ * parameter that contains the row permutations resulting from partial
+ * pivoting. The output parameter "even_row_xchg" is 1 when the
+ * number of row exchanges is even, or -1 otherwise. Assumes data
+ * type is always double.
+ *
+ * This function is similar to luDecomposition, except that it
+ * is tuned specifically for 4x4 matrices.
+ *
+ * @return true if the matrix is nonsingular, or false otherwise.
+ */
+ //
+ // Reference: Press, Flannery, Teukolsky, Vetterling,
+ // _Numerical_Recipes_in_C_, Cambridge University Press,
+ // 1988, pp 40-45.
+ //
+ static boolean luDecomposition(double[] matrix0,
+ int[] row_perm) {
+
+ // Can't re-use this temporary since the method is static.
+ double row_scale[] = new double[4];
+
+ // Determine implicit scaling information by looping over rows
+ {
+ int i, j;
+ int ptr, rs;
+ double big, temp;
+
+ ptr = 0;
+ rs = 0;
+
+ // For each row ...
+ i = 4;
+ while (i-- != 0) {
+ big = 0.0;
+
+ // For each column, find the largest element in the row
+ j = 4;
+ while (j-- != 0) {
+ temp = matrix0[ptr++];
+ temp = Math.abs(temp);
+ if (temp > big) {
+ big = temp;
+ }
+ }
+
+ // Is the matrix singular?
+ if (big == 0.0) {
+ return false;
+ }
+ row_scale[rs++] = 1.0 / big;
+ }
+ }
+
+ {
+ int j;
+ int mtx;
+
+ mtx = 0;
+
+ // For all columns, execute Crout's method
+ for (j = 0; j < 4; j++) {
+ int i, imax, k;
+ int target, p1, p2;
+ double sum, big, temp;
+
+ // Determine elements of upper diagonal matrix U
+ for (i = 0; i < j; i++) {
+ target = mtx + (4*i) + j;
+ sum = matrix0[target];
+ k = i;
+ p1 = mtx + (4*i);
+ p2 = mtx + j;
+ while (k-- != 0) {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 4;
+ }
+ matrix0[target] = sum;
+ }
+
+ // Search for largest pivot element and calculate
+ // intermediate elements of lower diagonal matrix L.
+ big = 0.0;
+ imax = -1;
+ for (i = j; i < 4; i++) {
+ target = mtx + (4*i) + j;
+ sum = matrix0[target];
+ k = j;
+ p1 = mtx + (4*i);
+ p2 = mtx + j;
+ while (k-- != 0) {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 4;
+ }
+ matrix0[target] = sum;
+
+ // Is this the best pivot so far?
+ if ((temp = row_scale[i] * Math.abs(sum)) >= big) {
+ big = temp;
+ imax = i;
+ }
+ }
+
+ if (imax < 0) {
+ return false;
+ }
+
+ // Is a row exchange necessary?
+ if (j != imax) {
+ // Yes: exchange rows
+ k = 4;
+ p1 = mtx + (4*imax);
+ p2 = mtx + (4*j);
+ while (k-- != 0) {
+ temp = matrix0[p1];
+ matrix0[p1++] = matrix0[p2];
+ matrix0[p2++] = temp;
+ }
+
+ // Record change in scale factor
+ row_scale[imax] = row_scale[j];
+ }
+
+ // Record row permutation
+ row_perm[j] = imax;
+
+ // Is the matrix singular
+ if (matrix0[(mtx + (4*j) + j)] == 0.0) {
+ return false;
+ }
+
+ // Divide elements of lower diagonal matrix L by pivot
+ if (j != (4-1)) {
+ temp = 1.0 / (matrix0[(mtx + (4*j) + j)]);
+ target = mtx + (4*(j+1)) + j;
+ i = 3 - j;
+ while (i-- != 0) {
+ matrix0[target] *= temp;
+ target += 4;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Solves a set of linear equations. The input parameters "matrix1",
+ * and "row_perm" come from luDecompostionD4x4 and do not change
+ * here. The parameter "matrix2" is a set of column vectors assembled
+ * into a 4x4 matrix of floating-point values. The procedure takes each
+ * column of "matrix2" in turn and treats it as the right-hand side of the
+ * matrix equation Ax = LUx = b. The solution vector replaces the
+ * original column of the matrix.
+ *
+ * If "matrix2" is the identity matrix, the procedure replaces its contents
+ * with the inverse of the matrix from which "matrix1" was originally
+ * derived.
+ */
+ //
+ // Reference: Press, Flannery, Teukolsky, Vetterling,
+ // _Numerical_Recipes_in_C_, Cambridge University Press,
+ // 1988, pp 44-45.
+ //
+ static void luBacksubstitution(double[] matrix1,
+ int[] row_perm,
+ double[] matrix2) {
+
+ int i, ii, ip, j, k;
+ int rp;
+ int cv, rv;
+
+ // rp = row_perm;
+ rp = 0;
+
+ // For each column vector of matrix2 ...
+ for (k = 0; k < 4; k++) {
+ // cv = &(matrix2[0][k]);
+ cv = k;
+ ii = -1;
+
+ // Forward substitution
+ for (i = 0; i < 4; i++) {
+ double sum;
+
+ ip = row_perm[rp+i];
+ sum = matrix2[cv+4*ip];
+ matrix2[cv+4*ip] = matrix2[cv+4*i];
+ if (ii >= 0) {
+ // rv = &(matrix1[i][0]);
+ rv = i*4;
+ for (j = ii; j <= i-1; j++) {
+ sum -= matrix1[rv+j] * matrix2[cv+4*j];
+ }
+ }
+ else if (sum != 0.0) {
+ ii = i;
+ }
+ matrix2[cv+4*i] = sum;
+ }
+
+ // Backsubstitution
+ // rv = &(matrix1[3][0]);
+ rv = 3*4;
+ matrix2[cv+4*3] /= matrix1[rv+3];
+
+ rv -= 4;
+ matrix2[cv+4*2] = (matrix2[cv+4*2] -
+ matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+2];
+
+ rv -= 4;
+ matrix2[cv+4*1] = (matrix2[cv+4*1] -
+ matrix1[rv+2] * matrix2[cv+4*2] -
+ matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+1];
+
+ rv -= 4;
+ matrix2[cv+4*0] = (matrix2[cv+4*0] -
+ matrix1[rv+1] * matrix2[cv+4*1] -
+ matrix1[rv+2] * matrix2[cv+4*2] -
+ matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+0];
+ }
+ }
+
+ // given that this matrix is affine
+ final double affineDeterminant() {
+ return mat[0]*(mat[5]*mat[10] - mat[6]*mat[9]) -
+ mat[1]*(mat[4]*mat[10] - mat[6]*mat[8]) +
+ mat[2]*(mat[4]*mat[ 9] - mat[5]*mat[8]);
+ }
+
+ /**
+ * Calculates and returns the determinant of this transform.
+ * @return the double precision determinant
+ */
+ public final double determinant() {
+
+ if (isAffine()) {
+ return mat[0]*(mat[5]*mat[10] - mat[6]*mat[9]) -
+ mat[1]*(mat[4]*mat[10] - mat[6]*mat[8]) +
+ mat[2]*(mat[4]*mat[ 9] - mat[5]*mat[8]);
+ }
+ // cofactor exapainsion along first row
+ return mat[0]*(mat[5]*(mat[10]*mat[15] - mat[11]*mat[14]) -
+ mat[6]*(mat[ 9]*mat[15] - mat[11]*mat[13]) +
+ mat[7]*(mat[ 9]*mat[14] - mat[10]*mat[13])) -
+ mat[1]*(mat[4]*(mat[10]*mat[15] - mat[11]*mat[14]) -
+ mat[6]*(mat[ 8]*mat[15] - mat[11]*mat[12]) +
+ mat[7]*(mat[ 8]*mat[14] - mat[10]*mat[12])) +
+ mat[2]*(mat[4]*(mat[ 9]*mat[15] - mat[11]*mat[13]) -
+ mat[5]*(mat[ 8]*mat[15] - mat[11]*mat[12]) +
+ mat[7]*(mat[ 8]*mat[13] - mat[ 9]*mat[12])) -
+ mat[3]*(mat[4]*(mat[ 9]*mat[14] - mat[10]*mat[13]) -
+ mat[5]*(mat[ 8]*mat[14] - mat[10]*mat[12]) +
+ mat[6]*(mat[ 8]*mat[13] - mat[ 9]*mat[12]));
+ }
+
+ /**
+ * Sets the value of this transform to a uniform scale; all of
+ * the matrix values are modified.
+ * @param scale the scale factor for the transform
+ */
+ public final void set(double scale) {
+ setScaleTranslation(0, 0, 0, scale);
+ }
+
+
+ /**
+ * Sets the value of this transform to a scale and translation
+ * matrix; the scale is not applied to the translation and all
+ * of the matrix values are modified.
+ * @param scale the scale factor for the transform
+ * @param v1 the translation amount
+ */
+ public final void set(double scale, Vector3d v1) {
+ setScaleTranslation(v1.x, v1.y, v1.z, scale);
+ }
+
+
+ /**
+ * Sets the value of this transform to a scale and translation
+ * matrix; the scale is not applied to the translation and all
+ * of the matrix values are modified.
+ * @param scale the scale factor for the transform
+ * @param v1 the translation amount
+ */
+ public final void set(float scale, Vector3f v1) {
+ setScaleTranslation(v1.x, v1.y, v1.z, scale);
+ }
+
+ /**
+ * Sets the value of this transform to a scale and translation matrix;
+ * the translation is scaled by the scale factor and all of the
+ * matrix values are modified.
+ * @param v1 the translation amount
+ * @param scale the scale factor for the transform AND the translation
+ */
+ public final void set(Vector3d v1, double scale) {
+ setScaleTranslation(v1.x*scale, v1.y*scale, v1.z*scale, scale);
+ }
+
+ /**
+ * Sets the value of this transform to a scale and translation matrix;
+ * the translation is scaled by the scale factor and all of the
+ * matrix values are modified.
+ * @param v1 the translation amount
+ * @param scale the scale factor for the transform AND the translation
+ */
+ public final void set(Vector3f v1, float scale) {
+ setScaleTranslation(v1.x*scale, v1.y*scale, v1.z*scale, scale);
+ }
+
+ private final void setScaleTranslation(double x, double y,
+ double z, double scale) {
+ mat[0] = scale;
+ mat[1] = 0.0;
+ mat[2] = 0.0;
+ mat[3] = x;
+ mat[4] = 0.0;
+ mat[5] = scale;
+ mat[6] = 0.0;
+ mat[7] = y;
+ mat[8] = 0.0;
+ mat[9] = 0.0;
+ mat[10] = scale;
+ mat[11] = z;
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ if(scales == null)
+ scales = new double[3];
+
+ scales[0] = scales[1] = scales[2] = scale;
+
+ type = AFFINE | CONGRUENT | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTATION_BIT | RIGID_BIT;
+ }
+
+
+
+ /**
+ * Multiplies each element of this transform by a scalar.
+ * @param scalar the scalar multiplier
+ */
+ public final void mul(double scalar) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] *= scalar;
+ }
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+ }
+
+ /**
+ * Multiplies each element of transform t1 by a scalar and places
+ * the result into this. Transform t1 is not modified.
+ * @param scalar the scalar multiplier
+ * @param t1 the original transform
+ */
+ public final void mul(double scalar, Transform3D t1) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] = t1.mat[i] * scalar;
+ }
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+
+ /**
+ * Sets the value of this transform to the result of multiplying itself
+ * with transform t1 (this = this * t1).
+ * @param t1 the other transform
+ */
+ public final void mul(Transform3D t1) {
+ double tmp0, tmp1, tmp2, tmp3;
+ double tmp4, tmp5, tmp6, tmp7;
+ double tmp8, tmp9, tmp10, tmp11;
+ boolean aff = false;
+
+ if (t1.isAffine()) {
+ tmp0 = mat[0]*t1.mat[0] + mat[1]*t1.mat[4] + mat[2]*t1.mat[8];
+ tmp1 = mat[0]*t1.mat[1] + mat[1]*t1.mat[5] + mat[2]*t1.mat[9];
+ tmp2 = mat[0]*t1.mat[2] + mat[1]*t1.mat[6] + mat[2]*t1.mat[10];
+ tmp3 = mat[0]*t1.mat[3] + mat[1]*t1.mat[7] + mat[2]*t1.mat[11] + mat[3];
+ tmp4 = mat[4]*t1.mat[0] + mat[5]*t1.mat[4] + mat[6]*t1.mat[8];
+ tmp5 = mat[4]*t1.mat[1] + mat[5]*t1.mat[5] + mat[6]*t1.mat[9];
+ tmp6 = mat[4]*t1.mat[2] + mat[5]*t1.mat[6] + mat[6]*t1.mat[10];
+ tmp7 = mat[4]*t1.mat[3] + mat[5]*t1.mat[7] + mat[6]*t1.mat[11] + mat[7];
+ tmp8 = mat[8]*t1.mat[0] + mat[9]*t1.mat[4] + mat[10]*t1.mat[8];
+ tmp9 = mat[8]*t1.mat[1] + mat[9]*t1.mat[5] + mat[10]*t1.mat[9];
+ tmp10 = mat[8]*t1.mat[2] + mat[9]*t1.mat[6] + mat[10]*t1.mat[10];
+ tmp11 = mat[8]*t1.mat[3] + mat[9]*t1.mat[7] + mat[10]*t1.mat[11] + mat[11];
+ if (isAffine()) {
+ mat[12] = mat[13] = mat[14] = 0;
+ mat[15] = 1;
+ aff = true;
+ } else {
+ double tmp12 = mat[12]*t1.mat[0] + mat[13]*t1.mat[4] +
+ mat[14]*t1.mat[8];
+ double tmp13 = mat[12]*t1.mat[1] + mat[13]*t1.mat[5] +
+ mat[14]*t1.mat[9];
+ double tmp14 = mat[12]*t1.mat[2] + mat[13]*t1.mat[6] +
+ mat[14]*t1.mat[10];
+ double tmp15 = mat[12]*t1.mat[3] + mat[13]*t1.mat[7] +
+ mat[14]*t1.mat[11] + mat[15];
+ mat[12] = tmp12;
+ mat[13] = tmp13;
+ mat[14] = tmp14;
+ mat[15] = tmp15;
+ }
+ } else {
+ tmp0 = mat[0]*t1.mat[0] + mat[1]*t1.mat[4] + mat[2]*t1.mat[8] +
+ mat[3]*t1.mat[12];
+ tmp1 = mat[0]*t1.mat[1] + mat[1]*t1.mat[5] + mat[2]*t1.mat[9] +
+ mat[3]*t1.mat[13];
+ tmp2 = mat[0]*t1.mat[2] + mat[1]*t1.mat[6] + mat[2]*t1.mat[10] +
+ mat[3]*t1.mat[14];
+ tmp3 = mat[0]*t1.mat[3] + mat[1]*t1.mat[7] + mat[2]*t1.mat[11] +
+ mat[3]*t1.mat[15];
+ tmp4 = mat[4]*t1.mat[0] + mat[5]*t1.mat[4] + mat[6]*t1.mat[8] +
+ mat[7]*t1.mat[12];
+ tmp5 = mat[4]*t1.mat[1] + mat[5]*t1.mat[5] + mat[6]*t1.mat[9] +
+ mat[7]*t1.mat[13];
+ tmp6 = mat[4]*t1.mat[2] + mat[5]*t1.mat[6] + mat[6]*t1.mat[10] +
+ mat[7]*t1.mat[14];
+ tmp7 = mat[4]*t1.mat[3] + mat[5]*t1.mat[7] + mat[6]*t1.mat[11] +
+ mat[7]*t1.mat[15];
+ tmp8 = mat[8]*t1.mat[0] + mat[9]*t1.mat[4] + mat[10]*t1.mat[8] +
+ mat[11]*t1.mat[12];
+ tmp9 = mat[8]*t1.mat[1] + mat[9]*t1.mat[5] + mat[10]*t1.mat[9] +
+ mat[11]*t1.mat[13];
+ tmp10 = mat[8]*t1.mat[2] + mat[9]*t1.mat[6] +
+ mat[10]*t1.mat[10]+ mat[11]*t1.mat[14];
+ tmp11 = mat[8]*t1.mat[3] + mat[9]*t1.mat[7] +
+ mat[10]*t1.mat[11] + mat[11]*t1.mat[15];
+
+ if (isAffine()) {
+ mat[12] = t1.mat[12];
+ mat[13] = t1.mat[13];
+ mat[14] = t1.mat[14];
+ mat[15] = t1.mat[15];
+ } else {
+ double tmp12 = mat[12]*t1.mat[0] + mat[13]*t1.mat[4] +
+ mat[14]*t1.mat[8] + mat[15]*t1.mat[12];
+ double tmp13 = mat[12]*t1.mat[1] + mat[13]*t1.mat[5] +
+ mat[14]*t1.mat[9] + mat[15]*t1.mat[13];
+ double tmp14 = mat[12]*t1.mat[2] + mat[13]*t1.mat[6] +
+ mat[14]*t1.mat[10] + mat[15]*t1.mat[14];
+ double tmp15 = mat[12]*t1.mat[3] + mat[13]*t1.mat[7] +
+ mat[14]*t1.mat[11] + mat[15]*t1.mat[15];
+ mat[12] = tmp12;
+ mat[13] = tmp13;
+ mat[14] = tmp14;
+ mat[15] = tmp15;
+ }
+ }
+
+ mat[0] = tmp0;
+ mat[1] = tmp1;
+ mat[2] = tmp2;
+ mat[3] = tmp3;
+ mat[4] = tmp4;
+ mat[5] = tmp5;
+ mat[6] = tmp6;
+ mat[7] = tmp7;
+ mat[8] = tmp8;
+ mat[9] = tmp9;
+ mat[10] = tmp10;
+ mat[11] = tmp11;
+
+ if (((dirtyBits & CONGRUENT_BIT) == 0) &&
+ ((type & CONGRUENT) != 0) &&
+ ((t1.dirtyBits & CONGRUENT_BIT) == 0) &&
+ ((t1.type & CONGRUENT) != 0)) {
+ type &= t1.type;
+ dirtyBits |= t1.dirtyBits | CLASSIFY_BIT |
+ ROTSCALESVD_DIRTY | RIGID_BIT;
+ } else {
+ if (aff) {
+ dirtyBits = ORTHO_BIT | CONGRUENT_BIT | RIGID_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+ } else {
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+ }
+ }
+
+ if (autoNormalize) {
+ normalize();
+ }
+
+ }
+
+ /**
+ * Sets the value of this transform to the result of multiplying transform
+ * t1 by transform t2 (this = t1*t2).
+ * @param t1 the left transform
+ * @param t2 the right transform
+ */
+ public final void mul(Transform3D t1, Transform3D t2) {
+ boolean aff = false;
+ if ((this != t1) && (this != t2)) {
+ if (t2.isAffine()) {
+
+ mat[0] = t1.mat[0]*t2.mat[0] + t1.mat[1]*t2.mat[4] + t1.mat[2]*t2.mat[8];
+ mat[1] = t1.mat[0]*t2.mat[1] + t1.mat[1]*t2.mat[5] + t1.mat[2]*t2.mat[9];
+ mat[2] = t1.mat[0]*t2.mat[2] + t1.mat[1]*t2.mat[6] + t1.mat[2]*t2.mat[10];
+ mat[3] = t1.mat[0]*t2.mat[3] + t1.mat[1]*t2.mat[7] +
+ t1.mat[2]*t2.mat[11] + t1.mat[3];
+ mat[4] = t1.mat[4]*t2.mat[0] + t1.mat[5]*t2.mat[4] + t1.mat[6]*t2.mat[8];
+ mat[5] = t1.mat[4]*t2.mat[1] + t1.mat[5]*t2.mat[5] + t1.mat[6]*t2.mat[9];
+ mat[6] = t1.mat[4]*t2.mat[2] + t1.mat[5]*t2.mat[6] + t1.mat[6]*t2.mat[10];
+ mat[7] = t1.mat[4]*t2.mat[3] + t1.mat[5]*t2.mat[7] +
+ t1.mat[6]*t2.mat[11] + t1.mat[7];
+ mat[8] = t1.mat[8]*t2.mat[0] + t1.mat[9]*t2.mat[4] + t1.mat[10]*t2.mat[8];
+ mat[9] = t1.mat[8]*t2.mat[1] + t1.mat[9]*t2.mat[5] + t1.mat[10]*t2.mat[9];
+ mat[10] = t1.mat[8]*t2.mat[2] + t1.mat[9]*t2.mat[6] + t1.mat[10]*t2.mat[10];
+ mat[11] = t1.mat[8]*t2.mat[3] + t1.mat[9]*t2.mat[7] +
+ t1.mat[10]*t2.mat[11] + t1.mat[11];
+ if (t1.isAffine()) {
+ aff = true;
+ mat[12] = mat[13] = mat[14] = 0;
+ mat[15] = 1;
+ } else {
+ mat[12] = t1.mat[12]*t2.mat[0] + t1.mat[13]*t2.mat[4] +
+ t1.mat[14]*t2.mat[8];
+ mat[13] = t1.mat[12]*t2.mat[1] + t1.mat[13]*t2.mat[5] +
+ t1.mat[14]*t2.mat[9];
+ mat[14] = t1.mat[12]*t2.mat[2] + t1.mat[13]*t2.mat[6] +
+ t1.mat[14]*t2.mat[10];
+ mat[15] = t1.mat[12]*t2.mat[3] + t1.mat[13]*t2.mat[7] +
+ t1.mat[14]*t2.mat[11] + t1.mat[15];
+ }
+ } else {
+ mat[0] = t1.mat[0]*t2.mat[0] + t1.mat[1]*t2.mat[4] +
+ t1.mat[2]*t2.mat[8] + t1.mat[3]*t2.mat[12];
+ mat[1] = t1.mat[0]*t2.mat[1] + t1.mat[1]*t2.mat[5] +
+ t1.mat[2]*t2.mat[9] + t1.mat[3]*t2.mat[13];
+ mat[2] = t1.mat[0]*t2.mat[2] + t1.mat[1]*t2.mat[6] +
+ t1.mat[2]*t2.mat[10] + t1.mat[3]*t2.mat[14];
+ mat[3] = t1.mat[0]*t2.mat[3] + t1.mat[1]*t2.mat[7] +
+ t1.mat[2]*t2.mat[11] + t1.mat[3]*t2.mat[15];
+ mat[4] = t1.mat[4]*t2.mat[0] + t1.mat[5]*t2.mat[4] +
+ t1.mat[6]*t2.mat[8] + t1.mat[7]*t2.mat[12];
+ mat[5] = t1.mat[4]*t2.mat[1] + t1.mat[5]*t2.mat[5] +
+ t1.mat[6]*t2.mat[9] + t1.mat[7]*t2.mat[13];
+ mat[6] = t1.mat[4]*t2.mat[2] + t1.mat[5]*t2.mat[6] +
+ t1.mat[6]*t2.mat[10] + t1.mat[7]*t2.mat[14];
+ mat[7] = t1.mat[4]*t2.mat[3] + t1.mat[5]*t2.mat[7] +
+ t1.mat[6]*t2.mat[11] + t1.mat[7]*t2.mat[15];
+ mat[8] = t1.mat[8]*t2.mat[0] + t1.mat[9]*t2.mat[4] +
+ t1.mat[10]*t2.mat[8] + t1.mat[11]*t2.mat[12];
+ mat[9] = t1.mat[8]*t2.mat[1] + t1.mat[9]*t2.mat[5] +
+ t1.mat[10]*t2.mat[9] + t1.mat[11]*t2.mat[13];
+ mat[10] = t1.mat[8]*t2.mat[2] + t1.mat[9]*t2.mat[6] +
+ t1.mat[10]*t2.mat[10] + t1.mat[11]*t2.mat[14];
+ mat[11] = t1.mat[8]*t2.mat[3] + t1.mat[9]*t2.mat[7] +
+ t1.mat[10]*t2.mat[11] + t1.mat[11]*t2.mat[15];
+ if (t1.isAffine()) {
+ mat[12] = t2.mat[12];
+ mat[13] = t2.mat[13];
+ mat[14] = t2.mat[14];
+ mat[15] = t2.mat[15];
+ } else {
+ mat[12] = t1.mat[12]*t2.mat[0] + t1.mat[13]*t2.mat[4] +
+ t1.mat[14]*t2.mat[8] + t1.mat[15]*t2.mat[12];
+ mat[13] = t1.mat[12]*t2.mat[1] + t1.mat[13]*t2.mat[5] +
+ t1.mat[14]*t2.mat[9] + t1.mat[15]*t2.mat[13];
+ mat[14] = t1.mat[12]*t2.mat[2] + t1.mat[13]*t2.mat[6] +
+ t1.mat[14]*t2.mat[10] + t1.mat[15]*t2.mat[14];
+ mat[15] = t1.mat[12]*t2.mat[3] + t1.mat[13]*t2.mat[7] +
+ t1.mat[14]*t2.mat[11] + t1.mat[15]*t2.mat[15];
+ }
+ }
+ } else {
+ double tmp0, tmp1, tmp2, tmp3;
+ double tmp4, tmp5, tmp6, tmp7;
+ double tmp8, tmp9, tmp10, tmp11;
+
+ if (t2.isAffine()) {
+ tmp0 = t1.mat[0]*t2.mat[0] + t1.mat[1]*t2.mat[4] + t1.mat[2]*t2.mat[8];
+ tmp1 = t1.mat[0]*t2.mat[1] + t1.mat[1]*t2.mat[5] + t1.mat[2]*t2.mat[9];
+ tmp2 = t1.mat[0]*t2.mat[2] + t1.mat[1]*t2.mat[6] + t1.mat[2]*t2.mat[10];
+ tmp3 = t1.mat[0]*t2.mat[3] + t1.mat[1]*t2.mat[7] +
+ t1.mat[2]*t2.mat[11] + t1.mat[3];
+ tmp4 = t1.mat[4]*t2.mat[0] + t1.mat[5]*t2.mat[4] + t1.mat[6]*t2.mat[8];
+ tmp5 = t1.mat[4]*t2.mat[1] + t1.mat[5]*t2.mat[5] + t1.mat[6]*t2.mat[9];
+ tmp6 = t1.mat[4]*t2.mat[2] + t1.mat[5]*t2.mat[6] + t1.mat[6]*t2.mat[10];
+ tmp7 = t1.mat[4]*t2.mat[3] + t1.mat[5]*t2.mat[7] +
+ t1.mat[6]*t2.mat[11] + t1.mat[7];
+ tmp8 = t1.mat[8]*t2.mat[0] + t1.mat[9]*t2.mat[4] + t1.mat[10]*t2.mat[8];
+ tmp9 = t1.mat[8]*t2.mat[1] + t1.mat[9]*t2.mat[5] + t1.mat[10]*t2.mat[9];
+ tmp10 = t1.mat[8]*t2.mat[2] + t1.mat[9]*t2.mat[6] + t1.mat[10]*t2.mat[10];
+ tmp11 = t1.mat[8]*t2.mat[3] + t1.mat[9]*t2.mat[7] +
+ t1.mat[10]*t2.mat[11] + t1.mat[11];
+ if (t1.isAffine()) {
+ aff = true;
+ mat[12] = mat[13] = mat[14] = 0;
+ mat[15] = 1;
+ } else {
+ double tmp12 = t1.mat[12]*t2.mat[0] + t1.mat[13]*t2.mat[4] +
+ t1.mat[14]*t2.mat[8];
+ double tmp13 = t1.mat[12]*t2.mat[1] + t1.mat[13]*t2.mat[5] +
+ t1.mat[14]*t2.mat[9];
+ double tmp14 = t1.mat[12]*t2.mat[2] + t1.mat[13]*t2.mat[6] +
+ t1.mat[14]*t2.mat[10];
+ double tmp15 = t1.mat[12]*t2.mat[3] + t1.mat[13]*t2.mat[7] +
+ t1.mat[14]*t2.mat[11] + t1.mat[15];
+ mat[12] = tmp12;
+ mat[13] = tmp13;
+ mat[14] = tmp14;
+ mat[15] = tmp15;
+ }
+ } else {
+ tmp0 = t1.mat[0]*t2.mat[0] + t1.mat[1]*t2.mat[4] +
+ t1.mat[2]*t2.mat[8] + t1.mat[3]*t2.mat[12];
+ tmp1 = t1.mat[0]*t2.mat[1] + t1.mat[1]*t2.mat[5] +
+ t1.mat[2]*t2.mat[9] + t1.mat[3]*t2.mat[13];
+ tmp2 = t1.mat[0]*t2.mat[2] + t1.mat[1]*t2.mat[6] +
+ t1.mat[2]*t2.mat[10] + t1.mat[3]*t2.mat[14];
+ tmp3 = t1.mat[0]*t2.mat[3] + t1.mat[1]*t2.mat[7] +
+ t1.mat[2]*t2.mat[11] + t1.mat[3]*t2.mat[15];
+ tmp4 = t1.mat[4]*t2.mat[0] + t1.mat[5]*t2.mat[4] +
+ t1.mat[6]*t2.mat[8] + t1.mat[7]*t2.mat[12];
+ tmp5 = t1.mat[4]*t2.mat[1] + t1.mat[5]*t2.mat[5] +
+ t1.mat[6]*t2.mat[9] + t1.mat[7]*t2.mat[13];
+ tmp6 = t1.mat[4]*t2.mat[2] + t1.mat[5]*t2.mat[6] +
+ t1.mat[6]*t2.mat[10] + t1.mat[7]*t2.mat[14];
+ tmp7 = t1.mat[4]*t2.mat[3] + t1.mat[5]*t2.mat[7] +
+ t1.mat[6]*t2.mat[11] + t1.mat[7]*t2.mat[15];
+ tmp8 = t1.mat[8]*t2.mat[0] + t1.mat[9]*t2.mat[4] +
+ t1.mat[10]*t2.mat[8] + t1.mat[11]*t2.mat[12];
+ tmp9 = t1.mat[8]*t2.mat[1] + t1.mat[9]*t2.mat[5] +
+ t1.mat[10]*t2.mat[9] + t1.mat[11]*t2.mat[13];
+ tmp10 = t1.mat[8]*t2.mat[2] + t1.mat[9]*t2.mat[6] +
+ t1.mat[10]*t2.mat[10] + t1.mat[11]*t2.mat[14];
+ tmp11 = t1.mat[8]*t2.mat[3] + t1.mat[9]*t2.mat[7] +
+ t1.mat[10]*t2.mat[11] + t1.mat[11]*t2.mat[15];
+
+ if (t1.isAffine()) {
+ mat[12] = t2.mat[12];
+ mat[13] = t2.mat[13];
+ mat[14] = t2.mat[14];
+ mat[15] = t2.mat[15];
+ } else {
+ double tmp12 = t1.mat[12]*t2.mat[0] + t1.mat[13]*t2.mat[4] +
+ t1.mat[14]*t2.mat[8] + t1.mat[15]*t2.mat[12];
+ double tmp13 = t1.mat[12]*t2.mat[1] + t1.mat[13]*t2.mat[5] +
+ t1.mat[14]*t2.mat[9] + t1.mat[15]*t2.mat[13];
+ double tmp14 = t1.mat[12]*t2.mat[2] + t1.mat[13]*t2.mat[6] +
+ t1.mat[14]*t2.mat[10] + t1.mat[15]*t2.mat[14];
+ double tmp15 = t1.mat[12]*t2.mat[3] + t1.mat[13]*t2.mat[7] +
+ t1.mat[14]*t2.mat[11] + t1.mat[15]*t2.mat[15];
+ mat[12] = tmp12;
+ mat[13] = tmp13;
+ mat[14] = tmp14;
+ mat[15] = tmp15;
+ }
+ }
+ mat[0] = tmp0;
+ mat[1] = tmp1;
+ mat[2] = tmp2;
+ mat[3] = tmp3;
+ mat[4] = tmp4;
+ mat[5] = tmp5;
+ mat[6] = tmp6;
+ mat[7] = tmp7;
+ mat[8] = tmp8;
+ mat[9] = tmp9;
+ mat[10] = tmp10;
+ mat[11] = tmp11;
+ }
+
+
+ if (((t1.dirtyBits & CONGRUENT_BIT) == 0) &&
+ ((t1.type & CONGRUENT) != 0) &&
+ ((t2.dirtyBits & CONGRUENT_BIT) == 0) &&
+ ((t2.type & CONGRUENT) != 0)) {
+ type = t1.type & t2.type;
+ dirtyBits = t1.dirtyBits | t2.dirtyBits | CLASSIFY_BIT |
+ ROTSCALESVD_DIRTY | RIGID_BIT;
+ } else {
+ if (aff) {
+ dirtyBits = ORTHO_BIT | CONGRUENT_BIT | RIGID_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+ } else {
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+ }
+ }
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+ /**
+ * Multiplies this transform by the inverse of transform t1. The final
+ * value is placed into this matrix (this = this*t1^-1).
+ * @param t1 the matrix whose inverse is computed.
+ */
+ public final void mulInverse(Transform3D t1) {
+ Transform3D t2 = VirtualUniverse.mc.getTransform3D(null);
+ t2.autoNormalize = false;
+ t2.invert(t1);
+ this.mul(t2);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t2);
+ }
+
+
+ /**
+ * Multiplies transform t1 by the inverse of transform t2. The final
+ * value is placed into this matrix (this = t1*t2^-1).
+ * @param t1 the left transform in the multiplication
+ * @param t2 the transform whose inverse is computed.
+ */
+ public final void mulInverse(Transform3D t1, Transform3D t2) {
+ Transform3D t3 = VirtualUniverse.mc.getTransform3D(null);
+ t3.autoNormalize = false;
+ t3.invert(t2);
+ this.mul(t1,t3);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3);
+ }
+
+ /**
+ * Multiplies transform t1 by the transpose of transform t2 and places
+ * the result into this transform (this = t1 * transpose(t2)).
+ * @param t1 the transform on the left hand side of the multiplication
+ * @param t2 the transform whose transpose is computed
+ */
+ public final void mulTransposeRight(Transform3D t1, Transform3D t2) {
+ Transform3D t3 = VirtualUniverse.mc.getTransform3D(null);
+ t3.autoNormalize = false;
+ t3.transpose(t2);
+ mul(t1, t3);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3);
+ }
+
+
+ /**
+ * Multiplies the transpose of transform t1 by transform t2 and places
+ * the result into this matrix (this = transpose(t1) * t2).
+ * @param t1 the transform whose transpose is computed
+ * @param t2 the transform on the right hand side of the multiplication
+ */
+ public final void mulTransposeLeft(Transform3D t1, Transform3D t2){
+ Transform3D t3 = VirtualUniverse.mc.getTransform3D(null);
+ t3.autoNormalize = false;
+ t3.transpose(t1);
+ mul(t3, t2);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3);
+ }
+
+
+ /**
+ * Multiplies the transpose of transform t1 by the transpose of
+ * transform t2 and places the result into this transform
+ * (this = transpose(t1) * transpose(t2)).
+ * @param t1 the transform on the left hand side of the multiplication
+ * @param t2 the transform on the right hand side of the multiplication
+ */
+ public final void mulTransposeBoth(Transform3D t1, Transform3D t2) {
+ Transform3D t3 = VirtualUniverse.mc.getTransform3D(null);
+ Transform3D t4 = VirtualUniverse.mc.getTransform3D(null);
+ t3.autoNormalize = false;
+ t4.autoNormalize = false;
+ t3.transpose(t1);
+ t4.transpose(t2);
+ mul(t3, t4);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t4);
+ }
+
+
+ /**
+ * Normalizes the rotational components (upper 3x3) of this matrix
+ * in place using a Singular Value Decomposition (SVD).
+ * This operation ensures that the column vectors of this matrix
+ * are orthogonal to each other. The primary use of this method
+ * is to correct for floating point errors that accumulate over
+ * time when concatenating a large number of rotation matrices.
+ * Note that the scale of the matrix is not altered by this method.
+ */
+ public final void normalize() {
+
+ if ((dirtyBits & (ROTATION_BIT|SVD_BIT)) != 0) {
+ computeScaleRotation(true);
+ } else if ((dirtyBits & (SCALE_BIT|SVD_BIT)) != 0) {
+ computeScales(true);
+ }
+
+ mat[0] = rot[0]*scales[0];
+ mat[1] = rot[1]*scales[1];
+ mat[2] = rot[2]*scales[2];
+ mat[4] = rot[3]*scales[0];
+ mat[5] = rot[4]*scales[1];
+ mat[6] = rot[5]*scales[2];
+ mat[8] = rot[6]*scales[0];
+ mat[9] = rot[7]*scales[1];
+ mat[10] = rot[8]*scales[2];
+ dirtyBits |= CLASSIFY_BIT;
+ dirtyBits &= ~ORTHO_BIT;
+ type |= ORTHO;
+ }
+
+ /**
+ * Normalizes the rotational components (upper 3x3) of transform t1
+ * using a Singular Value Decomposition (SVD), and places the result
+ * into this transform.
+ * This operation ensures that the column vectors of this matrix
+ * are orthogonal to each other. The primary use of this method
+ * is to correct for floating point errors that accumulate over
+ * time when concatenating a large number of rotation matrices.
+ * Note that the scale of the matrix is not altered by this method.
+ *
+ * @param t1 the source transform, which is not modified
+ */
+ public final void normalize(Transform3D t1){
+ set(t1);
+ normalize();
+ }
+
+ /**
+ * Normalizes the rotational components (upper 3x3) of this transform
+ * in place using a Cross Product (CP) normalization.
+ * This operation ensures that the column vectors of this matrix
+ * are orthogonal to each other. The primary use of this method
+ * is to correct for floating point errors that accumulate over
+ * time when concatenating a large number of rotation matrices.
+ * Note that the scale of the matrix is not altered by this method.
+ */
+ public final void normalizeCP() {
+ if ((dirtyBits & SCALE_BIT) != 0) {
+ computeScales(false);
+ }
+
+ double mag = mat[0]*mat[0] + mat[4]*mat[4] +
+ mat[8]*mat[8];
+
+ if (mag != 0) {
+ mag = 1.0/Math.sqrt(mag);
+ mat[0] = mat[0]*mag;
+ mat[4] = mat[4]*mag;
+ mat[8] = mat[8]*mag;
+ }
+
+ mag = mat[1]*mat[1] + mat[5]*mat[5] +
+ mat[9]*mat[9];
+
+ if (mag != 0) {
+ mag = 1.0/Math.sqrt(mag);
+ mat[1] = mat[1]*mag;
+ mat[5] = mat[5]*mag;
+ mat[9] = mat[9]*mag;
+ }
+ mat[2] = (mat[4]*mat[9] - mat[5]*mat[8])*scales[0];
+ mat[6] = (mat[1]*mat[8] - mat[0]*mat[9])*scales[1];
+ mat[10] = (mat[0]*mat[5] - mat[1]*mat[4])*scales[2];
+
+ mat[0] *= scales[0];
+ mat[1] *= scales[0];
+ mat[4] *= scales[1];
+ mat[5] *= scales[1];
+ mat[8] *= scales[2];
+ mat[9] *= scales[2];
+
+ // leave the AFFINE bit
+ dirtyBits |= CONGRUENT_BIT | RIGID_BIT | CLASSIFY_BIT | ROTATION_BIT | SVD_BIT;
+ dirtyBits &= ~ORTHO_BIT;
+ type |= ORTHO;
+ }
+
+
+ /**
+ * Normalizes the rotational components (upper 3x3) of transform t1
+ * using a Cross Product (CP) normalization, and
+ * places the result into this transform.
+ * This operation ensures that the column vectors of this matrix
+ * are orthogonal to each other. The primary use of this method
+ * is to correct for floating point errors that accumulate over
+ * time when concatenating a large number of rotation matrices.
+ * Note that the scale of the matrix is not altered by this method.
+ *
+ * @param t1 the transform to be normalized
+ */
+ public final void normalizeCP(Transform3D t1) {
+ set(t1);
+ normalizeCP();
+ }
+
+
+ /**
+ * Returns true if all of the data members of transform t1 are
+ * equal to the corresponding data members in this Transform3D.
+ * @param t1 the transform with which the comparison is made
+ * @return true or false
+ */
+ public boolean equals(Transform3D t1) {
+ return (t1 != null) &&
+ (mat[0] == t1.mat[0]) && (mat[1] == t1.mat[1]) &&
+ (mat[2] == t1.mat[2]) && (mat[3] == t1.mat[3]) &&
+ (mat[4] == t1.mat[4]) && (mat[5] == t1.mat[5]) &&
+ (mat[6] == t1.mat[6]) && (mat[7] == t1.mat[7]) &&
+ (mat[8] == t1.mat[8]) && (mat[9] == t1.mat[9]) &&
+ (mat[10] == t1.mat[10]) && (mat[11] == t1.mat[11]) &&
+ (mat[12] == t1.mat[12]) && (mat[13] == t1.mat[13]) &&
+ (mat[14] == t1.mat[14]) && ( mat[15] == t1.mat[15]);
+ }
+
+
+ /**
+ * Returns true if the Object o1 is of type Transform3D and all of the
+ * data members of o1 are equal to the corresponding data members in
+ * this Transform3D.
+ * @param o1 the object with which the comparison is made.
+ * @return true or false
+ */
+ public boolean equals(Object o1) {
+ return (o1 instanceof Transform3D) && equals((Transform3D) o1);
+ }
+
+
+ /**
+ * Returns true if the L-infinite distance between this matrix
+ * and matrix m1 is less than or equal to the epsilon parameter,
+ * otherwise returns false. The L-infinite
+ * distance is equal to
+ * MAX[i=0,1,2,3 ; j=0,1,2,3 ; abs[(this.m(i,j) - m1.m(i,j)]
+ * @param t1 the transform to be compared to this transform
+ * @param epsilon the threshold value
+ */
+ public boolean epsilonEquals(Transform3D t1, double epsilon) {
+ double diff;
+
+ for (int i=0 ; i<16 ; i++) {
+ diff = mat[i] - t1.mat[i];
+ if ((diff < 0 ? -diff : diff) > epsilon) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Returns a hash code value based on the data values in this
+ * object. Two different Transform3D objects with identical data
+ * values (i.e., Transform3D.equals returns true) will return the
+ * same hash number. Two Transform3D objects with different data
+ * members may return the same hash value, although this is not
+ * likely.
+ * @return the integer hash code value
+ */
+ public int hashCode() {
+ long bits = 1L;
+
+ for (int i = 0; i < 16; i++) {
+ bits = 31L * bits + Double.doubleToLongBits(mat[i]);
+ }
+ return (int) (bits ^ (bits >> 32));
+ }
+
+
+ /**
+ * Transform the vector vec using this transform and place the
+ * result into vecOut.
+ * @param vec the double precision vector to be transformed
+ * @param vecOut the vector into which the transformed values are placed
+ */
+ public final void transform(Vector4d vec, Vector4d vecOut) {
+
+ if (vec != vecOut) {
+ vecOut.x = (mat[0]*vec.x + mat[1]*vec.y
+ + mat[2]*vec.z + mat[3]*vec.w);
+ vecOut.y = (mat[4]*vec.x + mat[5]*vec.y
+ + mat[6]*vec.z + mat[7]*vec.w);
+ vecOut.z = (mat[8]*vec.x + mat[9]*vec.y
+ + mat[10]*vec.z + mat[11]*vec.w);
+ vecOut.w = (mat[12]*vec.x + mat[13]*vec.y
+ + mat[14]*vec.z + mat[15]*vec.w);
+ } else {
+ transform(vec);
+ }
+ }
+
+
+ /**
+ * Transform the vector vec using this Transform and place the
+ * result back into vec.
+ * @param vec the double precision vector to be transformed
+ */
+ public final void transform(Vector4d vec) {
+ double x = (mat[0]*vec.x + mat[1]*vec.y
+ + mat[2]*vec.z + mat[3]*vec.w);
+ double y = (mat[4]*vec.x + mat[5]*vec.y
+ + mat[6]*vec.z + mat[7]*vec.w);
+ double z = (mat[8]*vec.x + mat[9]*vec.y
+ + mat[10]*vec.z + mat[11]*vec.w);
+ vec.w = (mat[12]*vec.x + mat[13]*vec.y
+ + mat[14]*vec.z + mat[15]*vec.w);
+ vec.x = x;
+ vec.y = y;
+ vec.z = z;
+ }
+
+
+ /**
+ * Transform the vector vec using this Transform and place the
+ * result into vecOut.
+ * @param vec the single precision vector to be transformed
+ * @param vecOut the vector into which the transformed values are placed
+ */
+ public final void transform(Vector4f vec, Vector4f vecOut) {
+ if (vecOut != vec) {
+ vecOut.x = (float) (mat[0]*vec.x + mat[1]*vec.y
+ + mat[2]*vec.z + mat[3]*vec.w);
+ vecOut.y = (float) (mat[4]*vec.x + mat[5]*vec.y
+ + mat[6]*vec.z + mat[7]*vec.w);
+ vecOut.z = (float) (mat[8]*vec.x + mat[9]*vec.y
+ + mat[10]*vec.z + mat[11]*vec.w);
+ vecOut.w = (float) (mat[12]*vec.x + mat[13]*vec.y
+ + mat[14]*vec.z + mat[15]*vec.w);
+ } else {
+ transform(vec);
+ }
+ }
+
+
+ /**
+ * Transform the vector vec using this Transform and place the
+ * result back into vec.
+ * @param vec the single precision vector to be transformed
+ */
+ public final void transform(Vector4f vec) {
+ float x = (float) (mat[0]*vec.x + mat[1]*vec.y
+ + mat[2]*vec.z + mat[3]*vec.w);
+ float y = (float) (mat[4]*vec.x + mat[5]*vec.y
+ + mat[6]*vec.z + mat[7]*vec.w);
+ float z = (float) (mat[8]*vec.x + mat[9]*vec.y
+ + mat[10]*vec.z + mat[11]*vec.w);
+ vec.w = (float) (mat[12]*vec.x + mat[13]*vec.y
+ + mat[14]*vec.z + mat[15]*vec.w);
+ vec.x = x;
+ vec.y = y;
+ vec.z = z;
+ }
+
+
+ /**
+ * Transforms the point parameter with this transform and
+ * places the result into pointOut. The fourth element of the
+ * point input paramter is assumed to be one.
+ * @param point the input point to be transformed
+ * @param pointOut the transformed point
+ */
+ public final void transform(Point3d point, Point3d pointOut) {
+ if (point != pointOut) {
+ pointOut.x = mat[0]*point.x + mat[1]*point.y +
+ mat[2]*point.z + mat[3];
+ pointOut.y = mat[4]*point.x + mat[5]*point.y +
+ mat[6]*point.z + mat[7];
+ pointOut.z = mat[8]*point.x + mat[9]*point.y +
+ mat[10]*point.z + mat[11];
+ } else {
+ transform(point);
+ }
+ }
+
+
+ /**
+ * Transforms the point parameter with this transform and
+ * places the result back into point. The fourth element of the
+ * point input paramter is assumed to be one.
+ * @param point the input point to be transformed
+ */
+ public final void transform(Point3d point) {
+ double x = mat[0]*point.x + mat[1]*point.y + mat[2]*point.z + mat[3];
+ double y = mat[4]*point.x + mat[5]*point.y + mat[6]*point.z + mat[7];
+ point.z = mat[8]*point.x + mat[9]*point.y + mat[10]*point.z + mat[11];
+ point.x = x;
+ point.y = y;
+ }
+
+
+ /**
+ * Transforms the normal parameter by this transform and places the value
+ * into normalOut. The fourth element of the normal is assumed to be zero.
+ * @param normal the input normal to be transformed
+ * @param normalOut the transformed normal
+ */
+ public final void transform(Vector3d normal, Vector3d normalOut) {
+ if (normalOut != normal) {
+ normalOut.x = mat[0]*normal.x + mat[1]*normal.y + mat[2]*normal.z;
+ normalOut.y = mat[4]*normal.x + mat[5]*normal.y + mat[6]*normal.z;
+ normalOut.z = mat[8]*normal.x + mat[9]*normal.y + mat[10]*normal.z;
+ } else {
+ transform(normal);
+ }
+ }
+
+
+ /**
+ * Transforms the normal parameter by this transform and places the value
+ * back into normal. The fourth element of the normal is assumed to be zero.
+ * @param normal the input normal to be transformed
+ */
+ public final void transform(Vector3d normal) {
+ double x = mat[0]*normal.x + mat[1]*normal.y + mat[2]*normal.z;
+ double y = mat[4]*normal.x + mat[5]*normal.y + mat[6]*normal.z;
+ normal.z = mat[8]*normal.x + mat[9]*normal.y + mat[10]*normal.z;
+ normal.x = x;
+ normal.y = y;
+ }
+
+
+ /**
+ * Transforms the point parameter with this transform and
+ * places the result into pointOut. The fourth element of the
+ * point input paramter is assumed to be one.
+ * @param point the input point to be transformed
+ * @param pointOut the transformed point
+ */
+ public final void transform(Point3f point, Point3f pointOut) {
+ if (point != pointOut) {
+ pointOut.x = (float)(mat[0]*point.x + mat[1]*point.y +
+ mat[2]*point.z + mat[3]);
+ pointOut.y = (float)(mat[4]*point.x + mat[5]*point.y +
+ mat[6]*point.z + mat[7]);
+ pointOut.z = (float)(mat[8]*point.x + mat[9]*point.y +
+ mat[10]*point.z + mat[11]);
+ } else {
+ transform(point);
+ }
+ }
+
+
+ /**
+ * Transforms the point parameter with this transform and
+ * places the result back into point. The fourth element of the
+ * point input paramter is assumed to be one.
+ * @param point the input point to be transformed
+ */
+ public final void transform(Point3f point) {
+ float x = (float) (mat[0]*point.x + mat[1]*point.y +
+ mat[2]*point.z + mat[3]);
+ float y = (float) (mat[4]*point.x + mat[5]*point.y +
+ mat[6]*point.z + mat[7]);
+ point.z = (float) (mat[8]*point.x + mat[9]*point.y +
+ mat[10]*point.z + mat[11]);
+ point.x = x;
+ point.y = y;
+ }
+
+
+ /**
+ * Transforms the normal parameter by this transform and places the value
+ * into normalOut. The fourth element of the normal is assumed to be zero.
+ * Note: For correct lighting results, if a transform has uneven scaling
+ * surface normals should transformed by the inverse transpose of
+ * the transform. This the responsibility of the application and is not
+ * done automatically by this method.
+ * @param normal the input normal to be transformed
+ * @param normalOut the transformed normal
+ */
+ public final void transform(Vector3f normal, Vector3f normalOut) {
+ if (normal != normalOut) {
+ normalOut.x = (float) (mat[0]*normal.x + mat[1]*normal.y +
+ mat[2]*normal.z);
+ normalOut.y = (float) (mat[4]*normal.x + mat[5]*normal.y +
+ mat[6]*normal.z);
+ normalOut.z = (float) (mat[8]*normal.x + mat[9]*normal.y +
+ mat[10]*normal.z);
+ } else {
+ transform(normal);
+ }
+ }
+
+ /**
+ * Transforms the normal parameter by this transform and places the value
+ * back into normal. The fourth element of the normal is assumed to be zero.
+ * Note: For correct lighting results, if a transform has uneven scaling
+ * surface normals should transformed by the inverse transpose of
+ * the transform. This the responsibility of the application and is not
+ * done automatically by this method.
+ * @param normal the input normal to be transformed
+ */
+ public final void transform(Vector3f normal) {
+ float x = (float) (mat[0]*normal.x + mat[1]*normal.y +
+ mat[2]*normal.z);
+ float y = (float) (mat[4]*normal.x + mat[5]*normal.y +
+ mat[6]*normal.z);
+ normal.z = (float) (mat[8]*normal.x + mat[9]*normal.y +
+ mat[10]*normal.z);
+ normal.x = x;
+ normal.y = y;
+ }
+
+
+ /**
+ * Replaces the upper 3x3 matrix values of this transform with the
+ * values in the matrix m1.
+ * @param m1 the matrix that will be the new upper 3x3
+ */
+ public final void setRotationScale(Matrix3f m1) {
+ mat[0] = m1.m00; mat[1] = m1.m01; mat[2] = m1.m02;
+ mat[4] = m1.m10; mat[5] = m1.m11; mat[6] = m1.m12;
+ mat[8] = m1.m20; mat[9] = m1.m21; mat[10] = m1.m22;
+
+ // keep affine bit
+ dirtyBits |= (RIGID_BIT | CONGRUENT_BIT | ORTHO_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY);
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Replaces the upper 3x3 matrix values of this transform with the
+ * values in the matrix m1.
+ * @param m1 the matrix that will be the new upper 3x3
+ */
+ public final void setRotationScale(Matrix3d m1) {
+ mat[0] = m1.m00; mat[1] = m1.m01; mat[2] = m1.m02;
+ mat[4] = m1.m10; mat[5] = m1.m11; mat[6] = m1.m12;
+ mat[8] = m1.m20; mat[9] = m1.m21; mat[10] = m1.m22;
+
+ dirtyBits |= (RIGID_BIT | CONGRUENT_BIT | ORTHO_BIT |
+ CLASSIFY_BIT | ROTSCALESVD_DIRTY);
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+ /**
+ * Scales transform t1 by a Uniform scale matrix with scale
+ * factor s and then adds transform t2 (this = S*t1 + t2).
+ * @param s the scale factor
+ * @param t1 the transform to be scaled
+ * @param t2 the transform to be added
+ */
+ public final void scaleAdd(double s, Transform3D t1, Transform3D t2) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] = s*t1.mat[i] + t2.mat[i];
+ }
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Scales this transform by a Uniform scale matrix with scale factor
+ * s and then adds transform t1 (this = S*this + t1).
+ * @param s the scale factor
+ * @param t1 the transform to be added
+ */
+ public final void scaleAdd(double s, Transform3D t1) {
+ for (int i=0 ; i<16 ; i++) {
+ mat[i] = s*mat[i] + t1.mat[i];
+ }
+
+ dirtyBits = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY;
+
+ if (autoNormalize) {
+ normalize();
+ }
+ }
+
+
+ /**
+ * Gets the upper 3x3 values of this matrix and places them into
+ * the matrix m1.
+ * @param m1 the matrix that will hold the values
+ */
+ public final void getRotationScale(Matrix3f m1) {
+ m1.m00 = (float) mat[0];
+ m1.m01 = (float) mat[1];
+ m1.m02 = (float) mat[2];
+ m1.m10 = (float) mat[4];
+ m1.m11 = (float) mat[5];
+ m1.m12 = (float) mat[6];
+ m1.m20 = (float) mat[8];
+ m1.m21 = (float) mat[9];
+ m1.m22 = (float) mat[10];
+ }
+
+
+ /**
+ * Gets the upper 3x3 values of this matrix and places them into
+ * the matrix m1.
+ * @param m1 the matrix that will hold the values
+ */
+ public final void getRotationScale(Matrix3d m1) {
+ m1.m00 = mat[0];
+ m1.m01 = mat[1];
+ m1.m02 = mat[2];
+ m1.m10 = mat[4];
+ m1.m11 = mat[5];
+ m1.m12 = mat[6];
+ m1.m20 = mat[8];
+ m1.m21 = mat[9];
+ m1.m22 = mat[10];
+ }
+
+
+ /**
+ * Helping function that specifies the position and orientation of a
+ * view matrix. The inverse of this transform can be used to control
+ * the ViewPlatform object within the scene graph.
+ * @param eye the location of the eye
+ * @param center a point in the virtual world where the eye is looking
+ * @param up an up vector specifying the frustum's up direction
+ */
+ public void lookAt(Point3d eye, Point3d center, Vector3d up) {
+ double forwardx,forwardy,forwardz,invMag;
+ double upx,upy,upz;
+ double sidex,sidey,sidez;
+
+ forwardx = eye.x - center.x;
+ forwardy = eye.y - center.y;
+ forwardz = eye.z - center.z;
+
+ invMag = 1.0/Math.sqrt( forwardx*forwardx + forwardy*forwardy + forwardz*forwardz);
+ forwardx = forwardx*invMag;
+ forwardy = forwardy*invMag;
+ forwardz = forwardz*invMag;
+
+
+ invMag = 1.0/Math.sqrt( up.x*up.x + up.y*up.y + up.z*up.z);
+ upx = up.x*invMag;
+ upy = up.y*invMag;
+ upz = up.z*invMag;
+
+ // side = Up cross forward
+ sidex = upy*forwardz-forwardy*upz;
+ sidey = upz*forwardx-upx*forwardz;
+ sidez = upx*forwardy-upy*forwardx;
+
+ invMag = 1.0/Math.sqrt( sidex*sidex + sidey*sidey + sidez*sidez);
+ sidex *= invMag;
+ sidey *= invMag;
+ sidez *= invMag;
+
+ // recompute up = forward cross side
+
+ upx = forwardy*sidez-sidey*forwardz;
+ upy = forwardz*sidex-forwardx*sidez;
+ upz = forwardx*sidey-forwardy*sidex;
+
+ // transpose because we calculated the inverse of what we want
+ mat[0] = sidex;
+ mat[1] = sidey;
+ mat[2] = sidez;
+
+ mat[4] = upx;
+ mat[5] = upy;
+ mat[6] = upz;
+
+ mat[8] = forwardx;
+ mat[9] = forwardy;
+ mat[10] = forwardz;
+
+ mat[3] = -eye.x*mat[0] + -eye.y*mat[1] + -eye.z*mat[2];
+ mat[7] = -eye.x*mat[4] + -eye.y*mat[5] + -eye.z*mat[6];
+ mat[11] = -eye.x*mat[8] + -eye.y*mat[9] + -eye.z*mat[10];
+
+ mat[12] = mat[13] = mat[14] = 0;
+ mat[15] = 1;
+
+ type = AFFINE | CONGRUENT | RIGID | ORTHO;
+ dirtyBits = CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+ }
+
+
+ /**
+ * Creates a perspective projection transform that mimics a standard,
+ * camera-based,
+ * view-model. This transform maps coordinates from Eye Coordinates (EC)
+ * to Clipping Coordinates (CC). Note that unlike the similar function
+ * in OpenGL, the clipping coordinates generated by the resulting
+ * transform are in a right-handed coordinate system
+ * (as are all other coordinate systems in Java 3D).
+ * <p>
+ * The frustum function-call establishes a view model with the eye
+ * at the apex of a symmetric view frustum. The arguments
+ * define the frustum and its associated perspective projection:
+ * (left, bottom, -near) and (right, top, -near) specify the
+ * point on the near clipping plane that maps onto the
+ * lower-left and upper-right corners of the window respectively,
+ * assuming the eye is located at (0, 0, 0).
+ * @param left the vertical line on the left edge of the near
+ * clipping plane mapped to the left edge of the graphics window
+ * @param right the vertical line on the right edge of the near
+ * clipping plane mapped to the right edge of the graphics window
+ * @param bottom the horizontal line on the bottom edge of the near
+ * clipping plane mapped to the bottom edge of the graphics window
+ * @param top the horizontal line on the top edge of the near
+ * @param near the distance to the frustum's near clipping plane.
+ * This value must be positive, (the value -near is the location of the
+ * near clip plane).
+ * @param far the distance to the frustum's far clipping plane.
+ * This value must be positive, and must be greater than near.
+ */
+ public void frustum(double left, double right,
+ double bottom, double top,
+ double near, double far) {
+ double dx = 1/(right - left);
+ double dy = 1/(top - bottom);
+ double dz = 1/(far - near);
+
+ mat[0] = (2.0*near)*dx;
+ mat[5] = (2.0*near)*dy;
+ mat[10] = (far+near)*dz;
+ mat[2] = (right+left)*dx;
+ mat[6] = (top+bottom)*dy;
+ mat[11] = (2.0*far*near)*dz;
+ mat[14] = -1.0;
+ mat[1] = mat[3] = mat[4] = mat[7] = mat[8] = mat[9] = mat[12]
+ = mat[13] = mat[15] = 0;
+
+ // Matrix is a projection transform
+ type = 0;
+ dirtyBits = CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+ }
+
+
+ /**
+ * Creates a perspective projection transform that mimics a standard,
+ * camera-based,
+ * view-model. This transform maps coordinates from Eye Coordinates (EC)
+ * to Clipping Coordinates (CC). Note that unlike the similar function
+ * in OpenGL, the clipping coordinates generated by the resulting
+ * transform are in a right-handed coordinate system
+ * (as are all other coordinate systems in Java 3D). Also note that the
+ * field of view is specified in radians.
+ * @param fovx specifies the field of view in the x direction, in radians
+ * @param aspect specifies the aspect ratio and thus the field of
+ * view in the x direction. The aspect ratio is the ratio of x to y,
+ * or width to height.
+ * @param zNear the distance to the frustum's near clipping plane.
+ * This value must be positive, (the value -zNear is the location of the
+ * near clip plane).
+ * @param zFar the distance to the frustum's far clipping plane
+ */
+ public void perspective(double fovx, double aspect,
+ double zNear, double zFar) {
+ double sine, cotangent, deltaZ;
+ double half_fov = fovx * 0.5;
+ double x, y;
+ Vector3d v1, v2, v3, v4;
+ Vector3d norm = new Vector3d();
+
+ deltaZ = zFar - zNear;
+ sine = Math.sin(half_fov);
+// if ((deltaZ == 0.0) || (sine == 0.0) || (aspect == 0.0)) {
+// return;
+// }
+ cotangent = Math.cos(half_fov) / sine;
+
+ mat[0] = cotangent;
+ mat[5] = cotangent * aspect;
+ mat[10] = (zFar + zNear) / deltaZ;
+ mat[11] = 2.0 * zNear * zFar / deltaZ;
+ mat[14] = -1.0;
+ mat[1] = mat[2] = mat[3] = mat[4] = mat[6] = mat[7] = mat[8] =
+ mat[9] = mat[12] = mat[13] = mat[15] = 0;
+
+ // Matrix is a projection transform
+ type = 0;
+ dirtyBits = CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+ }
+
+
+ /**
+ * Creates an orthographic projection transform that mimics a standard,
+ * camera-based,
+ * view-model. This transform maps coordinates from Eye Coordinates (EC)
+ * to Clipping Coordinates (CC). Note that unlike the similar function
+ * in OpenGL, the clipping coordinates generated by the resulting
+ * transform are in a right-handed coordinate system
+ * (as are all other coordinate systems in Java 3D).
+ * @param left the vertical line on the left edge of the near
+ * clipping plane mapped to the left edge of the graphics window
+ * @param right the vertical line on the right edge of the near
+ * clipping plane mapped to the right edge of the graphics window
+ * @param bottom the horizontal line on the bottom edge of the near
+ * clipping plane mapped to the bottom edge of the graphics window
+ * @param top the horizontal line on the top edge of the near
+ * clipping plane mapped to the top edge of the graphics window
+ * @param near the distance to the frustum's near clipping plane
+ * (the value -near is the location of the near clip plane)
+ * @param far the distance to the frustum's far clipping plane
+ */
+ public void ortho(double left, double right, double bottom,
+ double top, double near, double far) {
+ double deltax = 1/(right - left);
+ double deltay = 1/(top - bottom);
+ double deltaz = 1/(far - near);
+
+// if ((deltax == 0.0) || (deltay == 0.0) || (deltaz == 0.0)) {
+// return;
+// }
+
+ mat[0] = 2.0 * deltax;
+ mat[3] = -(right + left) * deltax;
+ mat[5] = 2.0 * deltay;
+ mat[7] = -(top + bottom) * deltay;
+ mat[10] = 2.0 * deltaz;
+ mat[11] = (far + near) * deltaz;
+ mat[1] = mat[2] = mat[4] = mat[6] = mat[8] =
+ mat[9] = mat[12] = mat[13] = mat[14] = 0;
+ mat[15] = 1;
+ // Matrix is a projection transform
+ type = AFFINE;
+ dirtyBits = CLASSIFY_BIT | ROTSCALESVD_DIRTY | CONGRUENT_BIT |
+ RIGID_BIT | ORTHO_BIT;
+ }
+
+ /**
+ * get the scaling factor of matrix in this transform,
+ * use for distance scaling
+ */
+ double getDistanceScale() {
+ // The caller know that this matrix is affine
+ // orthogonal before invoke this procedure
+
+ if ((dirtyBits & SCALE_BIT) != 0) {
+ double max = mat[0]*mat[0] + mat[4]*mat[4] +
+ mat[8]*mat[8];
+ if (((dirtyBits & CONGRUENT_BIT) == 0) &&
+ ((type & CONGRUENT) != 0)) {
+ // in most case it is congruent
+ return Math.sqrt(max);
+ }
+ double tmp = mat[1]*mat[1] + mat[5]*mat[5] +
+ mat[9]*mat[9];
+ if (tmp > max) {
+ max = tmp;
+ }
+ tmp = mat[2]*mat[2] + mat[6]*mat[6] + mat[10]*mat[10];
+ return Math.sqrt((tmp > max) ? tmp : max);
+ }
+ return max3(scales);
+ }
+
+
+ static private void mat_mul(double[] m1, double[] m2, double[] m3) {
+
+ double[] result = m3;
+ if ((m1 == m3) || (m2 == m3)) {
+ result = new double[9];
+ }
+
+ result[0] = m1[0]*m2[0] + m1[1]*m2[3] + m1[2]*m2[6];
+ result[1] = m1[0]*m2[1] + m1[1]*m2[4] + m1[2]*m2[7];
+ result[2] = m1[0]*m2[2] + m1[1]*m2[5] + m1[2]*m2[8];
+
+ result[3] = m1[3]*m2[0] + m1[4]*m2[3] + m1[5]*m2[6];
+ result[4] = m1[3]*m2[1] + m1[4]*m2[4] + m1[5]*m2[7];
+ result[5] = m1[3]*m2[2] + m1[4]*m2[5] + m1[5]*m2[8];
+
+ result[6] = m1[6]*m2[0] + m1[7]*m2[3] + m1[8]*m2[6];
+ result[7] = m1[6]*m2[1] + m1[7]*m2[4] + m1[8]*m2[7];
+ result[8] = m1[6]*m2[2] + m1[7]*m2[5] + m1[8]*m2[8];
+
+ if (result != m3) {
+ for(int i=0;i<9;i++) {
+ m3[i] = result[i];
+ }
+ }
+ }
+
+ static private void transpose_mat(double[] in, double[] out) {
+ out[0] = in[0];
+ out[1] = in[3];
+ out[2] = in[6];
+
+ out[3] = in[1];
+ out[4] = in[4];
+ out[5] = in[7];
+
+ out[6] = in[2];
+ out[7] = in[5];
+ out[8] = in[8];
+ }
+
+
+ final static private void multipleScale(double m[] , double s[]) {
+ m[0] *= s[0];
+ m[1] *= s[0];
+ m[2] *= s[0];
+ m[4] *= s[1];
+ m[5] *= s[1];
+ m[6] *= s[1];
+ m[8] *= s[2];
+ m[9] *= s[2];
+ m[10] *= s[2];
+ }
+
+ private void compute_svd(Transform3D matrix, double[] outScale,
+ double[] outRot) {
+
+ int i,j;
+ double g,scale;
+ double m[] = new double[9];
+
+ // if (!svdAllocd) {
+ double[] u1 = new double[9];
+ double[] v1 = new double[9];
+ double[] t1 = new double[9];
+ double[] t2 = new double[9];
+ // double[] ts = new double[9];
+ // double[] svdTmp = new double[9]; It is replaced by t1
+ double[] svdRot = new double[9];
+ // double[] single_values = new double[3]; replaced by t2
+
+ double[] e = new double[3];
+ double[] svdScales = new double[3];
+
+
+ // TODO: initialize to 0's if alread allocd? Should not have to, since
+ // no operations depend on these being init'd to zero.
+
+ int converged, negCnt=0;
+ double cs,sn;
+ double c1,c2,c3,c4;
+ double s1,s2,s3,s4;
+ double cl1,cl2,cl3;
+
+
+ svdRot[0] = m[0] = matrix.mat[0];
+ svdRot[1] = m[1] = matrix.mat[1];
+ svdRot[2] = m[2] = matrix.mat[2];
+ svdRot[3] = m[3] = matrix.mat[4];
+ svdRot[4] = m[4] = matrix.mat[5];
+ svdRot[5] = m[5] = matrix.mat[6];
+ svdRot[6] = m[6] = matrix.mat[8];
+ svdRot[7] = m[7] = matrix.mat[9];
+ svdRot[8] = m[8] = matrix.mat[10];
+
+ // u1
+
+ if( m[3]*m[3] < EPS ) {
+ u1[0] = 1.0; u1[1] = 0.0; u1[2] = 0.0;
+ u1[3] = 0.0; u1[4] = 1.0; u1[5] = 0.0;
+ u1[6] = 0.0; u1[7] = 0.0; u1[8] = 1.0;
+ } else if( m[0]*m[0] < EPS ) {
+ t1[0] = m[0];
+ t1[1] = m[1];
+ t1[2] = m[2];
+ m[0] = m[3];
+ m[1] = m[4];
+ m[2] = m[5];
+
+ m[3] = -t1[0]; // zero
+ m[4] = -t1[1];
+ m[5] = -t1[2];
+
+ u1[0] = 0.0; u1[1] = 1.0; u1[2] = 0.0;
+ u1[3] = -1.0; u1[4] = 0.0; u1[5] = 0.0;
+ u1[6] = 0.0; u1[7] = 0.0; u1[8] = 1.0;
+ } else {
+ g = 1.0/Math.sqrt(m[0]*m[0] + m[3]*m[3]);
+ c1 = m[0]*g;
+ s1 = m[3]*g;
+ t1[0] = c1*m[0] + s1*m[3];
+ t1[1] = c1*m[1] + s1*m[4];
+ t1[2] = c1*m[2] + s1*m[5];
+
+ m[3] = -s1*m[0] + c1*m[3]; // zero
+ m[4] = -s1*m[1] + c1*m[4];
+ m[5] = -s1*m[2] + c1*m[5];
+
+ m[0] = t1[0];
+ m[1] = t1[1];
+ m[2] = t1[2];
+ u1[0] = c1; u1[1] = s1; u1[2] = 0.0;
+ u1[3] = -s1; u1[4] = c1; u1[5] = 0.0;
+ u1[6] = 0.0; u1[7] = 0.0; u1[8] = 1.0;
+ }
+
+ // u2
+
+ if( m[6]*m[6] < EPS ) {
+ } else if( m[0]*m[0] < EPS ){
+ t1[0] = m[0];
+ t1[1] = m[1];
+ t1[2] = m[2];
+ m[0] = m[6];
+ m[1] = m[7];
+ m[2] = m[8];
+
+ m[6] = -t1[0]; // zero
+ m[7] = -t1[1];
+ m[8] = -t1[2];
+
+ t1[0] = u1[0];
+ t1[1] = u1[1];
+ t1[2] = u1[2];
+ u1[0] = u1[6];
+ u1[1] = u1[7];
+ u1[2] = u1[8];
+
+ u1[6] = -t1[0]; // zero
+ u1[7] = -t1[1];
+ u1[8] = -t1[2];
+ } else {
+ g = 1.0/Math.sqrt(m[0]*m[0] + m[6]*m[6]);
+ c2 = m[0]*g;
+ s2 = m[6]*g;
+ t1[0] = c2*m[0] + s2*m[6];
+ t1[1] = c2*m[1] + s2*m[7];
+ t1[2] = c2*m[2] + s2*m[8];
+
+ m[6] = -s2*m[0] + c2*m[6];
+ m[7] = -s2*m[1] + c2*m[7];
+ m[8] = -s2*m[2] + c2*m[8];
+ m[0] = t1[0];
+ m[1] = t1[1];
+ m[2] = t1[2];
+
+ t1[0] = c2*u1[0];
+ t1[1] = c2*u1[1];
+ u1[2] = s2;
+
+ t1[6] = -u1[0]*s2;
+ t1[7] = -u1[1]*s2;
+ u1[8] = c2;
+ u1[0] = t1[0];
+ u1[1] = t1[1];
+ u1[6] = t1[6];
+ u1[7] = t1[7];
+ }
+
+ // v1
+
+ if( m[2]*m[2] < EPS ) {
+ v1[0] = 1.0; v1[1] = 0.0; v1[2] = 0.0;
+ v1[3] = 0.0; v1[4] = 1.0; v1[5] = 0.0;
+ v1[6] = 0.0; v1[7] = 0.0; v1[8] = 1.0;
+ } else if( m[1]*m[1] < EPS ) {
+ t1[2] = m[2];
+ t1[5] = m[5];
+ t1[8] = m[8];
+ m[2] = -m[1];
+ m[5] = -m[4];
+ m[8] = -m[7];
+
+ m[1] = t1[2]; // zero
+ m[4] = t1[5];
+ m[7] = t1[8];
+
+ v1[0] = 1.0; v1[1] = 0.0; v1[2] = 0.0;
+ v1[3] = 0.0; v1[4] = 0.0; v1[5] =-1.0;
+ v1[6] = 0.0; v1[7] = 1.0; v1[8] = 0.0;
+ } else {
+ g = 1.0/Math.sqrt(m[1]*m[1] + m[2]*m[2]);
+ c3 = m[1]*g;
+ s3 = m[2]*g;
+ t1[1] = c3*m[1] + s3*m[2]; // can assign to m[1]?
+ m[2] =-s3*m[1] + c3*m[2]; // zero
+ m[1] = t1[1];
+
+ t1[4] = c3*m[4] + s3*m[5];
+ m[5] =-s3*m[4] + c3*m[5];
+ m[4] = t1[4];
+
+ t1[7] = c3*m[7] + s3*m[8];
+ m[8] =-s3*m[7] + c3*m[8];
+ m[7] = t1[7];
+
+ v1[0] = 1.0; v1[1] = 0.0; v1[2] = 0.0;
+ v1[3] = 0.0; v1[4] = c3; v1[5] = -s3;
+ v1[6] = 0.0; v1[7] = s3; v1[8] = c3;
+ }
+
+ // u3
+
+ if( m[7]*m[7] < EPS ) {
+ } else if( m[4]*m[4] < EPS ) {
+ t1[3] = m[3];
+ t1[4] = m[4];
+ t1[5] = m[5];
+ m[3] = m[6]; // zero
+ m[4] = m[7];
+ m[5] = m[8];
+
+ m[6] = -t1[3]; // zero
+ m[7] = -t1[4]; // zero
+ m[8] = -t1[5];
+
+ t1[3] = u1[3];
+ t1[4] = u1[4];
+ t1[5] = u1[5];
+ u1[3] = u1[6];
+ u1[4] = u1[7];
+ u1[5] = u1[8];
+
+ u1[6] = -t1[3]; // zero
+ u1[7] = -t1[4];
+ u1[8] = -t1[5];
+
+ } else {
+ g = 1.0/Math.sqrt(m[4]*m[4] + m[7]*m[7]);
+ c4 = m[4]*g;
+ s4 = m[7]*g;
+ t1[3] = c4*m[3] + s4*m[6];
+ m[6] =-s4*m[3] + c4*m[6]; // zero
+ m[3] = t1[3];
+
+ t1[4] = c4*m[4] + s4*m[7];
+ m[7] =-s4*m[4] + c4*m[7];
+ m[4] = t1[4];
+
+ t1[5] = c4*m[5] + s4*m[8];
+ m[8] =-s4*m[5] + c4*m[8];
+ m[5] = t1[5];
+
+ t1[3] = c4*u1[3] + s4*u1[6];
+ u1[6] =-s4*u1[3] + c4*u1[6];
+ u1[3] = t1[3];
+
+ t1[4] = c4*u1[4] + s4*u1[7];
+ u1[7] =-s4*u1[4] + c4*u1[7];
+ u1[4] = t1[4];
+
+ t1[5] = c4*u1[5] + s4*u1[8];
+ u1[8] =-s4*u1[5] + c4*u1[8];
+ u1[5] = t1[5];
+ }
+
+ t2[0] = m[0];
+ t2[1] = m[4];
+ t2[2] = m[8];
+ e[0] = m[1];
+ e[1] = m[5];
+
+ if( e[0]*e[0]>EPS || e[1]*e[1]>EPS ) {
+ compute_qr( t2, e, u1, v1);
+ }
+
+ svdScales[0] = t2[0];
+ svdScales[1] = t2[1];
+ svdScales[2] = t2[2];
+
+
+ // Do some optimization here. If scale is unity, simply return the rotation matric.
+ if(almostOne(Math.abs(svdScales[0])) &&
+ almostOne(Math.abs(svdScales[1])) &&
+ almostOne(Math.abs(svdScales[2]))) {
+
+ for(i=0;i<3;i++)
+ if(svdScales[i]<0.0)
+ negCnt++;
+
+ if((negCnt==0)||(negCnt==2)) {
+ //System.out.println("Optimize!!");
+ outScale[0] = outScale[1] = outScale[2] = 1.0;
+ for(i=0;i<9;i++)
+ outRot[i] = svdRot[i];
+
+ return;
+ }
+ }
+
+ // TODO: could eliminate use of t1 and t1 by making a new method which
+ // transposes and multiplies two matricies
+ transpose_mat(u1, t1);
+ transpose_mat(v1, t2);
+
+
+ svdReorder( m, t1, t2, svdRot, svdScales, outRot, outScale);
+ }
+
+
+ private void svdReorder( double[] m, double[] t1, double[] t2, double[] rot,
+ double[] scales, double[] outRot, double[] outScale) {
+
+ int in0, in1, in2, index,i;
+ int[] svdOut = new int[3];
+ double[] svdMag = new double[3];
+
+
+ // check for rotation information in the scales
+ if(scales[0] < 0.0 ) { // move the rotation info to rotation matrix
+ scales[0] = -scales[0];
+ t2[0] = -t2[0];
+ t2[1] = -t2[1];
+ t2[2] = -t2[2];
+ }
+ if(scales[1] < 0.0 ) { // move the rotation info to rotation matrix
+ scales[1] = -scales[1];
+ t2[3] = -t2[3];
+ t2[4] = -t2[4];
+ t2[5] = -t2[5];
+ }
+ if(scales[2] < 0.0 ) { // move the rotation info to rotation matrix
+ scales[2] = -scales[2];
+ t2[6] = -t2[6];
+ t2[7] = -t2[7];
+ t2[8] = -t2[8];
+ }
+
+
+ mat_mul(t1,t2,rot);
+
+ // check for equal scales case and do not reorder
+ if(almostEqual(Math.abs(scales[0]), Math.abs(scales[1])) &&
+ almostEqual(Math.abs(scales[1]), Math.abs(scales[2])) ){
+ for(i=0;i<9;i++){
+ outRot[i] = rot[i];
+ }
+ for(i=0;i<3;i++){
+ outScale[i] = scales[i];
+ }
+
+ }else {
+
+ // sort the order of the results of SVD
+ if( scales[0] > scales[1]) {
+ if( scales[0] > scales[2] ) {
+ if( scales[2] > scales[1] ) {
+ svdOut[0] = 0; svdOut[1] = 2; svdOut[2] = 1; // xzy
+ } else {
+ svdOut[0] = 0; svdOut[1] = 1; svdOut[2] = 2; // xyz
+ }
+ } else {
+ svdOut[0] = 2; svdOut[1] = 0; svdOut[2] = 1; // zxy
+ }
+ } else { // y > x
+ if( scales[1] > scales[2] ) {
+ if( scales[2] > scales[0] ) {
+ svdOut[0] = 1; svdOut[1] = 2; svdOut[2] = 0; // yzx
+ } else {
+ svdOut[0] = 1; svdOut[1] = 0; svdOut[2] = 2; // yxz
+ }
+ } else {
+ svdOut[0] = 2; svdOut[1] = 1; svdOut[2] = 0; // zyx
+ }
+ }
+
+
+ // sort the order of the input matrix
+ svdMag[0] = (m[0]*m[0] + m[1]*m[1] + m[2]*m[2]);
+ svdMag[1] = (m[3]*m[3] + m[4]*m[4] + m[5]*m[5]);
+ svdMag[2] = (m[6]*m[6] + m[7]*m[7] + m[8]*m[8]);
+
+
+ if( svdMag[0] > svdMag[1]) {
+ if( svdMag[0] > svdMag[2] ) {
+ if( svdMag[2] > svdMag[1] ) {
+ // 0 - 2 - 1
+ in0 = 0; in2 = 1; in1 = 2;// xzy
+ } else {
+ // 0 - 1 - 2
+ in0 = 0; in1 = 1; in2 = 2; // xyz
+ }
+ } else {
+ // 2 - 0 - 1
+ in2 = 0; in0 = 1; in1 = 2; // zxy
+ }
+ } else { // y > x 1>0
+ if( svdMag[1] > svdMag[2] ) { // 1>2
+ if( svdMag[2] > svdMag[0] ) { // 2>0
+ // 1 - 2 - 0
+ in1 = 0; in2 = 1; in0 = 2; // yzx
+ } else {
+ // 1 - 0 - 2
+ in1 = 0; in0 = 1; in2 = 2; // yxz
+ }
+ } else {
+ // 2 - 1 - 0
+ in2 = 0; in1 = 1; in0 = 2; // zyx
+ }
+ }
+
+
+ index = svdOut[in0];
+ outScale[0] = scales[index];
+
+ index = svdOut[in1];
+ outScale[1] = scales[index];
+
+ index = svdOut[in2];
+ outScale[2] = scales[index];
+
+ index = svdOut[in0];
+ if (outRot == null)
+ System.out.println("outRot == null");
+ if ( rot == null)
+ System.out.println("rot == null");
+ System.out.flush();
+
+ outRot[0] = rot[index];
+
+ index = svdOut[in0]+3;
+ outRot[0+3] = rot[index];
+
+ index = svdOut[in0]+6;
+ outRot[0+6] = rot[index];
+
+ index = svdOut[in1];
+ outRot[1] = rot[index];
+
+ index = svdOut[in1]+3;
+ outRot[1+3] = rot[index];
+
+ index = svdOut[in1]+6;
+ outRot[1+6] = rot[index];
+
+ index = svdOut[in2];
+ outRot[2] = rot[index];
+
+ index = svdOut[in2]+3;
+ outRot[2+3] = rot[index];
+
+ index = svdOut[in2]+6;
+ outRot[2+6] = rot[index];
+ }
+
+ }
+
+ private int compute_qr( double[] s, double[] e, double[] u, double[] v) {
+ int i,j,k;
+ boolean converged;
+ double shift,ssmin,ssmax,r;
+
+ double utemp,vtemp;
+ double f,g;
+
+ final int MAX_INTERATIONS = 10;
+ final double CONVERGE_TOL = 4.89E-15;
+
+ double[] cosl = new double[2];
+ double[] cosr = new double[2];
+ double[] sinl = new double[2];
+ double[] sinr = new double[2];
+ double[] qr_m = new double[9];
+
+
+ double c_b48 = 1.;
+ double c_b71 = -1.;
+ int first;
+ converged = false;
+
+ first = 1;
+
+ if( Math.abs(e[1]) < CONVERGE_TOL || Math.abs(e[0]) < CONVERGE_TOL) converged = true;
+
+ for(k=0;k<MAX_INTERATIONS && !converged;k++) {
+ shift = compute_shift( s[1], e[1], s[2]);
+ f = (Math.abs(s[0]) - shift) * (d_sign(c_b48, s[0]) + shift/s[0]);
+ g = e[0];
+ r = compute_rot(f, g, sinr, cosr, 0, first);
+ f = cosr[0] * s[0] + sinr[0] * e[0];
+ e[0] = cosr[0] * e[0] - sinr[0] * s[0];
+ g = sinr[0] * s[1];
+ s[1] = cosr[0] * s[1];
+
+ r = compute_rot(f, g, sinl, cosl, 0, first);
+ first = 0;
+ s[0] = r;
+ f = cosl[0] * e[0] + sinl[0] * s[1];
+ s[1] = cosl[0] * s[1] - sinl[0] * e[0];
+ g = sinl[0] * e[1];
+ e[1] = cosl[0] * e[1];
+
+ r = compute_rot(f, g, sinr, cosr, 1, first);
+ e[0] = r;
+ f = cosr[1] * s[1] + sinr[1] * e[1];
+ e[1] = cosr[1] * e[1] - sinr[1] * s[1];
+ g = sinr[1] * s[2];
+ s[2] = cosr[1] * s[2];
+
+ r = compute_rot(f, g, sinl, cosl, 1, first);
+ s[1] = r;
+ f = cosl[1] * e[1] + sinl[1] * s[2];
+ s[2] = cosl[1] * s[2] - sinl[1] * e[1];
+ e[1] = f;
+
+ // update u matrices
+ utemp = u[0];
+ u[0] = cosl[0]*utemp + sinl[0]*u[3];
+ u[3] = -sinl[0]*utemp + cosl[0]*u[3];
+ utemp = u[1];
+ u[1] = cosl[0]*utemp + sinl[0]*u[4];
+ u[4] = -sinl[0]*utemp + cosl[0]*u[4];
+ utemp = u[2];
+ u[2] = cosl[0]*utemp + sinl[0]*u[5];
+ u[5] = -sinl[0]*utemp + cosl[0]*u[5];
+
+ utemp = u[3];
+ u[3] = cosl[1]*utemp + sinl[1]*u[6];
+ u[6] = -sinl[1]*utemp + cosl[1]*u[6];
+ utemp = u[4];
+ u[4] = cosl[1]*utemp + sinl[1]*u[7];
+ u[7] = -sinl[1]*utemp + cosl[1]*u[7];
+ utemp = u[5];
+ u[5] = cosl[1]*utemp + sinl[1]*u[8];
+ u[8] = -sinl[1]*utemp + cosl[1]*u[8];
+
+ // update v matrices
+
+ vtemp = v[0];
+ v[0] = cosr[0]*vtemp + sinr[0]*v[1];
+ v[1] = -sinr[0]*vtemp + cosr[0]*v[1];
+ vtemp = v[3];
+ v[3] = cosr[0]*vtemp + sinr[0]*v[4];
+ v[4] = -sinr[0]*vtemp + cosr[0]*v[4];
+ vtemp = v[6];
+ v[6] = cosr[0]*vtemp + sinr[0]*v[7];
+ v[7] = -sinr[0]*vtemp + cosr[0]*v[7];
+
+ vtemp = v[1];
+ v[1] = cosr[1]*vtemp + sinr[1]*v[2];
+ v[2] = -sinr[1]*vtemp + cosr[1]*v[2];
+ vtemp = v[4];
+ v[4] = cosr[1]*vtemp + sinr[1]*v[5];
+ v[5] = -sinr[1]*vtemp + cosr[1]*v[5];
+ vtemp = v[7];
+ v[7] = cosr[1]*vtemp + sinr[1]*v[8];
+ v[8] = -sinr[1]*vtemp + cosr[1]*v[8];
+
+ // if(debug)System.out.println("\n*********************** iteration #"+k+" ***********************\n");
+
+ qr_m[0] = s[0]; qr_m[1] = e[0]; qr_m[2] = 0.0;
+ qr_m[3] = 0.0; qr_m[4] = s[1]; qr_m[5] =e[1];
+ qr_m[6] = 0.0; qr_m[7] = 0.0; qr_m[8] =s[2];
+
+ if( Math.abs(e[1]) < CONVERGE_TOL || Math.abs(e[0]) < CONVERGE_TOL) converged = true;
+ }
+
+ if( Math.abs(e[1]) < CONVERGE_TOL ) {
+ compute_2X2( s[0],e[0],s[1],s,sinl,cosl,sinr,cosr, 0);
+
+ utemp = u[0];
+ u[0] = cosl[0]*utemp + sinl[0]*u[3];
+ u[3] = -sinl[0]*utemp + cosl[0]*u[3];
+ utemp = u[1];
+ u[1] = cosl[0]*utemp + sinl[0]*u[4];
+ u[4] = -sinl[0]*utemp + cosl[0]*u[4];
+ utemp = u[2];
+ u[2] = cosl[0]*utemp + sinl[0]*u[5];
+ u[5] = -sinl[0]*utemp + cosl[0]*u[5];
+
+ // update v matrices
+
+ vtemp = v[0];
+ v[0] = cosr[0]*vtemp + sinr[0]*v[1];
+ v[1] = -sinr[0]*vtemp + cosr[0]*v[1];
+ vtemp = v[3];
+ v[3] = cosr[0]*vtemp + sinr[0]*v[4];
+ v[4] = -sinr[0]*vtemp + cosr[0]*v[4];
+ vtemp = v[6];
+ v[6] = cosr[0]*vtemp + sinr[0]*v[7];
+ v[7] = -sinr[0]*vtemp + cosr[0]*v[7];
+ } else {
+ compute_2X2( s[1],e[1],s[2],s,sinl,cosl,sinr,cosr,1);
+
+ utemp = u[3];
+ u[3] = cosl[0]*utemp + sinl[0]*u[6];
+ u[6] = -sinl[0]*utemp + cosl[0]*u[6];
+ utemp = u[4];
+ u[4] = cosl[0]*utemp + sinl[0]*u[7];
+ u[7] = -sinl[0]*utemp + cosl[0]*u[7];
+ utemp = u[5];
+ u[5] = cosl[0]*utemp + sinl[0]*u[8];
+ u[8] = -sinl[0]*utemp + cosl[0]*u[8];
+
+ // update v matrices
+
+ vtemp = v[1];
+ v[1] = cosr[0]*vtemp + sinr[0]*v[2];
+ v[2] = -sinr[0]*vtemp + cosr[0]*v[2];
+ vtemp = v[4];
+ v[4] = cosr[0]*vtemp + sinr[0]*v[5];
+ v[5] = -sinr[0]*vtemp + cosr[0]*v[5];
+ vtemp = v[7];
+ v[7] = cosr[0]*vtemp + sinr[0]*v[8];
+ v[8] = -sinr[0]*vtemp + cosr[0]*v[8];
+ }
+
+ return(0);
+ }
+
+ static final double max( double a, double b) {
+ return ( a > b ? a : b);
+ }
+
+ static final double min( double a, double b) {
+ return ( a < b ? a : b);
+ }
+
+ static final double d_sign(double a, double b) {
+ double x = (a >= 0 ? a : - a);
+ return( b >= 0 ? x : -x);
+ }
+
+ static final double compute_shift( double f, double g, double h) {
+ double d__1, d__2;
+ double fhmn, fhmx, c, fa, ga, ha, as, at, au;
+ double ssmin;
+
+ fa = Math.abs(f);
+ ga = Math.abs(g);
+ ha = Math.abs(h);
+ fhmn = min(fa,ha);
+ fhmx = max(fa,ha);
+ if (fhmn == 0.) {
+ ssmin = 0.;
+ if (fhmx == 0.) {
+ } else {
+ d__1 = min(fhmx,ga) / max(fhmx,ga);
+ }
+ } else {
+ if (ga < fhmx) {
+ as = fhmn / fhmx + 1.;
+ at = (fhmx - fhmn) / fhmx;
+ d__1 = ga / fhmx;
+ au = d__1 * d__1;
+ c = 2. / (Math.sqrt(as * as + au) + Math.sqrt(at * at + au));
+ ssmin = fhmn * c;
+ } else {
+ au = fhmx / ga;
+ if (au == 0.) {
+
+
+ ssmin = fhmn * fhmx / ga;
+ } else {
+ as = fhmn / fhmx + 1.;
+ at = (fhmx - fhmn) / fhmx;
+ d__1 = as * au;
+ d__2 = at * au;
+ c = 1. / (Math.sqrt(d__1 * d__1 + 1.) + Math.sqrt(d__2 * d__2 + 1.));
+ ssmin = fhmn * c * au;
+ ssmin += ssmin;
+ }
+ }
+ }
+
+ return(ssmin);
+ }
+
+ static int compute_2X2( double f, double g, double h, double[] single_values,
+ double[] snl, double[] csl, double[] snr, double[] csr, int index) {
+
+ double c_b3 = 2.;
+ double c_b4 = 1.;
+
+ double d__1;
+ int pmax;
+ double temp;
+ boolean swap;
+ double a, d, l, m, r, s, t, tsign, fa, ga, ha;
+ double ft, gt, ht, mm;
+ boolean gasmal;
+ double tt, clt, crt, slt, srt;
+ double ssmin,ssmax;
+
+ ssmax = single_values[0];
+ ssmin = single_values[1];
+ clt = 0.0;
+ crt = 0.0;
+ slt = 0.0;
+ srt = 0.0;
+ tsign = 0.0;
+
+ ft = f;
+ fa = Math.abs(ft);
+ ht = h;
+ ha = Math.abs(h);
+
+ pmax = 1;
+ if( ha > fa)
+ swap = true;
+ else
+ swap = false;
+
+ if (swap) {
+ pmax = 3;
+ temp = ft;
+ ft = ht;
+ ht = temp;
+ temp = fa;
+ fa = ha;
+ ha = temp;
+
+ }
+ gt = g;
+ ga = Math.abs(gt);
+ if (ga == 0.) {
+
+ single_values[1] = ha;
+ single_values[0] = fa;
+ clt = 1.;
+ crt = 1.;
+ slt = 0.;
+ srt = 0.;
+ } else {
+ gasmal = true;
+
+ if (ga > fa) {
+ pmax = 2;
+ if (fa / ga < EPS) {
+
+ gasmal = false;
+ ssmax = ga;
+ if (ha > 1.) {
+ ssmin = fa / (ga / ha);
+ } else {
+ ssmin = fa / ga * ha;
+ }
+ clt = 1.;
+ slt = ht / gt;
+ srt = 1.;
+ crt = ft / gt;
+ }
+ }
+ if (gasmal) {
+
+ d = fa - ha;
+ if (d == fa) {
+
+ l = 1.;
+ } else {
+ l = d / fa;
+ }
+
+ m = gt / ft;
+
+ t = 2. - l;
+
+ mm = m * m;
+ tt = t * t;
+ s = Math.sqrt(tt + mm);
+
+ if (l == 0.) {
+ r = Math.abs(m);
+ } else {
+ r = Math.sqrt(l * l + mm);
+ }
+
+ a = (s + r) * .5;
+
+ if (ga > fa) {
+ pmax = 2;
+ if (fa / ga < EPS) {
+
+ gasmal = false;
+ ssmax = ga;
+ if (ha > 1.) {
+ ssmin = fa / (ga / ha);
+ } else {
+ ssmin = fa / ga * ha;
+ }
+ clt = 1.;
+ slt = ht / gt;
+ srt = 1.;
+ crt = ft / gt;
+ }
+ }
+ if (gasmal) {
+
+ d = fa - ha;
+ if (d == fa) {
+
+ l = 1.;
+ } else {
+ l = d / fa;
+ }
+
+ m = gt / ft;
+
+ t = 2. - l;
+
+ mm = m * m;
+ tt = t * t;
+ s = Math.sqrt(tt + mm);
+
+ if (l == 0.) {
+ r = Math.abs(m);
+ } else {
+ r = Math.sqrt(l * l + mm);
+ }
+
+ a = (s + r) * .5;
+
+
+ ssmin = ha / a;
+ ssmax = fa * a;
+ if (mm == 0.) {
+
+ if (l == 0.) {
+ t = d_sign(c_b3, ft) * d_sign(c_b4, gt);
+ } else {
+ t = gt / d_sign(d, ft) + m / t;
+ }
+ } else {
+ t = (m / (s + t) + m / (r + l)) * (a + 1.);
+ }
+ l = Math.sqrt(t * t + 4.);
+ crt = 2. / l;
+ srt = t / l;
+ clt = (crt + srt * m) / a;
+ slt = ht / ft * srt / a;
+ }
+ }
+ if (swap) {
+ csl[0] = srt;
+ snl[0] = crt;
+ csr[0] = slt;
+ snr[0] = clt;
+ } else {
+ csl[0] = clt;
+ snl[0] = slt;
+ csr[0] = crt;
+ snr[0] = srt;
+ }
+
+ if (pmax == 1) {
+ tsign = d_sign(c_b4, csr[0]) * d_sign(c_b4, csl[0]) * d_sign(c_b4, f);
+ }
+ if (pmax == 2) {
+ tsign = d_sign(c_b4, snr[0]) * d_sign(c_b4, csl[0]) * d_sign(c_b4, g);
+ }
+ if (pmax == 3) {
+ tsign = d_sign(c_b4, snr[0]) * d_sign(c_b4, snl[0]) * d_sign(c_b4, h);
+ }
+ single_values[index] = d_sign(ssmax, tsign);
+ d__1 = tsign * d_sign(c_b4, f) * d_sign(c_b4, h);
+ single_values[index+1] = d_sign(ssmin, d__1);
+
+
+ }
+ return 0;
+ }
+
+ static double compute_rot( double f, double g, double[] sin, double[] cos, int index, int first) {
+ int i__1;
+ double d__1, d__2;
+ double cs,sn;
+ int i;
+ double scale;
+ int count;
+ double f1, g1;
+ double r;
+ final double safmn2 = 2.002083095183101E-146;
+ final double safmx2 = 4.994797680505588E+145;
+
+ if (g == 0.) {
+ cs = 1.;
+ sn = 0.;
+ r = f;
+ } else if (f == 0.) {
+ cs = 0.;
+ sn = 1.;
+ r = g;
+ } else {
+ f1 = f;
+ g1 = g;
+ scale = max(Math.abs(f1),Math.abs(g1));
+ if (scale >= safmx2) {
+ count = 0;
+ while(scale >= safmx2) {
+ ++count;
+ f1 *= safmn2;
+ g1 *= safmn2;
+ scale = max(Math.abs(f1),Math.abs(g1));
+ }
+ r = Math.sqrt(f1*f1 + g1*g1);
+ cs = f1 / r;
+ sn = g1 / r;
+ i__1 = count;
+ for (i = 1; i <= count; ++i) {
+ r *= safmx2;
+ }
+ } else if (scale <= safmn2) {
+ count = 0;
+ while(scale <= safmn2) {
+ ++count;
+ f1 *= safmx2;
+ g1 *= safmx2;
+ scale = max(Math.abs(f1),Math.abs(g1));
+ }
+ r = Math.sqrt(f1*f1 + g1*g1);
+ cs = f1 / r;
+ sn = g1 / r;
+ i__1 = count;
+ for (i = 1; i <= count; ++i) {
+ r *= safmn2;
+ }
+ } else {
+ r = Math.sqrt(f1*f1 + g1*g1);
+ cs = f1 / r;
+ sn = g1 / r;
+ }
+ if (Math.abs(f) > Math.abs(g) && cs < 0.) {
+ cs = -cs;
+ sn = -sn;
+ r = -r;
+ }
+ }
+ sin[index] = sn;
+ cos[index] = cs;
+ return r;
+
+ }
+
+ static final private double max3( double[] values) {
+ if( values[0] > values[1] ) {
+ if( values[0] > values[2] )
+ return(values[0]);
+ else
+ return(values[2]);
+ } else {
+ if( values[1] > values[2] )
+ return(values[1]);
+ else
+ return(values[2]);
+ }
+ }
+
+
+ final private void computeScales(boolean forceSVD) {
+
+ if(scales == null)
+ scales = new double[3];
+
+ if ((!forceSVD || ((dirtyBits & SVD_BIT) == 0)) && isAffine()) {
+ if (isCongruent()) {
+ if (((dirtyBits & RIGID_BIT) == 0) &&
+ ((type & RIGID) != 0)) {
+ scales[0] = scales[1] = scales[2] = 1;
+ dirtyBits &= ~SCALE_BIT;
+ return;
+ }
+ scales[0] = scales[1] = scales[2] =
+ Math.sqrt(mat[0]*mat[0] + mat[4]*mat[4] +
+ mat[8]*mat[8]);
+ dirtyBits &= ~SCALE_BIT;
+ return;
+ }
+ if (isOrtho()) {
+ scales[0] = Math.sqrt(mat[0]*mat[0] + mat[4]*mat[4] +
+ mat[8]*mat[8]);
+ scales[1] = Math.sqrt(mat[1]*mat[1] + mat[5]*mat[5] +
+ mat[9]*mat[9]);
+ scales[2] = Math.sqrt(mat[2]*mat[2] + mat[6]*mat[6] +
+ mat[10]*mat[10]);
+ dirtyBits &= ~SCALE_BIT;
+ return;
+ }
+ }
+ // fall back to use SVD decomposition
+ if (rot == null)
+ rot = new double[9];
+
+ compute_svd(this, scales, rot);
+ dirtyBits &= ~ROTSCALESVD_DIRTY;
+ }
+
+ final private void computeScaleRotation(boolean forceSVD) {
+
+ if(rot == null)
+ rot = new double[9];
+
+ if(scales == null)
+ scales = new double[3];
+
+ if ((!forceSVD || ((dirtyBits & SVD_BIT) == 0)) && isAffine()) {
+ if (isCongruent()) {
+ if (((dirtyBits & RIGID_BIT) == 0) &&
+ ((type & RIGID) != 0)) {
+ rot[0] = mat[0];
+ rot[1] = mat[1];
+ rot[2] = mat[2];
+ rot[3] = mat[4];
+ rot[4] = mat[5];
+ rot[5] = mat[6];
+ rot[6] = mat[8];
+ rot[7] = mat[9];
+ rot[8] = mat[10];
+ scales[0] = scales[1] = scales[2] = 1;
+ dirtyBits &= (~ROTATION_BIT | ~SCALE_BIT);
+ return;
+ }
+ double s = Math.sqrt(mat[0]*mat[0] + mat[4]*mat[4] + mat[8]*mat[8]);
+ if (s == 0) {
+ compute_svd(this, scales, rot);
+ return;
+ }
+ scales[0] = scales[1] = scales[2] = s;
+ s = 1/s;
+ rot[0] = mat[0]*s;
+ rot[1] = mat[1]*s;
+ rot[2] = mat[2]*s;
+ rot[3] = mat[4]*s;
+ rot[4] = mat[5]*s;
+ rot[5] = mat[6]*s;
+ rot[6] = mat[8]*s;
+ rot[7] = mat[9]*s;
+ rot[8] = mat[10]*s;
+ dirtyBits &= (~ROTATION_BIT | ~SCALE_BIT);
+ return;
+ }
+ if (isOrtho()) {
+ double s;
+
+ scales[0] = Math.sqrt(mat[0]*mat[0] + mat[4]*mat[4] + mat[8]*mat[8]);
+ scales[1] = Math.sqrt(mat[1]*mat[1] + mat[5]*mat[5] + mat[9]*mat[9]);
+ scales[2] = Math.sqrt(mat[2]*mat[2] + mat[6]*mat[6] + mat[10]*mat[10]);
+
+ if ((scales[0] == 0) || (scales[1] == 0) || (scales[2] == 0)) {
+ compute_svd(this, scales, rot);
+ return;
+ }
+ s = 1/scales[0];
+ rot[0] = mat[0]*s;
+ rot[3] = mat[4]*s;
+ rot[6] = mat[8]*s;
+ s = 1/scales[1];
+ rot[1] = mat[1]*s;
+ rot[4] = mat[5]*s;
+ rot[7] = mat[9]*s;
+ s = 1/scales[2];
+ rot[2] = mat[2]*s;
+ rot[5] = mat[6]*s;
+ rot[8] = mat[10]*s;
+ dirtyBits &= (~ROTATION_BIT | ~SCALE_BIT);
+ return;
+ }
+ }
+ // fall back to use SVD decomposition
+ compute_svd(this, scales, rot);
+ dirtyBits &= ~ROTSCALESVD_DIRTY;
+ }
+
+
+ final void getRotation(Transform3D t) {
+ if ((dirtyBits & ROTATION_BIT)!= 0) {
+ computeScaleRotation(false);
+ }
+
+ t.mat[3] = t.mat[7] = t.mat[11] = t.mat[12] = t.mat[13] =
+ t.mat[14] = 0;
+ t.mat[15] = 1;
+ t.mat[0] = rot[0];
+ t.mat[1] = rot[1];
+ t.mat[2] = rot[2];
+ t.mat[4] = rot[3];
+ t.mat[5] = rot[4];
+ t.mat[6] = rot[5];
+ t.mat[8] = rot[6];
+ t.mat[9] = rot[7];
+ t.mat[10] = rot[8];
+
+ t.type = ORTHOGONAL | RIGID | CONGRUENT| AFFINE | ORTHO;
+ if ((dirtyBits & SVD_BIT) != 0) {
+ t.dirtyBits = CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+ } else {
+ t.dirtyBits = CLASSIFY_BIT | ROTATION_BIT | SCALE_BIT;
+ }
+ }
+
+ // somehow CanvasViewCache will directly modify mat[]
+ // instead of calling ortho(). So we need to reset dirty bit
+ final void setOrthoDirtyBit() {
+ dirtyBits = CLASSIFY_BIT | ROTSCALESVD_DIRTY;
+ type = 0;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TransformGroup.java b/src/classes/share/javax/media/j3d/TransformGroup.java
new file mode 100644
index 0000000..8895f1e
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransformGroup.java
@@ -0,0 +1,180 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Group node that contains a transform. The TransformGroup node
+ * specifies a single spatial transformation, via a Transform3D
+ * object, that can position, orient, and scale all of its children.
+ * <P>
+ * The specified transformation must be affine. Further, if the
+ * TransformGroup node is used as an ancestor of a ViewPlatform node
+ * in the scene graph, the transformation must be congruent-only
+ * rotations, translations, and uniform scales are allowed in
+ * a direct path from a Locale to a ViewPlatform node.
+ * <P>
+ * Note: Even though arbitrary affine transformations are
+ * allowed, better performance will result if all matrices
+ * within a branch graph are congruent, containing only rotations
+ * translation, and uniform scale.
+ * <P>
+ * The effects of transformations in the scene graph are cumulative.
+ * The concatenation of the transformations of each TransformGroup in
+ * a direct path from the Locale to a Leaf node defines a composite
+ * model transformation (CMT) that takes points in that Leaf node's
+ * local coordinates and transforms them into Virtual World (Vworld)
+ * coordinates. This composite transformation is used to
+ * transform points, normals, and distances into Vworld coordinates.
+ * Points are transformed by the CMT. Normals are transformed by the
+ * inverse-transpose of the CMT. Distances are transformed by the scale
+ * of the CMT. In the case of a transformation containing a nonuniform
+ * scale or shear, the maximum scale value in
+ * any direction is used. This ensures, for example, that a transformed
+ * bounding sphere, which is specified as a point and a radius,
+ * continues to enclose all objects that are also transformed using
+ * a nonuniform scale.
+ * <P>
+ */
+
+public class TransformGroup extends Group {
+ /**
+ * Specifies that the node allows access to
+ * its object's transform information.
+ */
+ public static final int
+ ALLOW_TRANSFORM_READ = CapabilityBits.TRANSFORM_GROUP_ALLOW_TRANSFORM_READ;
+
+ /**
+ * Specifies that the node allows writing
+ * its object's transform information.
+ */
+ public static final int
+ ALLOW_TRANSFORM_WRITE = CapabilityBits.TRANSFORM_GROUP_ALLOW_TRANSFORM_WRITE;
+
+ /**
+ * Constructs and initializes a TransformGroup using an
+ * identity transform.
+ */
+ public TransformGroup() {
+ }
+
+ /**
+ * Constructs and initializes a TransformGroup from
+ * the Transform passed.
+ * @param t1 the transform3D object
+ * @exception BadTransformException if the transform is not affine.
+ */
+ public TransformGroup(Transform3D t1) {
+ if (!t1.isAffine()) {
+ throw new BadTransformException(J3dI18N.getString("TransformGroup0"));
+ }
+
+ ((TransformGroupRetained)this.retained).setTransform(t1);
+ }
+
+ /**
+ * Creates the retained mode TransformGroupRetained object that this
+ * TransformGroup object will point to.
+ */
+ void createRetained() {
+ this.retained = new TransformGroupRetained();
+ this.retained.setSource(this);
+ }
+
+ /**
+ * Sets the transform component of this TransformGroup to the value of
+ * the passed transform.
+ * @param t1 the transform to be copied
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception BadTransformException if the transform is not affine.
+ */
+ public void setTransform(Transform3D t1) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TRANSFORM_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransformGroup1"));
+
+ if (!t1.isAffine()) {
+ throw new BadTransformException(J3dI18N.getString("TransformGroup0"));
+ }
+
+ ((TransformGroupRetained)this.retained).setTransform(t1);
+ }
+
+ /**
+ * Copies the transform component of this TransformGroup into
+ * the passed transform object.
+ * @param t1 the transform object to be copied into
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void getTransform(Transform3D t1) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_TRANSFORM_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransformGroup2"));
+
+ ((TransformGroupRetained)this.retained).getTransform(t1);
+ }
+
+
+ /**
+ * Creates 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) {
+ TransformGroup tg = new TransformGroup();
+ tg.duplicateNode(this, forceDuplicate);
+ return tg;
+ }
+
+
+ /**
+ * Copies all TransformGroup 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ Transform3D t = VirtualUniverse.mc.getTransform3D(null);
+ ((TransformGroupRetained) originalNode.retained).getTransform(t);
+ ((TransformGroupRetained) retained).setTransform(t);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TransformGroupData.java b/src/classes/share/javax/media/j3d/TransformGroupData.java
new file mode 100644
index 0000000..91a90e5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransformGroupData.java
@@ -0,0 +1,23 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class TransformGroupData extends NodeData {
+ // per path node data
+ // TODO: replace per path mirror objects with node data
+ // TODO: move other TransfromGroup related data here
+ boolean switchDirty = false;
+
+ // use for eliminate multiple updates and generate unique targets
+ boolean markedDirty = false;
+}
diff --git a/src/classes/share/javax/media/j3d/TransformGroupRetained.java b/src/classes/share/javax/media/j3d/TransformGroupRetained.java
new file mode 100644
index 0000000..d09e1c8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransformGroupRetained.java
@@ -0,0 +1,1219 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * Group node that contains a transform.
+ */
+
+class TransformGroupRetained extends GroupRetained implements TargetsInterface
+{
+
+ /**
+ * The Transform value for the TransformGroup.
+ */
+ Transform3D transform = new Transform3D();
+
+ /**
+ * The inverse of the transform
+ */
+ Transform3D invTransform = null;
+
+ /**
+ * The transpose of the inverse of the transform
+ */
+ Transform3D normalTransform = null;
+
+ /**
+ * The Transform value currently being used internally
+ */
+ Transform3D currentTransform = new Transform3D();
+
+ /**
+ * localVworld values for children of this TG
+ */
+ Transform3D[][] childLocalToVworld = null;
+ int[][] childLocalToVworldIndex = null;
+
+ // working variable for children transforms
+ Transform3D[][] childTrans = null;
+ int[][] childTransIndex = null;
+
+
+ /**
+ * A bitmask of the types in targets
+ */
+ int localTargetThreads = 0;
+
+ // combined localTargetThreads and decendants' localTargetThreads
+ int targetThreads = 0;
+
+ /**
+ * A list of WakeupOnTransformChange conditions for this Transform
+ */
+ WakeupIndexedList transformChange = null;
+
+ // The current list of child transform group nodes or link nodes
+ // under a transform group
+ ArrayList childTransformLinks = new ArrayList(1);
+
+ // working area while compile
+ boolean needNormalsTransform = false; // true if normals transformation
+ // is needed to push this
+ // transform down to geometry
+
+ // key which identifies a unique path from a
+ // locale to this transform group
+ HashKey currentKey = new HashKey();
+
+ boolean aboveAViewPlatform = false;
+
+ // maximum transform level of all shared path
+ int maxTransformLevel = -1;
+
+ // List of transform level, one per shared path
+ int transformLevels[] = null;
+
+ // J3d copy.
+ CachedTargets[] j3dCTs = null;
+
+ // User copy.
+ CachedTargets[] cachedTargets = null;
+
+ // Contains per path data, TODO: move to NodeRetained
+ TransformGroupData[] perPathData = null;
+
+
+ /**
+ * The constructor
+ */
+ TransformGroupRetained() {
+ this.nodeType = NodeRetained.TRANSFORMGROUP;
+ }
+
+ /**
+ * Sets the transform component of this TransformGroup to the value of
+ * the passed transform.
+ * @param t1 the transform to be copied
+ */
+ void setTransform(Transform3D t1) {
+ J3dMessage tchangeMessage = null;
+ int i, j;
+ Transform3D trans = null;
+
+ if (staticTransform != null) {
+ // this writeable transformGroup has a static transform
+ // merged into this node
+
+ trans = VirtualUniverse.mc.getTransform3D(staticTransform.transform);
+ trans.mul(t1);
+
+ transform.setWithLock(trans);
+
+ } else {
+ trans = VirtualUniverse.mc.getTransform3D(t1);
+ transform.setWithLock(t1);
+ }
+
+ if (transformChange != null) {
+ notifyConditions();
+ }
+
+ if (source.isLive()) {
+
+ if (aboveAViewPlatform && !t1.isCongruent()) {
+ throw new BadTransformException(J3dI18N.getString("ViewPlatformRetained0"));
+ }
+
+ tchangeMessage = VirtualUniverse.mc.getMessage();
+ tchangeMessage.type = J3dMessage.TRANSFORM_CHANGED;
+ tchangeMessage.threads = targetThreads;
+ tchangeMessage.args[1] = this;
+ tchangeMessage.args[2] = trans;
+
+ tchangeMessage.universe = universe;
+ //System.out.println("TransformGroupRetained --- TRANSFORM_CHANGED " + this);
+ VirtualUniverse.mc.processMessage(tchangeMessage);
+ }
+ }
+
+ /**
+ * Copies the transform component of this TransformGroup into
+ * the passed transform object.
+ * @param t1 the transform object to be copied into
+ */
+ void getTransform(Transform3D t1) {
+ transform.getWithLock(t1);
+
+ // if staticTransform exists for this node, need to
+ // redetermine the original user specified transform
+
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ t1.mul(invTransform, t1);
+ }
+ }
+
+
+ // get the inverse of the transform -- note: this method only
+ // supports static transform
+
+ Transform3D getInvTransform() {
+ if (invTransform == null) {
+ invTransform = new Transform3D(transform);
+ invTransform.invert();
+ }
+ return invTransform;
+ }
+
+
+ // get the inverse of the transpose -- note: this method only
+ // supports static transform, the translation component will
+ // not transform
+ Transform3D getNormalTransform() {
+ if (normalTransform == null) {
+ normalTransform = new Transform3D(transform);
+ normalTransform.invert();
+ normalTransform.transpose();
+ }
+ return normalTransform;
+ }
+
+ // synchronized with TransformStructure
+ synchronized void setNodeData(SetLiveState s) {
+ int i;
+
+ super.setNodeData(s);
+
+ childTrans = new Transform3D[s.currentTransforms.length][2];
+ childTransIndex = new int[s.currentTransforms.length][2];
+
+ for (i=0; i< s.currentTransforms.length; i++) {
+ childTrans[i][0] = new Transform3D();
+
+ childTrans[i][0].mul(s.currentTransforms[i]
+ [s.currentTransformsIndex[i]
+ [CURRENT_LOCAL_TO_VWORLD]], currentTransform);
+ childTrans[i][1] = new Transform3D(childTrans[i][0]);
+ childTransIndex[i][0] = 0;
+ childTransIndex[i][1] = 0;
+
+ }
+
+ if (!s.inSharedGroup) {
+ s.transformLevels[0] += 1;
+ maxTransformLevel = s.transformLevels[0];
+ } else {
+ for (i=0; i<s.keys.length; i++) {
+ s.transformLevels[i] += 1;
+ if (s.transformLevels[i] > maxTransformLevel) {
+ maxTransformLevel = s.transformLevels[i];
+ }
+ }
+ }
+
+ if (!inSharedGroup) {
+ if (childLocalToVworld == null) {
+ // If the node is a transformGroup then need to keep
+ // the child transforms as well
+ childLocalToVworld = new Transform3D[1][];
+ childLocalToVworldIndex = new int[1][];
+ transformLevels = new int[1];
+ // Use by TransformStructure
+ cachedTargets = new CachedTargets[1];
+ perPathData = new TransformGroupData[1];
+ }
+ childLocalToVworld[0] = childTrans[0];
+ childLocalToVworldIndex[0] = childTransIndex[0];
+ transformLevels[0] = s.transformLevels[0];
+
+ setAuxData(s, 0, 0);
+ } else {
+
+ // For inSharedGroup case.
+ int j, len;
+
+ if (childLocalToVworld == null) {
+ childLocalToVworld = new Transform3D[s.keys.length][];
+ childLocalToVworldIndex = new int[s.keys.length][];
+ transformLevels = new int[s.keys.length];
+ cachedTargets = new CachedTargets[s.keys.length];
+ perPathData = new TransformGroupData[s.keys.length];
+ len=0;
+ } else {
+
+ len = localToVworld.length - s.keys.length;
+
+ int newLen = localToVworld.length;
+
+ Transform3D newChildTList[][] = new Transform3D[newLen][];
+ int newChildIndexList[][] = new int[newLen][];
+ int newTransformLevels[] = new int[newLen];
+ CachedTargets newTargets[] = new CachedTargets[newLen];
+ TransformGroupData newPerPathData[] = new TransformGroupData[newLen];
+
+ System.arraycopy(childLocalToVworld, 0,
+ newChildTList, 0, childLocalToVworld.length);
+ System.arraycopy(childLocalToVworldIndex, 0,
+ newChildIndexList, 0, childLocalToVworldIndex.length);
+ System.arraycopy(transformLevels, 0,
+ newTransformLevels, 0, transformLevels.length);
+
+ System.arraycopy(cachedTargets, 0,
+ newTargets, 0, cachedTargets.length);
+
+ System.arraycopy(perPathData, 0,
+ newPerPathData, 0, perPathData.length);
+
+ childLocalToVworld = newChildTList;
+ childLocalToVworldIndex = newChildIndexList;
+ transformLevels = newTransformLevels;
+ cachedTargets = newTargets;
+ perPathData = newPerPathData;
+ }
+
+ int hkIndex;
+ int hkIndexPlus1, blkSize;
+
+ for(i=len, j=0; i<localToVworld.length; i++, j++) {
+ hkIndex = s.keys[j].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+
+ if(hkIndex < 0) {
+ System.out.println("Can't Find matching hashKey in setNodeData.");
+ System.out.println("We're in TROUBLE!!!");
+ break;
+ } else if(hkIndex >= i) { // Append to last.
+ childLocalToVworld[i] = childTrans[j];
+ childLocalToVworldIndex[i] = childTransIndex[j];
+ transformLevels[i] = s.transformLevels[j];
+ } else {
+ hkIndexPlus1 = hkIndex + 1;
+ blkSize = i - hkIndex;
+
+ System.arraycopy(childLocalToVworld, hkIndex,
+ childLocalToVworld, hkIndexPlus1, blkSize);
+
+ System.arraycopy(childLocalToVworldIndex, hkIndex,
+ childLocalToVworldIndex, hkIndexPlus1, blkSize);
+
+ System.arraycopy(transformLevels, hkIndex,
+ transformLevels, hkIndexPlus1, blkSize);
+
+ System.arraycopy(cachedTargets, hkIndex,
+ cachedTargets, hkIndexPlus1, blkSize);
+
+ System.arraycopy(perPathData, hkIndex,
+ perPathData, hkIndexPlus1, blkSize);
+
+ childLocalToVworld[hkIndex] = childTrans[j];
+ childLocalToVworldIndex[hkIndex] = childTransIndex[j];
+ transformLevels[hkIndex] = s.transformLevels[j];
+ }
+
+ setAuxData(s, j, hkIndex);
+ }
+ }
+ if (s.childTransformLinks != null) {
+ // do not duplicate shared nodes
+ synchronized(s.childTransformLinks) {
+ if(!inSharedGroup || !s.childTransformLinks.contains(this)) {
+ s.childTransformLinks.add(this);
+ }
+ }
+ }
+
+ s.localToVworld = childLocalToVworld;
+ s.localToVworldIndex = childLocalToVworldIndex;
+ s.currentTransforms = childTrans;
+ s.currentTransformsIndex = childTransIndex;
+
+ s.childTransformLinks = childTransformLinks;
+ s.parentTransformLink = this;
+ }
+
+ void setAuxData(SetLiveState s, int index, int hkIndex) {
+ super.setAuxData(s, index, hkIndex);
+ perPathData[hkIndex] = new TransformGroupData();
+ perPathData[hkIndex].switchState =
+ (SwitchState)s.switchStates.get(hkIndex);
+ }
+
+
+ // Add a WakeupOnTransformChange to the list
+ void removeCondition(WakeupOnTransformChange wakeup) {
+ synchronized (transformChange) {
+ transformChange.remove(wakeup);
+ }
+ }
+
+ // Add a WakeupOnTransformChange to the list
+ void addCondition(WakeupOnTransformChange wakeup) {
+ synchronized (transformChange) {
+ transformChange.add(wakeup);
+ }
+ }
+
+ void notifyConditions() {
+ synchronized (transformChange) {
+ WakeupOnTransformChange list[] = (WakeupOnTransformChange [])
+ transformChange.toArray(false);
+ for (int i=transformChange.size()-1; i >=0; i--) {
+ list[i].setTriggered();
+ }
+ }
+ }
+
+ boolean isStatic() {
+ if (!super.isStatic() ||
+ source.getCapability(TransformGroup.ALLOW_TRANSFORM_READ) ||
+ source.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ transform.mul(xform.transform, transform);
+ }
+
+ void traverse(boolean sameLevel, int level) {
+
+ System.out.println();
+ for (int i = 0; i < level; i++) {
+ System.out.print(".");
+ }
+ System.out.print(this);
+
+ if (isStatic()) {
+ System.out.print(" (s)");
+ } else {
+ System.out.print(" (w)");
+ }
+ System.out.println();
+ System.out.println(transform.toString());
+ super.traverse(true, level);
+ }
+
+ void compile(CompileState compState) {
+
+ // save and reset the keepTG and needNormalsTransform flags
+
+ boolean saveKeepTG = compState.keepTG;
+ compState.keepTG = false;
+
+ boolean saveNeedNormalsTransform = compState.needNormalsTransform;
+ compState.needNormalsTransform = false;
+
+ super.compile(compState);
+
+ if (compState.keepTG) {
+ // keep this transform group, don't merge it
+
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+ }
+
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ compState.numTransformGroups++;
+ if (isStatic())
+ compState.numStaticTransformGroups++;
+ if (mergeFlag == SceneGraphObjectRetained.MERGE)
+ compState.numMergedTransformGroups++;
+ }
+
+ if (mergeFlag == SceneGraphObjectRetained.DONT_MERGE) {
+ // a non-mergeable TG will trigger a merge of its subtree
+
+ compState.staticTransform = null;
+ compState.parentGroup = null;
+ super.merge(compState);
+
+ } else {
+ // flag this TG as to be merged later on
+ mergeFlag = SceneGraphObjectRetained.MERGE;
+ }
+
+ // restore compile state
+ compState.keepTG = saveKeepTG;
+ this.needNormalsTransform = compState.needNormalsTransform;
+ compState.needNormalsTransform = saveNeedNormalsTransform;
+ }
+
+ void merge(CompileState compState) {
+
+ TransformGroupRetained saveStaticTransform;
+
+ // merge the transforms
+ if (compState.staticTransform != null) {
+ staticTransform = compState.staticTransform;
+ mergeTransform(compState.staticTransform);
+ }
+
+ if (mergeFlag == SceneGraphObjectRetained.MERGE) {
+
+ // before we push down the static transform, check
+ // to see if the transform will be pushed down to shapes
+ // with geometry_with_normals and if so, check if
+ // the normal transform has uniform scale or not. If
+ // it doesn't, don't push it down.
+
+ if (this.needNormalsTransform) {
+ Transform3D normalXform = this.getNormalTransform();
+ if (!normalXform.isCongruent()) {
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+ }
+ }
+ }
+
+ if (mergeFlag == SceneGraphObjectRetained.MERGE) {
+ saveStaticTransform = compState.staticTransform;
+ compState.staticTransform = this;
+
+ // go to the merge method of the group node to start
+ // pushing down the static transform until it hits
+ // a leaf or a subtree which is already merged.
+ super.merge(compState);
+
+ // reset the compile state
+ compState.staticTransform = saveStaticTransform;
+
+ } else {
+ compState.parentGroup.compiledChildrenList.add(this);
+ parent = compState.parentGroup;
+ }
+
+ mergeFlag = SceneGraphObjectRetained.MERGE_DONE;
+ }
+
+ /**
+ * This setlive simply concatinates it's transform onto all the ones
+ * passed in.
+ */
+ void setLive(SetLiveState s) {
+ int i,j;
+ Transform3D trans = null;
+ Targets[] newTargets = null;
+ Targets[] savedTransformTargets = null;
+ int oldTraverseFlags = 0;
+ int len;
+ Object obj;
+
+ // TODO - optimization for targetThreads computation, require
+ // cleanup in GroupRetained.doSetLive()
+ //int savedTargetThreads = 0;
+ //savedTargetThreads = s.transformTargetThreads;
+ //s.transformTargetThreads = 0;
+
+ oldTraverseFlags = s.traverseFlags;
+
+ savedTransformTargets = s.transformTargets;
+
+ int numPaths = (s.inSharedGroup)? s.keys.length : 1;
+ newTargets = new Targets[numPaths];
+ for(i=0; i<numPaths; i++) {
+ newTargets[i] = new Targets();
+ }
+
+ s.transformTargets = newTargets;
+ s.traverseFlags = 0;
+
+ // This is needed b/c super.setlive is called after inSharedGroup check.
+ inSharedGroup = s.inSharedGroup;
+
+ trans = VirtualUniverse.mc.getTransform3D(null);
+ transform.getWithLock(trans);
+ currentTransform.set(trans);
+
+
+ ArrayList savedChildTransformLinks = s.childTransformLinks;
+ GroupRetained savedParentTransformLink = s.parentTransformLink;
+ Transform3D[][] oldCurrentList = s.currentTransforms;
+ int[][] oldCurrentIndexList = s.currentTransformsIndex;
+
+
+ super.doSetLive(s);
+
+
+ if (! inSharedGroup) {
+ if (s.transformTargets[0] != null) {
+ cachedTargets[0] = s.transformTargets[0].snapShotInit();
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.GRP_TARGETS);
+ }
+ } else {
+ int hkIndex;
+ for(i=0; i<numPaths; i++) {
+ if (s.transformTargets[i] != null) {
+ hkIndex = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ cachedTargets[hkIndex] = s.transformTargets[i].snapShotInit();
+ }
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(this, Targets.GRP_TARGETS);
+ }
+ }
+ }
+
+ // Assign data in cachedTargets to j3dCTs.
+ j3dCTs = new CachedTargets[cachedTargets.length];
+ copyCachedTargets(TargetsInterface.TRANSFORM_TARGETS, j3dCTs);
+
+ computeTargetThreads(TargetsInterface.TRANSFORM_TARGETS, cachedTargets);
+
+ // restore setLiveState from it's local variables.
+ // setNodeData did keep a reference to these variables.
+ s.localToVworld = localToVworld;
+ s.localToVworldIndex = localToVworldIndex;
+ s.currentTransforms = oldCurrentList;
+ s.currentTransformsIndex = oldCurrentIndexList;
+
+ s.childTransformLinks = savedChildTransformLinks;
+ s.parentTransformLink = savedParentTransformLink;
+
+ s.transformTargets = savedTransformTargets;
+
+ if (!s.inSharedGroup) {
+ s.transformLevels[0] -= 1;
+ } else {
+ for (i=0; i<s.keys.length; i++) {
+ s.transformLevels[i] -= 1;
+ }
+ }
+
+
+ if ((s.traverseFlags & NodeRetained.CONTAINS_VIEWPLATFORM) != 0) {
+ aboveAViewPlatform = true;
+ }
+ s.traverseFlags |= oldTraverseFlags;
+
+ if (aboveAViewPlatform && !trans.isCongruent()) {
+ throw new BadTransformException(J3dI18N.getString("ViewPlatformRetained0"));
+ }
+
+ super.markAsLive();
+ }
+
+
+ /**
+ * remove the localToVworld transform for a transformGroup
+ */
+ void removeNodeData(SetLiveState s) {
+
+ synchronized (this) { // synchronized with TransformStructure
+
+ if (refCount <= 0) {
+ childLocalToVworld = null;
+ childLocalToVworldIndex = null;
+ transformLevels = null;
+ // only use by TransformStruct.
+ cachedTargets = null;
+ perPathData = null;
+ targetThreads = 0;
+
+
+ if (parentTransformLink != null) {
+ ArrayList obj;
+ if (parentTransformLink
+ instanceof TransformGroupRetained) {
+ obj = ((TransformGroupRetained)
+ parentTransformLink).childTransformLinks;
+ } else {
+ obj = ((SharedGroupRetained)
+ parentTransformLink).childTransformLinks;
+ }
+ synchronized(obj) {
+ obj.remove(this);
+ }
+ }
+ aboveAViewPlatform = false;
+ }
+ else {
+ int i, index, len;
+ // Remove the localToVworld key
+ int newLen = localToVworld.length - s.keys.length;
+
+ Transform3D[][] newChildTList = new Transform3D[newLen][];
+ int[][] newChildIndexList = new int[newLen][];
+ int[] newTransformLevels = new int[newLen];
+ ArrayList[] newChildPTG = new ArrayList[newLen];
+ CachedTargets[] newTargets = new CachedTargets[newLen];
+ TransformGroupData[] newPerPathData = new TransformGroupData[newLen];
+
+ int[] tempIndex = new int[s.keys.length];
+ int curStart =0, newStart =0;
+ boolean found = false;
+ for(i=0;i<s.keys.length;i++) {
+ index = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length);
+
+ tempIndex[i] = index;
+
+ if(index >= 0) {
+ found = true;
+
+ if(index == curStart) {
+ curStart++;
+ }
+ else {
+ len = index - curStart;
+ System.arraycopy(childLocalToVworld, curStart, newChildTList,
+ newStart, len);
+ System.arraycopy(childLocalToVworldIndex, curStart, newChildIndexList,
+ newStart, len);
+ System.arraycopy(transformLevels, curStart, newTransformLevels,
+ newStart, len);
+ System.arraycopy(cachedTargets, curStart, newTargets, newStart, len);
+ System.arraycopy(perPathData, curStart,
+ newPerPathData, newStart, len);
+
+ curStart = index+1;
+ newStart = newStart + len;
+ }
+ }
+ else {
+ found = false;
+ System.out.println("TG.removeNodeData-Can't find matching hashKey.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ }
+
+ if((found == true) && (curStart < localToVworld.length)) {
+ len = localToVworld.length - curStart;
+ System.arraycopy(childLocalToVworld, curStart, newChildTList,
+ newStart, len);
+ System.arraycopy(childLocalToVworldIndex, curStart, newChildIndexList,
+ newStart, len);
+ System.arraycopy(transformLevels, curStart, newTransformLevels,
+ newStart, len);
+ System.arraycopy(cachedTargets, curStart, newTargets, newStart, len);
+ System.arraycopy(perPathData, curStart,
+ newPerPathData, newStart, len);
+ }
+
+ childLocalToVworld = newChildTList;
+ childLocalToVworldIndex = newChildIndexList;
+ transformLevels = newTransformLevels;
+ cachedTargets = newTargets;
+ perPathData = newPerPathData;
+ }
+ super.removeNodeData(s);
+ // Set it back to its parent localToVworld data.
+ // This is b/c the parent has changed it localToVworld data arrays.
+ s.localToVworld = childLocalToVworld;
+ s.localToVworldIndex = childLocalToVworldIndex;
+ }
+ }
+
+ void clearLive(SetLiveState s) {
+
+ Targets[] savedTransformTargets = null;
+
+ savedTransformTargets = s.transformTargets;
+ // no need to gather targets from tg in clear live
+ s.transformTargets = null;
+
+ super.clearLive(s);
+
+ // restore setLiveState from it's local variables.
+ // removeNodeData has altered these variables.
+ s.localToVworld = localToVworld;
+ s.localToVworldIndex = localToVworldIndex;
+ s.transformTargets = savedTransformTargets;
+
+ synchronized (this) { // synchronized with TransformStructure
+ if (inSharedGroup) {
+ if (transformLevels != null) {
+ maxTransformLevel = transformLevels[0];
+ for (int i=1; i<transformLevels.length; i++) {
+ if (transformLevels[i] > maxTransformLevel) {
+ maxTransformLevel = transformLevels[i];
+ }
+ }
+ } else {
+ maxTransformLevel = -1;
+ }
+
+ if (s.switchTargets != null) {
+ for (int i=0; i<s.switchTargets.length; i++) {
+ if (s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(this, Targets.GRP_TARGETS);
+ }
+ }
+ }
+
+ } else {
+ maxTransformLevel = -1;
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.GRP_TARGETS);
+ }
+ }
+ }
+ // TODO: recontruct targetThreads
+ }
+
+
+ void computeCombineBounds(Bounds bounds) {
+ NodeRetained child;
+ BoundingSphere boundingSphere = new BoundingSphere();
+ boundingSphere.setRadius(-1.0);
+
+ if(boundsAutoCompute) {
+ for (int i=children.size()-1; i>=0; i--) {
+ child = (NodeRetained)children.get(i);
+ if(child != null)
+ child.computeCombineBounds(boundingSphere);
+ }
+ }
+ else {
+ // Should this be lock too ? ( MT safe ? )
+ synchronized(localBounds) {
+ boundingSphere.set(localBounds);
+ }
+ }
+
+ // Should this be lock too ? ( MT safe ? )
+ // Thoughts :
+ // Make a temp copy with lock : transform.getWithLock(trans);, but this will cause gc ...
+ synchronized(transform) {
+ boundingSphere.transform(transform);
+ }
+ bounds.combine(boundingSphere);
+
+ }
+
+ void processChildLocalToVworld(ArrayList dirtyTransformGroups,
+ ArrayList keySet,
+ UpdateTargets targets,
+ ArrayList blUsers) {
+
+ synchronized(this) { // sync with setLive/clearLive
+
+ if (inSharedGroup) {
+ if (localToVworldKeys != null) {
+ for(int j=0; j<localToVworldKeys.length; j++) {
+ if (perPathData[j].markedDirty) {
+ updateChildLocalToVworld(localToVworldKeys[j], j,
+ dirtyTransformGroups,
+ keySet, targets,
+ blUsers);
+ } else {
+ //System.out.println("tg.procChild markedDiry skip");
+ }
+ }
+ }
+ } else {
+ if (perPathData != null && perPathData[0].markedDirty) {
+ updateChildLocalToVworld(dirtyTransformGroups, keySet,
+ targets, blUsers);
+ } else {
+ //System.out.println("tg.procChild markedDiry skip");
+ }
+ }
+ }
+ }
+
+ // for shared case
+ void updateChildLocalToVworld(HashKey key, int index,
+ ArrayList dirtyTransformGroups,
+ ArrayList keySet,
+ UpdateTargets targets,
+ ArrayList blUsers) {
+
+ int i, j;
+ Object obj;
+ Transform3D lToVw, childLToVw;
+ TransformGroupRetained tg;
+ LinkRetained ln;
+ CachedTargets ct;
+
+ synchronized(this) { // sync with setLive/clearLive
+
+ if (localToVworld != null) {
+ perPathData[index].markedDirty = false;
+ // update immediate child's localToVworld
+
+ if (perPathData[index].switchState.currentSwitchOn ) {
+ lToVw = getCurrentLocalToVworld(index);
+ childLToVw = getUpdateChildLocalToVworld(index);
+ childLToVw.mul(lToVw, currentTransform);
+ dirtyTransformGroups.add(this);
+ keySet.add(key);
+ ct = j3dCTs[index];
+ if (ct != null) {
+ targets.addCachedTargets(ct);
+ if (ct.targetArr[Targets.BLN_TARGETS] != null) {
+ gatherBlUsers(blUsers,
+ ct.targetArr[Targets.BLN_TARGETS]);
+ }
+ }
+ } else {
+ perPathData[index].switchDirty = true;
+ //System.out.println("tg.updateChild skip");
+ }
+
+
+
+ // update child's localToVworld of its children
+ // transformLink may contain link nodes
+ synchronized(childTransformLinks) {
+ for (i=0; i<childTransformLinks.size(); i++) {
+ obj = childTransformLinks.get(i);
+
+ if (obj instanceof TransformGroupRetained) {
+ tg = (TransformGroupRetained)obj;
+ tg.updateChildLocalToVworld(
+ tg.localToVworldKeys[index],
+ index, dirtyTransformGroups, keySet,
+ targets, blUsers);
+ } else { // LinkRetained
+ ln = (LinkRetained)obj;
+ currentKey.set(localToVworldKeys[index]);
+ currentKey.append(LinkRetained.plus).append(ln.nodeId);
+ if ((ln.sharedGroup != null) &&
+ (ln.sharedGroup.localToVworldKeys != null)) {
+ j = currentKey.equals(ln.sharedGroup.localToVworldKeys,0,
+ ln.sharedGroup.localToVworldKeys.length);
+ if(j < 0) {
+ System.out.
+ println("TransformGroupRetained : Can't find hashKey");
+ }
+
+ if (j < ln.sharedGroup.localToVworldKeys.length) {
+ ln.sharedGroup.
+ updateChildLocalToVworld(ln.sharedGroup.
+ localToVworldKeys[j], j,
+ dirtyTransformGroups, keySet,
+ targets, blUsers);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // for non-shared case
+ void updateChildLocalToVworld(ArrayList dirtyTransformGroups,
+ ArrayList keySet,
+ UpdateTargets targets,
+ ArrayList blUsers) {
+ int i, j;
+ Object obj;
+ Transform3D lToVw, childLToVw;
+ TransformGroupRetained tg;
+ LinkRetained ln;
+ CachedTargets ct;
+
+ synchronized(this) { // sync with setLive/clearLive
+
+ if (localToVworld != null) {
+ perPathData[0].markedDirty = false;
+ // update immediate child's localToVworld
+
+ if (perPathData[0].switchState.currentSwitchOn ) {
+ lToVw = getCurrentLocalToVworld(0);
+ childLToVw = getUpdateChildLocalToVworld(0);
+ childLToVw.mul(lToVw, currentTransform);
+ dirtyTransformGroups.add(this);
+ ct = j3dCTs[0];
+ if (ct != null) {
+ targets.addCachedTargets(ct);
+ if (ct.targetArr[Targets.BLN_TARGETS] != null) {
+ gatherBlUsers(blUsers,
+ ct.targetArr[Targets.BLN_TARGETS]);
+ }
+ }
+ } else {
+ perPathData[0].switchDirty = true;
+ //System.out.println("tg.updateChild skip");
+ }
+
+
+ // update child's localToVworld of its children
+ // transformLink contains top level transform group nodes
+ // and link nodes
+ synchronized(childTransformLinks) {
+ for (i=0; i<childTransformLinks.size(); i++) {
+ obj = childTransformLinks.get(i);
+
+ if (obj instanceof TransformGroupRetained) {
+ tg = (TransformGroupRetained)obj;
+ tg.updateChildLocalToVworld(dirtyTransformGroups,
+ keySet, targets, blUsers);
+
+ } else { // LinkRetained
+ ln = (LinkRetained)obj;
+ currentKey.reset();
+ currentKey.append(locale.nodeId);
+ currentKey.append(LinkRetained.plus).append(ln.nodeId);
+ if ((ln.sharedGroup != null) &&
+ (ln.sharedGroup.localToVworldKeys != null)) {
+ j = currentKey.equals(ln.sharedGroup.localToVworldKeys,0,
+ ln.sharedGroup.localToVworldKeys.length);
+ if(j < 0) {
+ System.out.
+ println("TransformGroupRetained : Can't find hashKey");
+ }
+
+ if (j<ln.sharedGroup.localToVworldKeys.length) {
+ ln.sharedGroup.
+ updateChildLocalToVworld(
+ ln.sharedGroup.
+ localToVworldKeys[j],
+ j, dirtyTransformGroups,
+ keySet, targets, blUsers);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Transform the input bound by the current LocalToVWorld, this
+ * one overwrite the one defined in NodeRetained since for
+ * TransformGroup, it has to use currentChildLocalToVworld
+ * instead of currentLocalToVworld
+ */
+ void transformBounds(SceneGraphPath path, Bounds bound) {
+ if (!((NodeRetained) path.item.retained).inSharedGroup) {
+ bound.transform(getCurrentChildLocalToVworld());
+ } else {
+ HashKey key = new HashKey("");
+ path.getHashKey(key);
+ bound.transform(getCurrentChildLocalToVworld(key));
+ }
+ }
+
+
+ /**
+ * get the to be updated child localToVworld
+ */
+ Transform3D getUpdateChildLocalToVworld(int index) {
+ int currentIndex = childLocalToVworldIndex[index][NodeRetained.CURRENT_LOCAL_TO_VWORLD];
+
+ if (currentIndex == childLocalToVworldIndex[index][NodeRetained.LAST_LOCAL_TO_VWORLD]) {
+ currentIndex = currentIndex ^ 1;
+ childLocalToVworldIndex[index][NodeRetained.CURRENT_LOCAL_TO_VWORLD] = currentIndex;
+ }
+ return childLocalToVworld[index][currentIndex];
+ }
+
+
+ /**
+ * Get the current child localToVworld transform for a node
+ */
+ Transform3D getCurrentChildLocalToVworld() {
+ return getCurrentChildLocalToVworld(0);
+ }
+
+ Transform3D getCurrentChildLocalToVworld(int index) {
+ return childLocalToVworld[index][childLocalToVworldIndex[index][NodeRetained.CURRENT_LOCAL_TO_VWORLD]];
+ }
+
+ Transform3D getCurrentChildLocalToVworld(HashKey key) {
+ if (!inSharedGroup) {
+ return childLocalToVworld[0][childLocalToVworldIndex[0][NodeRetained.CURRENT_LOCAL_TO_VWORLD]];
+ } else {
+ int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ if(i>= 0) {
+ return childLocalToVworld[i]
+ [childLocalToVworldIndex[i][NodeRetained.CURRENT_LOCAL_TO_VWORLD]];
+ }
+ }
+ return new Transform3D();
+ }
+
+
+ /**
+ * Get the last child localToVworld transform for a node
+ */
+ Transform3D getLastChildLocalToVworld(HashKey key) {
+
+ if (!inSharedGroup) {
+ return childLocalToVworld[0][childLocalToVworldIndex[0][NodeRetained.LAST_LOCAL_TO_VWORLD]];
+ } else {
+ int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ if(i>= 0) {
+ return childLocalToVworld[i]
+ [childLocalToVworldIndex[i][NodeRetained.LAST_LOCAL_TO_VWORLD]];
+ }
+ }
+ return new Transform3D();
+ }
+
+ // ****************************
+ // TargetsInterface methods
+ // ****************************
+
+ public int getTargetThreads(int type) {
+ // type is ignored here, only need for SharedGroup
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ return targetThreads;
+ } else {
+ System.out.println("getTargetsThreads: wrong arguments");
+ return -1;
+ }
+ }
+
+ public CachedTargets getCachedTargets(int type, int index, int child) {
+ // type is ignored here, only need for SharedGroup
+ // child is ignored here
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ return cachedTargets[index];
+ } else {
+ System.out.println("getCachedTargets: wrong arguments");
+ return null;
+ }
+ }
+
+ TargetsInterface getClosestTargetsInterface(int type) {
+ return (type == TargetsInterface.TRANSFORM_TARGETS)?
+ (TargetsInterface)this:
+ (TargetsInterface)parentSwitchLink;
+ }
+
+ // re-evalute localTargetThreads using newCachedTargets and
+ // re-evaluate targetThreads
+ public void computeTargetThreads(int type,
+ CachedTargets[] newCachedTargets) {
+
+ // type is ignored here, only need for SharedGroup
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ localTargetThreads = J3dThread.UPDATE_TRANSFORM;
+
+ for(int i=0; i<newCachedTargets.length; i++) {
+ if (newCachedTargets[i] != null) {
+ localTargetThreads |= newCachedTargets[i].computeTargetThreads();
+ }
+ }
+ targetThreads = localTargetThreads;
+
+ int numLinks = childTransformLinks.size();
+ TargetsInterface childLink;
+ NodeRetained node;
+ for(int i=0; i<numLinks; i++) {
+
+ node = (NodeRetained)childTransformLinks.get(i);
+ if (node.nodeType == NodeRetained.LINK) {
+ childLink = (TargetsInterface)
+ ((LinkRetained)node).sharedGroup;
+ } else {
+ childLink = (TargetsInterface) node;
+ }
+ if (childLink != null) {
+ targetThreads |=
+ childLink.getTargetThreads(TargetsInterface.TRANSFORM_TARGETS);
+ }
+ }
+ } else {
+ System.out.println("computeTargetsThreads: wrong arguments");
+ }
+
+ }
+
+ // re-compute localTargetThread, targetThreads and
+ // propagate changes to ancestors
+ public void updateTargetThreads(int type,
+ CachedTargets[] newCachedTargets) {
+ // type is ignored here, only need for SharedGroup
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ computeTargetThreads(type, newCachedTargets);
+ if (parentTransformLink != null) {
+ TargetsInterface pti = (TargetsInterface)parentTransformLink;
+ pti.propagateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ targetThreads);
+ }
+ } else {
+ System.out.println("updateTargetThreads: wrong arguments");
+ }
+ }
+
+ // re-evaluate targetThreads using childTargetThreads and
+ // propagate changes to ancestors
+ public void propagateTargetThreads(int type, int childTargetThreads) {
+ // type is ignored here, only need for SharedGroup
+
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ // TODO : For now we'll OR more than exact.
+ //targetThreads = localTargetThreads | childTargetThreads;
+ targetThreads = targetThreads | childTargetThreads;
+ if (parentTransformLink != null) {
+ TargetsInterface pti = (TargetsInterface)parentTransformLink;
+ pti.propagateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ targetThreads);
+ }
+ } else {
+ System.out.println("propagateTargetThreads: wrong arguments");
+ }
+ }
+
+ public void updateCachedTargets(int type, CachedTargets[] newCt) {
+ // type is ignored here, only need for SharedGroup
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ j3dCTs = newCt;
+ } else {
+ System.out.println("updateCachedTargets: wrong arguments");
+ }
+ }
+
+ public void copyCachedTargets(int type, CachedTargets[] newCt) {
+ // type is ignored here, only need for SharedGroup
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ int size = cachedTargets.length;
+ for (int i=0; i<size; i++) {
+ newCt[i] = cachedTargets[i];
+ }
+ } else {
+ System.out.println("copyCachedTargets: wrong arguments");
+ }
+ }
+
+ public void resetCachedTargets(int type,
+ CachedTargets[] newCtArr, int child) {
+ // type is ignored here, only need for SharedGroup
+ // child is ignored here
+ if (type == TargetsInterface.TRANSFORM_TARGETS) {
+ cachedTargets = newCtArr;
+ } else {
+ System.out.println("resetCachedTargets: wrong arguments");
+ }
+ }
+
+ public ArrayList getTargetsData(int type, int index) {
+ // not used
+ return null;
+ }
+
+ void childCheckSetLive(NodeRetained child, int childIndex,
+ SetLiveState s, NodeRetained linkNode) {
+ s.currentTransforms = childLocalToVworld;
+ s.currentTransformsIndex = childLocalToVworldIndex;
+ s.parentTransformLink = this;
+ s.childTransformLinks = childTransformLinks;
+ s.localToVworld = s.currentTransforms;
+ s.localToVworldIndex = s.currentTransformsIndex;
+
+ child.setLive(s);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TransformInterpolator.java b/src/classes/share/javax/media/j3d/TransformInterpolator.java
new file mode 100644
index 0000000..93a8f93
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransformInterpolator.java
@@ -0,0 +1,234 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+/**
+ * TransformInterpolator is an abstract class that extends
+ * Interpolator to provide common methods used by various transform
+ * related interpolator subclasses. These include methods to set/get
+ * the target of TransformGroup, and set/get transform of axis.
+ *
+ * @since Java 3D 1.3
+ */
+
+public abstract class TransformInterpolator extends Interpolator {
+ /**
+ * The TransformGroup node affected by this transformInterpolator
+ */
+ protected TransformGroup target = null;
+
+ /**
+ * The transform that defines the local coordinate
+ */
+ protected Transform3D axis = new Transform3D();
+
+ /**
+ * The inverse transform that defines the local coordinate
+ */
+ protected Transform3D axisInverse = new Transform3D();
+
+ /**
+ * The transform which is passed into computeTransform() when computeTransform()
+ * is called implicitly from processStimulus()
+ */
+ private Transform3D currentTransform = new Transform3D();
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+
+ /**
+ * Constructs a TransformInterpolator node with a null alpha value and
+ * a null target of TransformGroup
+ */
+ public TransformInterpolator() {
+ }
+
+ /**
+ * Constructs a trivial transform interpolator with a specified alpha,
+ * a specified target and an default axis set to Identity.
+ * @param alpha The alpha object for this transform Interpolator
+ * @param target The target TransformGroup for this TransformInterpolator
+ */
+ public TransformInterpolator(Alpha alpha, TransformGroup target) {
+ super(alpha);
+ this.target = target;
+ axis.setIdentity();
+ axisInverse.setIdentity();
+ }
+ /**
+ * Constructs a new transform interpolator that set an specified alpha,
+ * a specified targe and a specified axisOfTransform.
+ * @param alpha the alpha object for this interpolator
+ * @param target the transformGroup node affected by this transformInterpolator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * system in which this interpolator operates.
+ */
+ public TransformInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform){
+
+ super(alpha);
+ this.target = target;
+ axis.set(axisOfTransform);
+ axisInverse.invert(axis);
+ }
+
+ /**
+ * This method sets the target TransformGroup node for this
+ * interpolator.
+ * @param target The target TransformGroup
+ */
+ public void setTarget(TransformGroup target) {
+ this.target = target;
+ }
+
+ /**
+ * This method retrieves this interpolator's TransformGroup
+ * node reference.
+ * @return the Interpolator's target TransformGroup
+ */
+ public TransformGroup getTarget() {
+ return target;
+ }
+
+ /**
+ * This method sets the axis of transform for this interpolator.
+ * @param axisOfTransform the transform that defines the local coordinate
+ * system in which this interpolator operates
+ */
+ public void setTransformAxis(Transform3D axisOfTransform) {
+ this.axis.set(axisOfTransform);
+ this.axisInverse.invert(this.axis);
+ }
+
+ /**
+ * This method retrieves this interpolator's axis of transform.
+ * @return the interpolator's axis of transform
+ */
+ public Transform3D getTransformAxis() {
+ return new Transform3D(this.axis);
+ }
+
+ /**
+ * 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
+ */
+ public abstract void computeTransform(float alphaValue,
+ Transform3D transform);
+
+ /**
+ * This method is invoked by the behavior scheduler every frame.
+ * First it gets the alpha value that corresponds to the current time.
+ * Then it calls computeTransform() method to computes the transform based on this
+ * alpha vaule, and updates the specified TransformGroup node with this new transform.
+ * @param criteria an enumeration of the criteria that caused the
+ * stimulus
+ */
+ public void processStimulus(Enumeration criteria) {
+ // Handle stimulus
+ WakeupCriterion criterion = passiveWakeupCriterion;
+
+ if (alpha != null) {
+ float value = alpha.value();
+ if (value != prevAlphaValue) {
+ computeTransform(value, currentTransform);
+ target.setTransform(currentTransform);
+ prevAlphaValue = value;
+ }
+ if (!alpha.finished() && !alpha.isPaused()) {
+ criterion = defaultWakeupCriterion;
+ }
+ }
+ wakeupOn(criterion);
+ }
+
+ /**
+ * Copies all TransformInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ TransformInterpolator ti = (TransformInterpolator) originalNode;
+
+ setTransformAxis(ti.getTransformAxis());
+
+ // this reference will be updated in updateNodeReferences()
+ setTarget(ti.getTarget());
+ }
+
+ /**
+ * Callback used to allow a node to check if any scene graph objects
+ * referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any object references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding object in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * object is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ // check TransformGroup
+ Node n = getTarget();
+
+ if (n != null) {
+ setTarget((TransformGroup) referenceTable.getNewObjectReference(n));
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TransformStructure.java b/src/classes/share/javax/media/j3d/TransformStructure.java
new file mode 100644
index 0000000..6cd60ca
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransformStructure.java
@@ -0,0 +1,724 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * A transform update is a object that manages TransformGroups
+ */
+
+class TransformStructure extends J3dStructure implements ObjectUpdate {
+ /**
+ * An ArrayList of TransformGroups to traverse
+ */
+ UnorderList traverseList = new UnorderList(TransformGroupRetained.class);
+
+ /**
+ * A Parallel ArrayList of Transforms for the traverse list
+ */
+ UnorderList transformList = new UnorderList(Transform3D.class);
+
+ ArrayList objectList = new ArrayList();
+
+ /**
+ * arraylist of the bounding leaf users affected by the transform
+ */
+ ArrayList blUsers = new ArrayList();
+
+ // to gather transform targets
+ UpdateTargets targets = new UpdateTargets();
+
+ /**
+ * An arrayList of nodes that need collisionBounds updates
+ */
+ ArrayList collisionObjectList = new ArrayList();
+
+ // The object that contains the dynamic HashKey - a string type object
+ HashKey key = new HashKey(250);
+
+ // List of dirty TransformGroups
+ ArrayList dirtyTransformGroups = new ArrayList();
+
+ // Associated Keys with the dirtyNodeGroup
+ ArrayList keySet = new ArrayList();
+
+ // current locale under traversal
+ Locale locale = null;
+
+ // The transform used in intermediate calc
+ Transform3D currentTrans = new Transform3D();
+
+ TransformGroupRetained tgs[];
+ Transform3D t3ds[];
+
+ // the active list contains changed TransformGroup minus those that
+ // have been switched-off, plus those that have been changed but
+ // just switched-on
+ UnorderList activeTraverseList =
+ new UnorderList(TransformGroupRetained.class);
+
+ // contains TG that have been previously changed but just switched-on
+ ArrayList switchDirtyTgList = new ArrayList(1);
+
+ boolean lazyUpdate = false;
+
+ // ArrayList of switches that have changed, use for lastSwitchOn updates
+ ArrayList switchChangedList = new ArrayList();
+
+ // true if already in MasterControl's update object list
+ boolean inUpdateObjectList = false;
+
+ /**
+ * This constructor does nothing
+ */
+ TransformStructure(VirtualUniverse u) {
+ super(u, J3dThread.UPDATE_TRANSFORM);
+ }
+
+ void processMessages(long referenceTime) {
+ J3dMessage[] messages = getMessages(referenceTime);
+ int nMsg = getNumMessage();
+ J3dMessage m;
+ int i, index;
+
+ if (nMsg <= 0) {
+ return;
+ }
+
+ targets.clearNodes();
+ objectList.clear();
+ blUsers.clear();
+ inUpdateObjectList = false;
+
+ synchronized (universe.sceneGraphLock) {
+ // first compact the TRANSFORM_CHANGED messages by going
+ // backwards through the messages
+ for (i = (nMsg-1); i >= 0; i--) {
+ m = messages[i];
+ if (m.type == J3dMessage.TRANSFORM_CHANGED) {
+ index = traverseList.indexOf(m.args[1]);
+ // if this transform group isn't in our list, add
+ // the information
+ if (index == -1) {
+ traverseList.add(m.args[1]);
+ transformList.add(m.args[2]);
+ }
+ }
+ }
+
+ for (i=0; i<nMsg; i++) {
+ m = messages[i];
+
+ switch (m.type) {
+ case J3dMessage.INSERT_NODES:
+ objectList.add(m.args[0]);
+ if (m.args[1] != null) {
+ TargetsInterface ti = (TargetsInterface)m.args[1];
+ ti.updateCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS,
+ (CachedTargets[])m.args[2]);
+ }
+ break;
+ case J3dMessage.REMOVE_NODES:
+ removeNodes(m);
+ break;
+ case J3dMessage.SWITCH_CHANGED:
+ processSwitchChanged(m);
+ break;
+ case J3dMessage.SHAPE3D_CHANGED:
+ objectList.add(m.args[3]);
+ if (m.args[4] != null) {
+ TargetsInterface ti = (TargetsInterface)m.args[4];
+ ti.updateCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS,
+ (CachedTargets[])m.args[5]);
+ }
+ break;
+ case J3dMessage.GEOMETRY_CHANGED:
+ objectList.add(m.args[0]);
+ break;
+ case J3dMessage.MORPH_CHANGED:
+ objectList.add(m.args[3]);
+ break;
+ case J3dMessage.TEXT3D_DATA_CHANGED:
+ objectList.add(m.args[1]);
+ Object tiArr[] = (Object[])m.args[2];
+ if (tiArr != null) {
+ Object newCtArr[] = (Object[])m.args[3];
+ for (int j=0; j<tiArr.length;j++) {
+ TargetsInterface ti =
+ (TargetsInterface)tiArr[j];
+ ti.updateCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS,
+ (CachedTargets[])newCtArr[j]);
+ }
+ }
+ break;
+ case J3dMessage.TEXT3D_TRANSFORM_CHANGED:
+ objectList.add(m.args[0]);
+ break;
+ case J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED:
+ processBoundsAutoComputeChanged(m);
+ break;
+ case J3dMessage.REGION_BOUND_CHANGED:
+ processRegionBoundChanged(m);
+ break;
+ case J3dMessage.COLLISION_BOUND_CHANGED:
+ processCollisionBoundChanged(m);
+ break;
+ }
+ m.decRefcount();
+ }
+ processCurrentLocalToVworld();
+
+ // TODO: temporary -- processVwcBounds will be
+ // done in GeometryStructure
+ if (objectList.size() > 0) {
+ processGeometryAtomVwcBounds();
+ }
+ processVwcBounds();
+ }
+
+ Arrays.fill(messages, 0, nMsg, null);
+ }
+
+ void processCurrentLocalToVworld() {
+ int i, j, tSize, sSize;
+ TransformGroupRetained tg;
+ BranchGroupRetained bgr;
+ Transform3D t;
+ TransformGroupData data;
+
+ lazyUpdate = false;
+
+ tSize = transformList.size();
+ sSize = switchDirtyTgList.size();
+ if (tSize <= 0 && sSize <= 0) {
+ return;
+ }
+
+ // process TG with setTransform changes
+ // update Transform3D, switchDirty and lToVwDrity flags
+ if (tSize > 0) {
+ tgs = (TransformGroupRetained[])traverseList.toArray(false);
+ t3ds = (Transform3D[])transformList.toArray(false);
+ for (i=0; i<tSize; i++) {
+ tg = tgs[i];
+ tg.currentTransform.set(t3ds[i]);
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ t3ds[i]);
+
+ synchronized(tg) { // synchronized with tg.set/clearLive
+ if(tg.perPathData != null) {
+ if (! tg.inSharedGroup) {
+ data = tg.perPathData[0];
+ if (! data.switchState.inSwitch) {
+ // always add to activetraverseList if not in switch
+ activeTraverseList.add(tg);
+ data.markedDirty = true;
+ data.switchDirty = false;
+ } else {
+ // if in switch, add to activetraverseList only if it is
+ // currently switched on, otherwise, mark it as
+ // switchDirty
+ if (data.switchState.currentSwitchOn) {
+ activeTraverseList.add(tg);
+ data.switchDirty = false;
+ data.markedDirty = true;
+ } else {
+ data.switchDirty = true;
+ data.markedDirty = false;
+ }
+ }
+ } else {
+ int npaths = tg.perPathData.length;
+ boolean added = false;
+
+ for (int k=0; k<npaths; k++) {
+ data = tg.perPathData[k];
+ if (!data.switchState.inSwitch) {
+ if (!added) {
+ // add to activetraverseList if not in switch
+ added = true;
+ activeTraverseList.add(tg);
+ }
+ data.markedDirty = true;
+ data.switchDirty = false;
+ } else {
+ // if in switch, add to activetraverseList only if
+ // it is currently switched on, otherwise,
+ // mark it as switchDirty
+ if (data.switchState.currentSwitchOn) {
+ if (!added) {
+ added = true;
+ activeTraverseList.add(tg);
+ }
+ data.switchDirty = false;
+ data.markedDirty = true;
+ } else {
+ data.switchDirty = true;
+ data.markedDirty = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // merge switchDirty into activeTraverseList
+ if (sSize > 0) {
+ int size = switchDirtyTgList.size();
+ for (i=0; i<size; i++) {
+ // Note: UnorderList does not implement addAll
+ activeTraverseList.add(switchDirtyTgList.get(i));
+ }
+ switchDirtyTgList.clear();
+ lazyUpdate = true;
+ }
+
+ // activeTraverseList contains switched-on tg as well
+ tgs = (TransformGroupRetained[])activeTraverseList.toArray(false);
+ tSize = activeTraverseList.size();
+
+ // process active TGs
+ if (tSize > 0) {
+
+ sortTransformGroups(tSize);
+
+ // update lToVw and gather targets
+ for (i=0; i<tSize; i++) {
+ tgs[i].processChildLocalToVworld(dirtyTransformGroups, keySet,
+ targets, blUsers);
+ }
+ if (!inUpdateObjectList) {
+ VirtualUniverse.mc.addMirrorObject(this);
+ inUpdateObjectList = true;
+ }
+ }
+
+ transformList.clear();
+ traverseList.clear();
+ activeTraverseList.clear();
+ }
+
+
+ private void sortTransformGroups(int size) {
+ if (size < 7) {
+ insertSort(size);
+ } else {
+ quicksort(0, size-1);
+ }
+ }
+
+ // Insertion sort on smallest arrays
+ private void insertSort(int size) {
+ for (int i=0; i<size; i++) {
+ for (int j=i; j>0 &&
+ (tgs[j-1].maxTransformLevel > tgs[j].maxTransformLevel); j--) {
+ TransformGroupRetained tmptg = tgs[j];
+ tgs[j] = tgs[j-1];
+ tgs[j-1] = tmptg;
+ }
+ }
+ }
+
+ private void quicksort( int l, int r ) {
+ int i = l;
+ int j = r;
+ double k = tgs[(l+r) / 2].maxTransformLevel;
+ do {
+ while (tgs[i].maxTransformLevel<k) i++;
+ while (k<tgs[j].maxTransformLevel) j--;
+ if (i<=j) {
+ TransformGroupRetained tmptg = tgs[i];
+ tgs[i] = tgs[j];
+ tgs[j] = tmptg;
+
+ i++;
+ j--;
+ }
+ } while (i<=j);
+
+ if (l<j) quicksort(l,j);
+ if (l<r) quicksort(i,r);
+ }
+
+
+ public void updateObject() {
+ processLastLocalToVworld();
+ processLastSwitchOn();
+ }
+
+
+ void processLastSwitchOn() {
+ int size = switchChangedList.size();
+ if (size > 0) {
+ SwitchState switchState;
+
+ for (int i = 0; i < size; i++) {
+ switchState = (SwitchState)switchChangedList.get(i);
+ switchState.updateLastSwitchOn();
+ }
+ switchChangedList.clear();
+ }
+ }
+
+
+ void processLastLocalToVworld() {
+ int i, j, k;
+ TransformGroupRetained tg;
+ HashKey key;
+
+
+ int dTGSize = dirtyTransformGroups.size();
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ J3dDebug.doDebug(J3dDebug.transformStructure, J3dDebug.LEVEL_5,
+ "processLastLocalToVworld(): dTGSize= " + dTGSize + "\n");
+ }
+
+ for (i=0, k=0; i < dTGSize; i++) {
+ tg = (TransformGroupRetained)dirtyTransformGroups.get(i);
+ // Check if the transformGroup is still alive
+
+ // TODO: This is a hack, should be fixed after EA
+ // Null pointer checking should be removed!
+ // should call trans = tg.getCurrentChildLocalToVworld(key);
+ synchronized(tg) {
+ if (tg.childLocalToVworld != null) {
+ if (tg.inSharedGroup) {
+ key = (HashKey) keySet.get(k++);
+ for (j=0; j<tg.localToVworldKeys.length; j++) {
+ if (tg.localToVworldKeys[j].equals(key)) {
+ break;
+ }
+ }
+ if (j < tg.localToVworldKeys.length) {
+ // last index = current index
+ tg.childLocalToVworldIndex[j][NodeRetained.LAST_LOCAL_TO_VWORLD] =
+ tg.childLocalToVworldIndex[j][NodeRetained.CURRENT_LOCAL_TO_VWORLD];
+ }
+ }
+ else {
+ // last index = current index
+ tg.childLocalToVworldIndex[0][NodeRetained.LAST_LOCAL_TO_VWORLD] =
+ tg.childLocalToVworldIndex[0][NodeRetained.CURRENT_LOCAL_TO_VWORLD];
+ }
+ }
+
+ }
+
+ }
+ dirtyTransformGroups.clear();
+ keySet.clear();
+
+ }
+
+ void processGeometryAtomVwcBounds() {
+
+
+ Shape3DRetained ms;
+ GeometryAtom ga;
+
+ //int num_locales = universe.listOfLocales.size();
+ int oSize = objectList.size();
+ for (int i = 0; i < oSize; i++) {
+ Object[] nodes = (Object[]) objectList.get(i);
+ if (J3dDebug.devPhase && J3dDebug.debug) {
+ J3dDebug.doDebug(J3dDebug.transformStructure, J3dDebug.LEVEL_5,
+ "vwcBounds computed this frame = " + nodes.length + "\n");
+ }
+ for (int j = 0; j < nodes.length; j++) {
+ // If the list has geometry atoms, update the vwc bounds
+ synchronized(nodes[j]) {
+ if (nodes[j] instanceof GeometryAtom) {
+ ga = (GeometryAtom) nodes[j];
+ ms = ga.source;
+
+ // update mirrorShape's vwcBounds if in use
+ // shape with multiple geometries only needed to be
+ // updated once
+
+ synchronized(ms.bounds) {
+ ms.vwcBounds.transform(ms.bounds,
+ ms.getCurrentLocalToVworld(0));
+ }
+ if (ms.collisionBound != null) {
+ ms.collisionVwcBound.transform(
+ ms.collisionBound,
+ ms.getCurrentLocalToVworld(0));
+ }
+ ga.centroidIsDirty = true;
+ } else if (nodes[j] instanceof GroupRetained) {
+ // Update collisionVwcBounds of mirror GroupRetained
+ GroupRetained g = (GroupRetained) nodes[j];
+ Bounds bound = (g.sourceNode.collisionBound != null ?
+ g.sourceNode.collisionBound :
+ g.sourceNode.getEffectiveBounds());
+ g.collisionVwcBounds.transform(bound,
+ g.getCurrentLocalToVworld());
+ }
+ }
+ }
+ }
+ // process collision bounds only update
+ for (int i = 0; i < collisionObjectList.size(); i++) {
+ Object[] nodes = (Object[]) collisionObjectList.get(i);
+ for (int j = 0; j < nodes.length; j++) {
+ synchronized(nodes[j]) {
+ if (nodes[j] instanceof GeometryAtom) {
+ ga = (GeometryAtom) nodes[j];
+ ms = ga.source;
+
+ if (ms.collisionVwcBound != null) {
+ ms.collisionVwcBound.transform(
+ ms.collisionBound,
+ ms.getCurrentLocalToVworld(0));
+ }
+ }
+ }
+ }
+ }
+ collisionObjectList.clear();
+ }
+
+ void processVwcBounds() {
+
+
+ int size;
+ int i,j;
+ GeometryAtom ga;
+ Shape3DRetained ms;
+ Object nodes[], nodesArr[];
+
+ UnorderList arrList = targets.targetList[Targets.GEO_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (i = 0; i<size; i++) {
+ nodes = (Object[])nodesArr[i];
+ for (j = 0; j < nodes.length; j++) {
+ synchronized(nodes[j]) {
+ ga = (GeometryAtom) nodes[j];
+ ms = ga.source;
+ synchronized(ms.bounds) {
+ ms.vwcBounds.transform(ms.bounds,
+ ms.getCurrentLocalToVworld(0));
+ }
+ if (ms.collisionBound != null) {
+ ms.collisionVwcBound.transform(
+ ms.collisionBound,
+ ms.getCurrentLocalToVworld(0));
+ }
+ ga.centroidIsDirty = true;
+ }
+ }
+ }
+ }
+
+ arrList = targets.targetList[Targets.GRP_TARGETS];
+ if (arrList != null) {
+ size = arrList.size();
+ nodesArr = arrList.toArray(false);
+
+ for (i = 0; i<size; i++) {
+ nodes = (Object[])nodesArr[i];
+ for (j = 0; j < nodes.length; j++) {
+ // Update collisionVwcBounds of mirror GroupRetained
+ GroupRetained g = (GroupRetained)nodes[j];
+ Bounds bound = (g.sourceNode.collisionBound != null ?
+ g.sourceNode.collisionBound :
+ g.sourceNode.getEffectiveBounds());
+ g.collisionVwcBounds.transform(bound,
+ g.getCurrentLocalToVworld());
+ }
+ }
+ }
+
+ // process collision bounds only update
+ for (i = 0; i < collisionObjectList.size(); i++) {
+ nodes = (Object[]) collisionObjectList.get(i);
+ for (j = 0; j < nodes.length; j++) {
+ synchronized(nodes[j]) {
+ if (nodes[j] instanceof GeometryAtom) {
+ ga = (GeometryAtom) nodes[j];
+ ms = ga.source;
+
+ if (ms.collisionVwcBound != null) {
+ ms.collisionVwcBound.transform(
+ ms.collisionBound,
+ ms.getCurrentLocalToVworld(0));
+ }
+ }
+ }
+ }
+ }
+ collisionObjectList.clear();
+ }
+
+ void processRegionBoundChanged(J3dMessage m) {
+ // need to update mirrorShape's bounds
+ processBoundsChanged((Object[]) m.args[0], (Bounds)m.args[1]);
+ }
+
+ void processBoundsChanged(Object[] gaArray, Bounds updateBounds) {
+ int i;
+ GeometryAtom ga;
+ Shape3DRetained ms;
+
+ for (i=0; i<gaArray.length; i++) {
+ ga = (GeometryAtom)gaArray[i];
+ ms = ga.source;
+
+ // update mirrorShape's bound objects
+ // since boundsAutoCompute is false and user specified a bound
+ ms.bounds = updateBounds;
+ if (ms.collisionBound == null) {
+ ms.collisionVwcBound = ms.vwcBounds;
+ }
+ }
+ objectList.add(gaArray);
+ }
+
+
+ void processCollisionBoundChanged(J3dMessage m) {
+ int i;
+ Shape3DRetained ms;
+ Bounds collisionBound = (Bounds)m.args[1];
+
+ if (m.args[0] instanceof GroupRetained) {
+ GroupRetained g = (GroupRetained) m.args[0];
+ if (g.mirrorGroup != null) {
+ objectList.add(g.mirrorGroup);
+ }
+ } else {
+ Object[] gaArray = (Object[]) m.args[0];
+ GeometryAtom ga;
+
+ for (i=0; i<gaArray.length; i++) {
+ ga = (GeometryAtom)gaArray[i];
+ ms = ga.source;
+
+ ms.collisionBound = collisionBound;
+
+ if (ms.collisionBound != null) {
+ // may be previously points to ms.vwcBounds, therefore
+ // needs to create one
+ ms.collisionVwcBound = (Bounds)ms.collisionBound.clone();
+ } else {
+ ms.collisionVwcBound = ms.vwcBounds;
+ }
+ }
+ collisionObjectList.add(gaArray);
+ }
+ }
+
+ void processBoundsAutoComputeChanged(J3dMessage m) {
+ // need to update mirrorShape's bounds
+ processBoundsChanged((Object[]) m.args[0], (Bounds) m.args[1]);
+ }
+
+ void processSwitchChanged(J3dMessage m) {
+ ArrayList switchList = (ArrayList)m.args[2];
+
+
+ int size = switchList.size();
+ if (size > 0) {
+ // update SwitchState's CurrentSwitchOn flag
+ SwitchState switchState;
+ for (int j=0; j<size; j++) {
+ switchState = (SwitchState)switchList.get(j);
+ switchState.updateCurrentSwitchOn();
+ }
+
+ // process switch dirty TranformGroups
+ UpdateTargets targets = (UpdateTargets)m.args[0];
+ UnorderList arrList = targets.targetList[Targets.GRP_TARGETS];
+
+ if (arrList != null) {
+
+ Object[] nodes;
+ Object[] nodesArr = arrList.toArray(false);
+ int aSize = arrList.size();
+ int nPaths;
+ boolean added;
+
+ TransformGroupRetained tg;
+ TransformGroupData data;
+
+ for (int j=0; j<aSize; j++) {
+ nodes = (Object[])nodesArr[j];
+
+ for (int i=0; i<nodes.length; i++) {
+ added = false;
+ tg = (TransformGroupRetained)nodes[i];
+
+ synchronized(tg) { // synchronized with tg.set/clearLive
+ if (tg.perPathData != null) {
+ nPaths = tg.perPathData.length;
+
+ for (int k=0; k<nPaths; k++) {
+ data = tg.perPathData[k];
+ if (data.switchState.currentSwitchOn &&
+ data.switchDirty) {
+ if (!added) {
+ // only needed to add once
+ switchDirtyTgList.add(tg);
+ added = true;
+ }
+ data.switchDirty = false;
+ data.markedDirty = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // gather a list of SwitchState for lastSwitchOn update
+ switchChangedList.addAll(switchList);
+
+ if (!inUpdateObjectList) {
+ VirtualUniverse.mc.addMirrorObject(this);
+ inUpdateObjectList = true;
+ }
+ }
+ }
+
+ UpdateTargets getTargetList() {
+ return targets;
+ }
+
+ ArrayList getBlUsers() {
+ return blUsers;
+ }
+
+ boolean getLazyUpdate() {
+ return lazyUpdate;
+ }
+
+ void removeNodes(J3dMessage m) {
+ if (m.args[1] != null) {
+ TargetsInterface ti = (TargetsInterface)m.args[1];
+ ti.updateCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS,
+ (CachedTargets[])m.args[2]);
+ }
+ }
+
+ void cleanup() {}
+}
diff --git a/src/classes/share/javax/media/j3d/TransparencyAttributes.java b/src/classes/share/javax/media/j3d/TransparencyAttributes.java
new file mode 100644
index 0000000..11dc4c1
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransparencyAttributes.java
@@ -0,0 +1,531 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The TransparencyAttributes object defines all attributes affecting
+ * transparency of the object. The transparency attributes are:<P>
+ * <UL>
+ * <LI>Transparency mode - defines how transparency is applied to
+ * this Appearance component object:</LI><P>
+ * <UL>
+ * <LI>FASTEST - uses the fastest available method for transparency.</LI><P>
+ * <LI>NICEST - uses the nicest available method for transparency.</LI><P>
+ * <LI>SCREEN_DOOR - uses screen-door transparency. This is done using
+ * an on/off stipple pattern in which the percentage of transparent pixels
+ * is approximately equal to the value specified by the transparency
+ * parameter.</LI><P>
+ * <LI>BLENDED - uses alpha blended transparency. The blend equation is
+ * specified by the srcBlendFunction and dstBlendFunction attributes.
+ * The default equation is:
+ * <ul>
+ * <code>alpha<sub><font size=-1>src</font></sub>*src +
+ * (1-alpha<sub><font size=-1>src</font></sub>)*dst</code>
+ * </ul>
+ * where <code>alpha<sub><font size=-1>src</font></sub></code> is
+ * <code>1-transparency</code>.
+ * When this mode is used with a Raster object or with a Geometry
+ * that contains per-vertex colors with alpha, the alpha values in
+ * the Raster's image or in the Geometry's per-vertex colors are
+ * combined with the transparency value in this TransparencyAttributes
+ * object to perform blending. In this case, the alpha value used for
+ * blending at each pixel is:
+ * <ul>
+ * <code>alpha<sub><font size=-1>src</font></sub> =
+ * alpha<sub><font size=-1>pix</font></sub> *
+ * (1-transparency)</code>.
+ * </ul>
+ * </LI><P>
+ * <LI>NONE - no transparency; opaque object.</LI><P>
+ * </UL>
+ * <LI>Blend function - used in blended transparency and antialiasing
+ * operations. The source function specifies the factor that is
+ * multiplied by the source color. This value is added to the product
+ * of the destination factor and the destination color. The default
+ * source blend function is BLEND_SRC_ALPHA. The source blend function
+ * is one of the following:</LI><P>
+ * <UL>
+ * <LI>BLEND_ZERO - the blend function is <code>f = 0</code>.</LI>
+ * <LI>BLEND_ONE - the blend function is <code>f = 1</code>.</LI>
+ * <LI>BLEND_SRC_ALPHA - the blend function is <code>f =
+ * alpha<sub><font size=-1>src</font></sub></code>.</LI>
+ * <LI>BLEND_ONE_MINUS_SRC_ALPHA - the blend function is <code>f =
+ * 1 - alpha<sub><font size=-1>src</font></sub></code>.</LI></UL><P>
+ * <LI>Blend value - the amount of transparency to be applied to this
+ * Appearance component object. The transparency values are in the
+ * range [0.0, 1.0], with 0.0 being fully opaque and 1.0 being
+ * fully transparent.</LI><P>
+ * </UL>
+ */
+public class TransparencyAttributes extends NodeComponent {
+ /**
+ * Specifies that this TransparencyAttributes object
+ * allows reading its transparency mode component information.
+ */
+ public static final int
+ ALLOW_MODE_READ = CapabilityBits.TRANSPARENCY_ATTRIBUTES_ALLOW_MODE_READ;
+
+ /**
+ * Specifies that this TransparencyAttributes object
+ * allows writing its transparency mode component information.
+ */
+ public static final int
+ ALLOW_MODE_WRITE = CapabilityBits.TRANSPARENCY_ATTRIBUTES_ALLOW_MODE_WRITE;
+
+ /**
+ * Specifies that this TransparencyAttributes object
+ * allows reading its transparency value.
+ */
+ public static final int
+ ALLOW_VALUE_READ = CapabilityBits.TRANSPARENCY_ATTRIBUTES_ALLOW_VALUE_READ;
+
+ /**
+ * Specifies that this TransparencyAttributes object
+ * allows writing its transparency value.
+ */
+ public static final int
+ ALLOW_VALUE_WRITE = CapabilityBits.TRANSPARENCY_ATTRIBUTES_ALLOW_VALUE_WRITE;
+
+ /**
+ * Specifies that this TransparencyAttributes object
+ * allows reading its blend function.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_BLEND_FUNCTION_READ =
+ CapabilityBits.TRANSPARENCY_ATTRIBUTES_ALLOW_BLEND_FUNCTION_READ;
+
+ /**
+ * Specifies that this TransparencyAttributes object
+ * allows writing its blend function.
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int ALLOW_BLEND_FUNCTION_WRITE =
+ CapabilityBits.TRANSPARENCY_ATTRIBUTES_ALLOW_BLEND_FUNCTION_WRITE;
+
+ /**
+ * Use the fastest available method for transparency.
+ * @see #setTransparencyMode
+ */
+ public static final int FASTEST = 0;
+
+ /**
+ * Use the nicest available method for transparency.
+ * @see #setTransparencyMode
+ */
+ public static final int NICEST = 1;
+
+ /**
+ * Use alpha blended transparency. The blend equation is
+ * specified by the srcBlendFunction and dstBlendFunction attributes.
+ * The default equation is:
+ * <ul>
+ * <code>alpha<sub><font size=-1>src</font></sub>*src +
+ * (1-alpha<sub><font size=-1>src</font></sub>)*dst</code>
+ * </ul>
+ * where <code>alpha<sub><font size=-1>src</font></sub></code> is
+ * <code>1-transparency</code>.
+ * When this mode is used with a Raster object or with a Geometry
+ * that contains per-vertex colors with alpha, the alpha values in
+ * the Raster's image or in the Geometry's per-vertex colors are
+ * combined with the transparency value in this TransparencyAttributes
+ * object to perform blending. In this case, the alpha value used for
+ * blending at each pixel is:
+ * <ul>
+ * <code>alpha<sub><font size=-1>src</font></sub> =
+ * alpha<sub><font size=-1>pix</font></sub> *
+ * (1-transparency)</code>.
+ * </ul>
+ *
+ * @see #setTransparencyMode
+ * @see #setSrcBlendFunction
+ * @see #setDstBlendFunction
+ */
+ public static final int BLENDED = 2;
+
+ /**
+ * Use screen-door transparency. This is done using an on/off stipple
+ * pattern where the percentage of pixels that are transparent is
+ * approximately equal to the value specified by the transparency
+ * parameter.
+ * @see #setTransparencyMode
+ */
+ public static final int SCREEN_DOOR = 3;
+
+ /**
+ * No transparency, opaque object.
+ * @see #setTransparencyMode
+ */
+ public static final int NONE = 4;
+
+ /**
+ * Blend function: <code>f = 0</code>.
+ * @see #setSrcBlendFunction
+ * @see #setDstBlendFunction
+ * @since Java 3D 1.2
+ */
+ public static final int BLEND_ZERO = 0;
+
+ /**
+ * Blend function: <code>f = 1</code>.
+ * @see #setSrcBlendFunction
+ * @see #setDstBlendFunction
+ * @since Java 3D 1.2
+ */
+ public static final int BLEND_ONE = 1;
+
+ /**
+ * Blend function:
+ * <code>f = alpha<sub><font size=-1>src</font></sub></code>.
+ * @see #setSrcBlendFunction
+ * @see #setDstBlendFunction
+ * @since Java 3D 1.2
+ */
+ public static final int BLEND_SRC_ALPHA = 2;
+
+ /**
+ * Blend function:
+ * <code>f = 1-alpha<sub><font size=-1>src</font></sub></code>.
+ * @see #setSrcBlendFunction
+ * @see #setDstBlendFunction
+ * @since Java 3D 1.2
+ */
+ public static final int BLEND_ONE_MINUS_SRC_ALPHA = 3;
+
+
+ /**
+ * Constructs a TransparencyAttributes object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * transparency mode : <code>NONE</code><br>
+ * transparency value : 0.0<br>
+ * source blend function : <code>BLEND_SRC_ALPHA</code><br>
+ * destination blend function : <code>BLEND_ONE_MINUS_SRC_ALPHA</code><br>
+ * </ul>
+ */
+ public TransparencyAttributes() {
+ // Just use the default for all attributes
+ }
+
+ /**
+ * Construct TransparencyAttributes object with specified values.
+ * @param tMode the transparency mode
+ * @param tVal the transparency value
+ * @exception IllegalArgumentException if
+ * <code>tMode</code> is a value other than
+ * <code>NONE</code>, <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>SCREEN_DOOR</code>, or <code>BLENDED</code>
+ *
+ */
+ public TransparencyAttributes(int tMode, float tVal){
+ this(tMode, tVal, BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
+ }
+
+ /**
+ * Construct TransparencyAttributes object with specified values.
+ * @param tMode the transparency mode
+ * @param tVal the transparency value
+ * @param srcBlendFunction the blend function to be used for the source
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ * @param dstBlendFunction the blend function to be used for the
+ * destination
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ * @exception IllegalArgumentException if
+ * <code>tMode</code> is a value other than
+ * <code>NONE</code>, <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>SCREEN_DOOR</code>, or <code>BLENDED</code>
+ * @exception IllegalArgumentException if
+ * <code>srcBlendFunction</code> or <code>dstBlendFunction</code>
+ * is a value other than <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or
+ * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public TransparencyAttributes(int tMode,
+ float tVal,
+ int srcBlendFunction,
+ int dstBlendFunction) {
+ if ((tMode < FASTEST) ||(tMode > NONE)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes6"));
+ }
+
+ if ((srcBlendFunction < BLEND_ZERO) ||
+ (srcBlendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes7"));
+ }
+
+ if ((dstBlendFunction < BLEND_ZERO) ||
+ (dstBlendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes8"));
+ }
+
+ ((TransparencyAttributesRetained)this.retained).initTransparencyMode(tMode);
+ ((TransparencyAttributesRetained)this.retained).initTransparency(tVal);
+ ((TransparencyAttributesRetained)this.retained).initSrcBlendFunction(srcBlendFunction);
+ ((TransparencyAttributesRetained)this.retained).initDstBlendFunction(dstBlendFunction);
+ }
+
+ /**
+ * Sets the transparency mode for this
+ * appearance component object.
+ * @param transparencyMode the transparency mode to be used, one of
+ * <code>NONE</code>, <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>SCREEN_DOOR</code>, or <code>BLENDED</code>
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if
+ * <code>transparencyMode</code> is a value other than
+ * <code>NONE</code>, <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>SCREEN_DOOR</code>, or <code>BLENDED</code>
+ */
+ public void setTransparencyMode(int transparencyMode) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes0"));
+
+ if ((transparencyMode < FASTEST) || (transparencyMode > NONE)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes6"));
+ }
+
+ if (isLive())
+ ((TransparencyAttributesRetained)this.retained).setTransparencyMode(transparencyMode);
+ else
+ ((TransparencyAttributesRetained)this.retained).initTransparencyMode(transparencyMode);
+ }
+
+
+
+ /**
+ * Gets the transparency mode for this
+ * appearance component object.
+ * @return transparencyMode the transparency mode
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getTransparencyMode() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_MODE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes1"));
+
+ return ((TransparencyAttributesRetained)this.retained).getTransparencyMode();
+ }
+
+ /**
+ * Sets this appearance's transparency.
+ * @param transparency the appearance's transparency
+ * in the range [0.0, 1.0] with 0.0 being
+ * fully opaque and 1.0 being fully transparent
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setTransparency(float transparency) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_VALUE_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes2"));
+
+
+ if (isLive())
+ ((TransparencyAttributesRetained)this.retained).setTransparency(transparency);
+ else
+ ((TransparencyAttributesRetained)this.retained).initTransparency(transparency);
+
+ }
+
+
+ /**
+ * Retrieves this appearance's transparency.
+ * @return the appearance's transparency
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public float getTransparency() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_VALUE_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes3"));
+
+ return ((TransparencyAttributesRetained)this.retained).getTransparency();
+ }
+
+ /**
+ * Sets the source blend function used in blended transparency
+ * and antialiasing operations. The source function specifies the
+ * factor that is multiplied by the source color; this value is
+ * added to the product of the destination factor and the
+ * destination color. The default source blend function is
+ * <code>BLEND_SRC_ALPHA</code>.
+ *
+ * @param blendFunction the blend function to be used for the source
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if
+ * <code>blendFunction</code>
+ * is a value other than <code>BLEND_ZERO</code>,
+ * <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or
+ * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setSrcBlendFunction(int blendFunction) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_BLEND_FUNCTION_WRITE))
+ throw new
+ CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes4"));
+
+ if ((blendFunction < BLEND_ZERO) ||
+ (blendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes7"));
+ }
+
+
+ if (isLive())
+ ((TransparencyAttributesRetained)this.retained).setSrcBlendFunction(blendFunction);
+ else
+ ((TransparencyAttributesRetained)this.retained).initSrcBlendFunction(blendFunction);
+ }
+
+
+
+ /**
+ * Gets the source blend function for this
+ * TransparencyAttributes object.
+ * @return the source blend function.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getSrcBlendFunction() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_BLEND_FUNCTION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes5"));
+ return ((TransparencyAttributesRetained)this.retained).getSrcBlendFunction();
+ }
+
+ /**
+ * Sets the destination blend function used in blended transparency
+ * and antialiasing operations. The destination function specifies the
+ * factor that is multiplied by the destination color; this value is
+ * added to the product of the source factor and the
+ * source color. The default destination blend function is
+ * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ *
+ * @param blendFunction the blend function to be used for the destination
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @exception IllegalArgumentException if
+ * <code>blendFunction</code>
+ * is a value other than <code>BLEND_ZERO</code>,
+ * <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or
+ * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ *
+ * @since Java 3D 1.2
+ */
+ public void setDstBlendFunction(int blendFunction) {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_BLEND_FUNCTION_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes4"));
+
+ if ((blendFunction < BLEND_ZERO) ||
+ (blendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
+ throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes8"));
+ }
+
+ if (isLive())
+ ((TransparencyAttributesRetained)this.retained).setDstBlendFunction(blendFunction);
+ else
+ ((TransparencyAttributesRetained)this.retained).initDstBlendFunction(blendFunction);
+ }
+
+
+
+ /**
+ * Gets the destination blend function for this
+ * TransparencyAttributes object.
+ * @return the destination blend function.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.2
+ */
+ public int getDstBlendFunction() {
+ if (isLiveOrCompiled())
+ if (!this.getCapability(ALLOW_BLEND_FUNCTION_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes5"));
+
+ return ((TransparencyAttributesRetained)this.retained).getDstBlendFunction();
+ }
+
+ /**
+ * Creates a retained mode TransparencyAttributesRetained object that this
+ * TransparencyAttributes component object will point to.
+ */
+ void createRetained() {
+ this.retained = new TransparencyAttributesRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ TransparencyAttributes transa = new TransparencyAttributes();
+ transa.duplicateNodeComponent(this);
+ return transa;
+ }
+
+
+
+ /**
+ * Copies all node information from <code>originalNodeComponent</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object).
+ *
+ * @param originalNodeComponent 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 NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(NodeComponent originalNodeComponent,
+ boolean forceDuplicate) {
+ super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+ TransparencyAttributesRetained attr =
+ (TransparencyAttributesRetained) originalNodeComponent.retained;
+ TransparencyAttributesRetained rt =
+ (TransparencyAttributesRetained) retained;
+
+ rt.initTransparencyMode(attr.getTransparencyMode());
+ rt.initTransparency(attr.getTransparency());
+ rt.initSrcBlendFunction(attr.getSrcBlendFunction());
+ rt.initDstBlendFunction(attr.getDstBlendFunction());
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java b/src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java
new file mode 100644
index 0000000..75da932
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java
@@ -0,0 +1,347 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+
+/**
+ * The TransparencyAttributes object defines all attributes affecting
+ * transparency of the object.
+ */
+class TransparencyAttributesRetained extends NodeComponentRetained {
+ // A list of pre-defined bits to indicate which component
+ // in this TransparencyAttributes object changed.
+ static final int MODE_CHANGED = 0x01;
+ static final int VALUE_CHANGED = 0x02;
+ static final int SRC_BLEND_FUNCTION_CHANGED = 0x04;
+ static final int DST_BLEND_FUNCTION_CHANGED = 0x08;
+
+ // Integer flag that contains bitset to indicate
+ // which field changed.
+ int isDirty = 0xffff;
+
+ // Transparency mode (alpha, screen_door)
+ int transparencyMode = TransparencyAttributes.NONE;
+ float transparency = 0.0f;
+
+ // Transparency blend functions
+ int srcBlendFunction = TransparencyAttributes.BLEND_SRC_ALPHA;
+ int dstBlendFunction = TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA;
+
+ // Here are some blend functions that are used in multi-pass only
+ static final int BLEND_ZERO = 0;
+ static final int BLEND_ONE = 1;
+ static final int BLEND_SRC_ALPHA = 2;
+ static final int BLEND_ONE_MINUS_SRC_ALPHA = 3;
+ static final int BLEND_DST_COLOR = 4;
+ static final int BLEND_SRC_COLOR = 5;
+ static final int BLEND_ONE_MINUS_SRC_COLOR = 6;
+ static final int BLEND_CONSTANT_COLOR = 7;
+
+ /**
+ * Sets the transparency mode for this
+ * appearance component object.
+ * @param transparencyMode the transparency mode to be used, one of
+ * <code>NONE</code>, <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>SCREEN_DOOR</code>, or <code>BLENDED</code>
+ */
+ final void initTransparencyMode(int transparencyMode) {
+ this.transparencyMode = transparencyMode;
+ }
+
+ /**
+ * Sets the transparency mode for this
+ * appearance component object and sends a message notifying
+ * the interested structures of the change.
+ * @param transparencyMode the transparency mode to be used, one of
+ * <code>FASTEST</code>, <code>NICEST</code>,
+ * <code>SCREEN_DOOR</code>, or <code>BLENDED</code>
+ */
+ final void setTransparencyMode(int transparencyMode) {
+ initTransparencyMode(transparencyMode);
+ sendMessage(MODE_CHANGED, new Integer(transparencyMode));
+ }
+
+ /**
+ * Gets the transparency mode for this
+ * appearance component object.
+ * @return transparencyMode the transparency mode
+ */
+ final int getTransparencyMode() {
+ return transparencyMode;
+ }
+
+ /**
+ * Sets this appearance's transparency.
+ * @param transparency the appearance's transparency
+ * in the range [0.0, 1.0] with 0.0 being
+ * fully opaque and 1.0 being fully transparent
+ */
+ final void initTransparency(float transparency) {
+ this.transparency = transparency;
+ }
+
+ /**
+ * Sets this appearance's transparency and sends a message notifying
+ * the interested structures of the change.
+ * @param transparency the appearance's transparency
+ * in the range [0.0, 1.0] with 0.0 being
+ * fully opaque and 1.0 being fully transparent
+ */
+ final void setTransparency(float transparency) {
+ initTransparency(transparency);
+ sendMessage(VALUE_CHANGED, new Float(transparency));
+ }
+
+ /**
+ * Retrieves this appearance's transparency.
+ * @return the appearance's transparency
+ */
+ final float getTransparency() {
+ return this.transparency;
+ }
+
+ /**
+ * Sets the source blend function used in blended transparency
+ * and antialiasing operations. The source function specifies the
+ * factor that is multiplied by the source color; this value is
+ * added to the product of the destination factor and the
+ * destination color. The default source blend function is
+ * <code>BLEND_SRC_ALPHA</code>.
+ *
+ * @param blendFunction the blend function to be used for the source
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ */
+ final void initSrcBlendFunction(int blendFunction) {
+ this.srcBlendFunction = blendFunction;
+ }
+
+
+ /**
+ * Sets the source blend function used in blended transparency
+ * and antialiasing operations and sends a message notifying the
+ * interested structures of the change. The source function specifies the
+ * factor that is multiplied by the source color; this value is
+ * added to the product of the destination factor and the
+ * destination color. The default source blend function is
+ * <code>BLEND_SRC_ALPHA</code>.
+ *
+ * @param blendFunction the blend function to be used for the source
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ */
+ final void setSrcBlendFunction(int blendFunction) {
+ initSrcBlendFunction(blendFunction);
+ sendMessage(SRC_BLEND_FUNCTION_CHANGED, new Integer(blendFunction));
+ }
+
+
+ /**
+ * Retrieves this appearance's source blend function.
+ * @return the appearance's source blend function
+ */
+ final int getSrcBlendFunction() {
+ return srcBlendFunction;
+ }
+
+
+ /**
+ * Sets the destination blend function used in blended transparency
+ * and antialiasing operations. The destination function specifies the
+ * factor that is multiplied by the destination color; this value is
+ * added to the product of the source factor and the
+ * source color. The default destination blend function is
+ * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ *
+ * @param blendFunction the blend function to be used for the destination
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ *
+ */
+ final void initDstBlendFunction(int blendFunction) {
+ this.dstBlendFunction = blendFunction;
+ }
+
+
+ /**
+ * Sets the destination blend function used in blended transparency
+ * and antialiasing operations and sends a message notifying the
+ * interested structures of the change. The destination function
+ * specifies the factor that is multiplied by the destination
+ * color; this value is added to the product of the source factor
+ * and the source color. The default destination blend function is
+ * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ *
+ * @param blendFunction the blend function to be used for the destination
+ * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
+ * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+ */
+ final void setDstBlendFunction(int blendFunction) {
+ initDstBlendFunction(blendFunction);
+ sendMessage(DST_BLEND_FUNCTION_CHANGED, new Integer(blendFunction));
+ }
+
+
+ /**
+ * Retrieves this appearance's destination blend function.
+ * @return the appearance's destination blend function
+ */
+ final int getDstBlendFunction() {
+ return dstBlendFunction;
+ }
+
+
+ /**
+ * Creates and initializes a mirror object, point the mirror object
+ * to the retained object if the object is not editable
+ */
+ synchronized void createMirrorObject() {
+ if (mirror == null) {
+ // Check the capability bits and let the mirror object
+ // point to itself if is not editable
+ if (isStatic()) {
+ mirror = this;
+ } else {
+ TransparencyAttributesRetained mirrorTa
+ = new TransparencyAttributesRetained();
+ mirrorTa.source = source;
+ mirrorTa.set(this);
+ mirror = mirrorTa;
+
+ }
+ } else {
+ ((TransparencyAttributesRetained) mirror).set(this);
+ }
+ }
+
+ /**
+ * These two native methods update the native context.
+ */
+ native void updateNative(long ctx,
+ float alpha, int geometryType,
+ int polygonMode,
+ boolean lineAA, boolean pointAA,
+ int transparencyMode,
+ int srcBlendFunction,
+ int dstBlendFunction);
+
+ void updateNative(long ctx,
+ float alpha, int geometryType, int polygonMode,
+ boolean lineAA,
+ boolean pointAA) {
+ updateNative(ctx, alpha, geometryType, polygonMode,
+ lineAA, pointAA, transparencyMode,
+ srcBlendFunction, dstBlendFunction);
+ }
+
+ /**
+ * Initializes a mirror object, point the mirror object to the retained
+ * object if the object is not editable
+ */
+ synchronized void initMirrorObject() {
+ ((TransparencyAttributesRetained)mirror).set(this);
+ }
+
+ /**
+ * Update the "component" field of the mirror object with the
+ * given "value"
+ */
+ synchronized void updateMirrorObject(int component, Object value) {
+
+ TransparencyAttributesRetained mirrorTa =
+ (TransparencyAttributesRetained) mirror;
+
+ if ((component & MODE_CHANGED) != 0) {
+ mirrorTa.transparencyMode = ((Integer)value).intValue();
+ }
+ else if ((component & VALUE_CHANGED) != 0) {
+ mirrorTa.transparency = ((Float)value).floatValue();
+ }
+ else if ((component & SRC_BLEND_FUNCTION_CHANGED) != 0) {
+ mirrorTa.srcBlendFunction = ((Integer) value).intValue();
+ }
+ else if ((component & DST_BLEND_FUNCTION_CHANGED) != 0) {
+ mirrorTa.dstBlendFunction = ((Integer) value).intValue();
+ }
+ }
+
+
+ boolean equivalent(TransparencyAttributesRetained tr) {
+ return ((tr != null) &&
+ (tr.transparencyMode == transparencyMode) &&
+ (tr.transparency == transparency) &&
+ (tr.srcBlendFunction == srcBlendFunction) &&
+ (tr.dstBlendFunction == dstBlendFunction));
+ }
+
+ protected void set(TransparencyAttributesRetained transp) {
+ super.set(transp);
+ transparencyMode = transp.transparencyMode;
+ transparency = transp.transparency;
+ srcBlendFunction = transp.srcBlendFunction;
+ dstBlendFunction = transp.dstBlendFunction;
+ }
+
+
+
+ final void sendMessage(int attrMask, Object attr) {
+
+ ArrayList univList = new ArrayList();
+ ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);
+
+ // Send to rendering attribute structure, regardless of
+ // whether there are users or not (alternate appearance case ..)
+ J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+ createMessage.type = J3dMessage.TRANSPARENCYATTRIBUTES_CHANGED;
+ createMessage.universe = null;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+ createMessage.args[3] = new Integer(changedFrequent);
+ VirtualUniverse.mc.processMessage(createMessage);
+
+
+ // System.out.println("univList.size is " + univList.size());
+ for(int i=0; i<univList.size(); i++) {
+ createMessage = VirtualUniverse.mc.getMessage();
+ createMessage.threads = J3dThread.UPDATE_RENDER;
+ createMessage.type = J3dMessage.TRANSPARENCYATTRIBUTES_CHANGED;
+
+ createMessage.universe = (VirtualUniverse) univList.get(i);
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(attrMask);
+ createMessage.args[2] = attr;
+
+ ArrayList gL = (ArrayList) gaList.get(i);
+ GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+ gL.toArray(gaArr);
+ createMessage.args[3] = gaArr;
+
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ }
+
+ void handleFrequencyChange(int bit) {
+ if (bit == TransparencyAttributes.ALLOW_MODE_WRITE ||
+ bit == TransparencyAttributes.ALLOW_VALUE_WRITE||
+ bit == TransparencyAttributes.ALLOW_BLEND_FUNCTION_WRITE) {
+ setFrequencyChangeMask(bit, 0x1);
+ }
+ }
+
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/TransparencyInterpolator.java b/src/classes/share/javax/media/j3d/TransparencyInterpolator.java
new file mode 100644
index 0000000..c524f6a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransparencyInterpolator.java
@@ -0,0 +1,271 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+
+/**
+ * TransparencyInterpolator behavior. This class defines a behavior
+ * that modifies the transparency of its target TransparencyAttributes
+ * object by linearly interpolating between a pair of specified
+ * transparency values (using the value generated by the specified
+ * Alpha object).
+ * <P>
+ * There are two forms of constructor to specify the
+ * type of transparency interpolation. The first constructor takes
+ * an Alpha and a TransparencyAttributes object and creates a transparency
+ * interpolator that maps an Alpha value of 1.0 to a transparency
+ * value of 1.0, and an Alpha value of 0.0 and maps it to a
+ * transparency value of 0.0. The second constructor takes an Alpha,
+ * a TransparencyAttributes object, a minimum transparency value and a
+ * maximum transparency value. This constructor provides more
+ * flexibility by specifying how the Alpha values are mapped
+ * to the transparency values - an Alpha of 1.0 maps to the
+ * maximum transparency value and an Alpha of 0.0 maps to the
+ * minimum transparency value.<P>
+ *
+ * @see Alpha
+ * @see TransparencyAttributes
+ */
+
+
+public class TransparencyInterpolator extends Interpolator {
+
+ TransparencyAttributes target;
+ float minimumTransparency;
+ float maximumTransparency;
+
+ // We can't use a boolean flag since it is possible
+ // that after alpha change, this procedure only run
+ // once at alpha.finish(). So the best way is to
+ // detect alpha value change.
+ private float prevAlphaValue = Float.NaN;
+ private WakeupCriterion passiveWakeupCriterion =
+ (WakeupCriterion) new WakeupOnElapsedFrames(0, true);
+
+ // non-public, default constructor used by cloneNode
+ TransparencyInterpolator() {
+ }
+
+ /**
+ * Constructs a trivial transparency interpolator with a specified target,
+ * a minimum transparency of 0.0f and a maximum transparency of 1.0f.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransparencyAttributes component object affected
+ * by this interpolator
+ */
+ public TransparencyInterpolator(Alpha alpha,
+ TransparencyAttributes target) {
+
+ super(alpha);
+
+ this.target = target;
+ this.minimumTransparency = 0.0f;
+ this.maximumTransparency = 1.0f;
+ }
+
+ /**
+ * Constructs a new transparency interpolator that varies the target
+ * material's transparency between the two transparency values.
+ * @param alpha the alpha object for this Interpolator
+ * @param target the TransparencyAttributes component object affected
+ * by this interpolator
+ * @param minimumTransparency the starting transparency
+ * @param maximumTransparency the ending transparency
+ */
+ public TransparencyInterpolator(Alpha alpha,
+ TransparencyAttributes target,
+ float minimumTransparency,
+ float maximumTransparency) {
+
+ super(alpha);
+
+ this.target = target;
+ this.minimumTransparency = minimumTransparency;
+ this.maximumTransparency = maximumTransparency;
+ }
+
+ /**
+ * This method sets the minimumTransparency for this interpolator.
+ * @param transparency the new minimum transparency
+ */
+ public void setMinimumTransparency(float transparency) {
+ this.minimumTransparency = transparency;
+ }
+
+ /**
+ * This method retrieves this interpolator's minimumTransparency.
+ * @return the interpolator's minimum transparency value
+ */
+ public float getMinimumTransparency() {
+ return this.minimumTransparency;
+ }
+
+ /**
+ * This method sets the maximumTransparency for this interpolator.
+ * @param transparency the new maximum transparency
+ */
+ public void setMaximumTransparency(float transparency) {
+ this.maximumTransparency = transparency;
+ }
+
+ /**
+ * This method retrieves this interpolator's maximumTransparency.
+ * @return the interpolator's maximal transparency vslue
+ */
+ public float getMaximumTransparency() {
+ return this.maximumTransparency;
+ }
+
+ /**
+ * This method sets the target TransparencyAttributes object
+ * for this interpolator.
+ * @param target the target TransparencyAttributes object
+ */
+ public void setTarget(TransparencyAttributes target) {
+ this.target = target;
+ }
+
+ /**
+ * This method retrieves this interpolator's target reference.
+ * @return the interpolator's target TransparencyAttributes object
+ */
+ public TransparencyAttributes getTarget() {
+ return target;
+ }
+
+ // The TransparencyInterpolator's initialize routine uses the default
+ // initialization routine.
+
+ /**
+ * This method is invoked by the behavior scheduler every frame. It
+ * maps the alpha value that corresponds to the current time into a
+ * transparency value and updates the specified TransparencyAttributes
+ * object with this new transparency value.
+ * @param criteria an enumeration of the criteria that caused the
+ * stimulus
+ */
+ public void processStimulus(Enumeration criteria) {
+ // Handle stimulus
+ WakeupCriterion criterion = passiveWakeupCriterion;
+
+ if (alpha != null) {
+ float value = alpha.value();
+
+ if (value != prevAlphaValue) {
+ float val = (float)((1.0-value)*minimumTransparency +
+ value*maximumTransparency);
+
+ target.setTransparency(val);
+ prevAlphaValue = value;
+ }
+ if (!alpha.finished() && !alpha.isPaused()) {
+ criterion = defaultWakeupCriterion;
+ }
+ }
+ wakeupOn(criterion);
+ }
+
+ /**
+ * 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) {
+ TransparencyInterpolator ti = new TransparencyInterpolator();
+ ti.duplicateNode(this, forceDuplicate);
+ return ti;
+ }
+
+
+ /**
+ * Copies all TransparencyInterpolator 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
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ TransparencyInterpolator ti =
+ (TransparencyInterpolator) originalNode;
+
+ setMinimumTransparency(ti.getMinimumTransparency());
+ setMaximumTransparency(ti.getMaximumTransparency());
+
+ // this reference will be updated in updateNodeReferences()
+ setTarget(ti.getTarget());
+ }
+
+ /**
+ * Callback used to allow a node to check if any nodes referenced
+ * by that node have been duplicated via a call to <code>cloneTree</code>.
+ * This method is called by <code>cloneTree</code> after all nodes in
+ * the sub-graph have been duplicated. The cloned Leaf node's method
+ * will be called and the Leaf node can then look up any node references
+ * by using the <code>getNewObjectReference</code> method found in the
+ * <code>NodeReferenceTable</code> object. If a match is found, a
+ * reference to the corresponding Node in the newly cloned sub-graph
+ * is returned. If no corresponding reference is found, either a
+ * DanglingReferenceException is thrown or a reference to the original
+ * node is returned depending on the value of the
+ * <code>allowDanglingReferences</code> parameter passed in the
+ * <code>cloneTree</code> call.
+ * <p>
+ * NOTE: Applications should <i>not</i> call this method directly.
+ * It should only be called by the cloneTree method.
+ *
+ * @param referenceTable a NodeReferenceTableObject that contains the
+ * <code>getNewObjectReference</code> method needed to search for
+ * new object instances.
+ * @see NodeReferenceTable
+ * @see Node#cloneTree
+ * @see DanglingReferenceException
+ */
+ public void updateNodeReferences(NodeReferenceTable referenceTable) {
+ super.updateNodeReferences(referenceTable);
+
+ // check TransparencyAttributes
+ NodeComponent nc = getTarget();
+
+ if (nc != null) {
+ setTarget((TransparencyAttributes) referenceTable.getNewObjectReference(nc));
+ }
+ }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/TransparentRenderingInfo.java b/src/classes/share/javax/media/j3d/TransparentRenderingInfo.java
new file mode 100644
index 0000000..5008b22
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TransparentRenderingInfo.java
@@ -0,0 +1,104 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import javax.vecmath.*;
+import java.util.*;
+
+class TransparentRenderingInfo extends Object {
+ // For DepthSortedTransparency, rm is the rendermolecule
+ // that this rInfo is part of
+ // For non depth sorted transparency, rm is one of the rendermolecules
+ // in the textureBin that is rendered, all renderMolecules under
+ // rm.textureBin's will be rendered
+ RenderMolecule rm;
+ RenderAtomListInfo rInfo;
+ TransparentRenderingInfo prev;
+ TransparentRenderingInfo next;
+ double zVal; // Used in DepthSorted Transparency
+ // TODO: Add Dirty info
+
+ /**
+ * update state before rendering transparent objects
+ */
+ boolean updateState(Canvas3D cv) {
+
+ TextureBin textureBin = rm.textureBin;
+ AttributeBin attributeBin = textureBin.attributeBin;
+
+ // Get a collection to check if switch is on
+
+ RenderMolecule rm = textureBin.transparentRMList ;
+
+ // Optimization to skip updating Attributes if
+ // all switch are off. Note that switch condition
+ // is check again in rm.render().
+ while (rm != null) {
+ if (rm.isSwitchOn()) {
+ break;
+ }
+ if (rm.next != null) {
+ rm = rm.next;
+ } else {
+ rm = rm.nextMap;
+ }
+ }
+
+ if (rm == null) {
+ return false;
+ }
+
+ if (cv.environmentSet != attributeBin.environmentSet) {
+
+ boolean visible = (attributeBin.definingRenderingAttributes == null ||
+ attributeBin.definingRenderingAttributes.visible);
+
+ if ( (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+ == View.VISIBILITY_DRAW_VISIBLE && !visible) ||
+ (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+ == View.VISIBILITY_DRAW_INVISIBLE && visible)) {
+ return false;
+ }
+ attributeBin.environmentSet.lightBin.updateAttributes(cv);
+ attributeBin.environmentSet.updateAttributes(cv);
+ attributeBin.updateAttributes(cv);
+ }
+ else {
+ if (cv.attributeBin != attributeBin) {
+ boolean visible = (attributeBin.definingRenderingAttributes == null ||
+ attributeBin.definingRenderingAttributes.visible);
+
+ if ( (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+ == View.VISIBILITY_DRAW_VISIBLE && !visible) ||
+ (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+ == View.VISIBILITY_DRAW_INVISIBLE && visible)) {
+ return false;
+ }
+ attributeBin.updateAttributes(cv);
+ }
+ }
+ return true;
+ }
+
+ void render(Canvas3D cv) {
+ if (updateState(cv)) {
+ rm.textureBin.render(cv, rm.textureBin.transparentRMList);
+ }
+ }
+
+
+ void sortRender(Canvas3D cv) {
+ if (updateState(cv)) {
+ rm.textureBin.render(cv, this);
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TriangleArray.java b/src/classes/share/javax/media/j3d/TriangleArray.java
new file mode 100644
index 0000000..984e87f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TriangleArray.java
@@ -0,0 +1,154 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The TriangleArray object draws the array of vertices as individual
+ * triangles. Each group
+ * of three vertices defines a triangle to be drawn.
+ */
+
+public class TriangleArray extends GeometryArray {
+
+ // non-public, no parameter constructor
+ TriangleArray() {}
+
+ /**
+ * Constructs an empty TriangleArray object with the specified
+ * number of vertices, and vertex format.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @exception IllegalArgumentException if vertexCount is less than 3
+ * or vertexCount is <i>not</i> a multiple of 3
+ */
+ public TriangleArray(int vertexCount, int vertexFormat) {
+ super(vertexCount,vertexFormat);
+
+ if (vertexCount < 3 || ((vertexCount%3) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleArray0"));
+ }
+
+ /**
+ * Constructs an empty TriangleArray object with the specified
+ * number of vertices, and vertex format, number of texture coordinate
+ * sets, and texture coordinate mapping array.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 3
+ * or vertexCount is <i>not</i> a multiple of 3
+ *
+ * @since Java 3D 1.2
+ */
+ public TriangleArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap);
+
+ if (vertexCount < 3 || ((vertexCount%3) != 0))
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleArray0"));
+ }
+
+ /**
+ * Creates the retained mode TriangleArrayRetained object that this
+ * TriangleArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new TriangleArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ TriangleArrayRetained rt = (TriangleArrayRetained) retained;
+ int texSetCount = rt.getTexCoordSetCount();
+ TriangleArray t;
+
+ if (texSetCount == 0) {
+ t = new TriangleArray(rt.getVertexCount(),
+ rt.getVertexFormat());
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ t = new TriangleArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap);
+ }
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TriangleArrayRetained.java b/src/classes/share/javax/media/j3d/TriangleArrayRetained.java
new file mode 100644
index 0000000..749e3a2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TriangleArrayRetained.java
@@ -0,0 +1,414 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The TriangleArray object draws the array of vertices as individual
+ * triangles. Each group
+ * of three vertices defines a triangle to be drawn.
+ */
+
+class TriangleArrayRetained extends GeometryArrayRetained {
+
+ TriangleArrayRetained() {
+ this.geoType = GEO_TYPE_TRI_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[3];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)
+ && (sdist[0] <= 1.0)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ sdist,iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectCylinder(pnts, pickCylinder, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+ }
+
+
+ boolean intersect(Point3d[] pnts) {
+ Point3d[] points = new Point3d[3];
+ double dist[] = new double[1];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i<validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i<validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ }
+ break;
+ case 2: // Line
+ while (i<validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ if (intersectSegment(points, pnts[0], pnts[1], dist,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case 1: // Point
+ while (i<validVertexCount) {
+ getVertexData(i++, points[0]);
+ getVertexData(i++, points[1]);
+ getVertexData(i++, points[2]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0])) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+
+ boolean intersect(Transform3D thisToOtherVworld,
+ GeometryRetained geom) {
+
+ Point3d[] pnts = new Point3d[3];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ thisToOtherVworld.transform(pnts[2]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ Point3d[] pnts = new Point3d[3];
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[1]);
+ if (intersectBoundingSphere(pnts, bsphere, null,
+ null)) {
+ return true;
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < validVertexCount) {
+ getVertexData(i++, pnts[0]);
+ getVertexData(i++, pnts[1]);
+ getVertexData(i++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ null, null)) {
+ return true;
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+
+ return false;
+ }
+
+ // From Graphics Gems IV (pg5) and Graphics Gems II, Pg170
+ void computeCentroid() {
+ int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+ initialVertexIndex : initialCoordIndex);
+
+ Point3d pnt0 = getPoint3d();
+ Point3d pnt1 = getPoint3d();
+ Point3d pnt2 = getPoint3d();
+ Vector3d vec = getVector3d();
+ Vector3d normal = getVector3d();
+ Vector3d tmpvec = getVector3d();
+
+ double area;
+ double totalarea = 0;
+
+ centroid.x = 0;
+ centroid.y = 0;
+ centroid.z = 0;
+
+
+ while(i < validVertexCount) {
+ getVertexData(i++, pnt0);
+ getVertexData(i++, pnt1);
+ getVertexData(i++, pnt2);
+
+ // Determine the normal
+ vec.sub(pnt0, pnt1);
+ tmpvec.sub(pnt1, pnt2);
+
+ // Do the cross product
+ normal.cross(vec, tmpvec);
+ normal.normalize();
+
+ // If a degenerate triangle, don't include
+ if (Double.isNaN(normal.x + normal.y + normal.z))
+ continue;
+
+ // compute the area
+ getCrossValue(pnt0, pnt1, tmpvec);
+ getCrossValue(pnt1, pnt2, tmpvec);
+ getCrossValue(pnt2, pnt0, tmpvec);
+ area = normal.dot(tmpvec);
+ centroid.x += (pnt0.x + pnt1.x + pnt2.x)* area;
+ centroid.y += (pnt0.y + pnt1.y + pnt2.y)* area;
+ centroid.z += (pnt0.z + pnt1.z + pnt2.z)* area;
+ totalarea += area;
+
+ }
+ if (totalarea != 0.0) {
+ area = 1.0/(3.0 * totalarea);
+ centroid.x *= area;
+ centroid.y *= area;
+ centroid.z *= area;
+ }
+ freeVector3d(tmpvec);
+ freeVector3d(vec);
+ freeVector3d(normal);
+ freePoint3d(pnt0);
+ freePoint3d(pnt1);
+ freePoint3d(pnt2);
+ }
+
+ int getClassType() {
+ return TRIANGLE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TriangleFanArray.java b/src/classes/share/javax/media/j3d/TriangleFanArray.java
new file mode 100644
index 0000000..c5757f9
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TriangleFanArray.java
@@ -0,0 +1,179 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The TriangleFanArray object draws an array of vertices as a set of
+ * connected triangle fans. An array of per-strip
+ * vertex counts specifies where the separate strips (fans) appear
+ * in the vertex array. For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex,
+ * the previous vertex and the first vertex. This can be thought of
+ * as a collection of convex polygons.
+ */
+
+public class TriangleFanArray extends GeometryStripArray {
+
+ // non-public, no parameter constructor
+ TriangleFanArray() {}
+
+ /**
+ * Constructs an empty TriangleFanArray object with the specified
+ * number of vertices, vertex format, and
+ * array of per-strip vertex counts.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 3
+ * or any element in the stripVertexCounts array is less than 3
+ */
+ public TriangleFanArray(int vertexCount,
+ int vertexFormat,
+ int stripVertexCounts[]) {
+
+ super(vertexCount, vertexFormat, stripVertexCounts);
+
+ if (vertexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleFanArray0"));
+ }
+
+ /**
+ * Constructs an empty TriangleFanArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and
+ * array of per-strip vertex counts.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 3
+ * or any element in the stripVertexCounts array is less than 3
+ *
+ * @since Java 3D 1.2
+ */
+ public TriangleFanArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int stripVertexCounts[]) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ stripVertexCounts);
+
+ if (vertexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleFanArray0"));
+ }
+
+ /**
+ * Creates the retained mode TriangleFanArrayRetained object that this
+ * TriangleFanArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new TriangleFanArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ TriangleFanArrayRetained rt = (TriangleFanArrayRetained) retained;
+ int stripcounts[] = new int[rt.getNumStrips()];
+ rt.getStripVertexCounts(stripcounts);
+ int texSetCount = rt.getTexCoordSetCount();
+ TriangleFanArray t;
+ if (texSetCount == 0) {
+ t = new TriangleFanArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ stripcounts);
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ t = new TriangleFanArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ stripcounts);
+ }
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java b/src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java
new file mode 100644
index 0000000..68a42f4
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java
@@ -0,0 +1,499 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The TriangleFanArray object draws an array of vertices as a set of
+ * connected triangle fans. An array of per-strip
+ * vertex counts specifies where the separate strips (fans) appear
+ * in the vertex array. For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex,
+ * the previous vertex and the first vertex. This can be thought of
+ * as a collection of convex polygons.
+ */
+
+class TriangleFanArrayRetained extends GeometryStripArrayRetained {
+
+ TriangleFanArrayRetained() {
+ this.geoType = GEO_TYPE_TRI_FAN_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[3];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = 0;
+ int j, end;
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleFanArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+ }
+
+ // intersect pnts[] with every triangle in this object
+ boolean intersect(Point3d[] pnts) {
+ int j, end;
+ Point3d[] points = new Point3d[3];
+ double dist[] = new double[1];
+ int i = 0;
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+
+
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 2: // Line
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectSegment(points, pnts[0], pnts[1],
+ dist, null)) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0])) {
+ return true;
+ }
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) {
+ int i = 0, j, end;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ thisToOtherVworld.transform(pnts[2]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = 0;
+ int j, end;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ end = j + stripVertexCounts[i++];
+ while ( j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while ( j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, null, null)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while ( j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope, null, null)) {
+ return true;
+ }
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ // From Graphics Gems IV (pg5) and Graphics Gems II, Pg170
+ void computeCentroid() {
+ Vector3d vec = getVector3d();
+ Vector3d normal = getVector3d();
+ Vector3d tmpvec = getVector3d();
+ Point3d pnt0 = getPoint3d();
+ Point3d pnt1 = getPoint3d();
+ Point3d pnt2 = getPoint3d();
+ double area, totalarea = 0;
+ int end, replaceIndex, j, i = 0;
+ centroid.x = 0;
+ centroid.y = 0;
+ centroid.z = 0;
+
+ while( i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnt0);
+ getVertexData(j++, pnt1);
+ replaceIndex = 2;
+ while (j < end) {
+ area = 0;
+ if (replaceIndex == 2) {
+ getVertexData(j++, pnt2);
+ replaceIndex = 1;
+ } else {
+ getVertexData(j++, pnt1);
+ replaceIndex = 2;
+ }
+
+ // Determine the normal
+ vec.sub(pnt0, pnt1);
+ tmpvec.sub(pnt1, pnt2);
+
+ // Do the cross product
+ normal.cross(vec, tmpvec);
+ normal.normalize();
+ // If a degenerate triangle, don't include
+ if (Double.isNaN(normal.x + normal.y + normal.z))
+ continue;
+
+ tmpvec.set(0,0,0);
+
+ // compute the area
+ getCrossValue(pnt0, pnt1, tmpvec);
+ getCrossValue(pnt1, pnt2, tmpvec);
+ getCrossValue(pnt2, pnt0, tmpvec);
+ area = normal.dot(tmpvec);
+ totalarea += area;
+ centroid.x += (pnt0.x + pnt1.x + pnt2.x) * area;
+ centroid.y += (pnt0.y + pnt1.y + pnt2.y) * area;
+ centroid.z += (pnt0.z + pnt1.z + pnt2.z) * area;
+
+ }
+ }
+
+ if (totalarea != 0.0) {
+ area = 1.0/(3.0 * totalarea);
+ centroid.x *= area;
+ centroid.y *= area;
+ centroid.z *= area;
+ }
+ freeVector3d(tmpvec);
+ freeVector3d(vec);
+ freeVector3d(normal);
+ freePoint3d(pnt0);
+ freePoint3d(pnt1);
+ freePoint3d(pnt2);
+ }
+
+ int getClassType() {
+ return TRIANGLE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TriangleStripArray.java b/src/classes/share/javax/media/j3d/TriangleStripArray.java
new file mode 100644
index 0000000..48c05c5
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TriangleStripArray.java
@@ -0,0 +1,178 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The TriangleStripArray object draws an array of vertices as a set of
+ * connected triangle strips. An array of per-strip vertex counts specifies
+ * where the separate strips appear in the vertex array.
+ * For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex and
+ * the two previous vertices.
+ */
+
+public class TriangleStripArray extends GeometryStripArray {
+
+ // non-public, no parameter constructor
+ TriangleStripArray() {}
+
+ /**
+ * Constructs an empty TriangleStripArray object with the specified
+ * number of vertices, vertex format, and
+ * array of per-strip vertex counts.
+ * @param vertexCount the number of vertex elements in this array
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 3
+ * or any element in the stripVertexCounts array is less than 3
+ */
+ public TriangleStripArray(int vertexCount,
+ int vertexFormat,
+ int stripVertexCounts[]) {
+
+ super(vertexCount, vertexFormat, stripVertexCounts);
+
+ if (vertexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleStripArray0"));
+ }
+
+ /**
+ * Constructs an empty TriangleStripArray object with the specified
+ * number of vertices, vertex format, number of texture coordinate
+ * sets, texture coordinate mapping array, and
+ * array of per-strip vertex counts.
+ *
+ * @param vertexCount the number of vertex elements in this array<p>
+ *
+ * @param vertexFormat a mask indicating which components are
+ * present in each vertex. This is specified as one or more
+ * individual flags that are bitwise "OR"ed together to describe
+ * the per-vertex data.
+ * The flags include: COORDINATES, to signal the inclusion of
+ * vertex positions--always present; NORMALS, to signal
+ * the inclusion of per vertex normals; one of COLOR_3,
+ * COLOR_4, to signal the inclusion of per vertex
+ * colors (without or with color information); and one of
+ * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
+ * to signal the
+ * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
+ *
+ * @param texCoordSetCount the number of texture coordinate sets
+ * in this GeometryArray object. If <code>vertexFormat</code>
+ * does not include one of <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3 or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetCount</code> parameter is not used.<p>
+ *
+ * @param texCoordSetMap an array that maps texture coordinate
+ * sets to texture units. The array is indexed by texture unit
+ * number for each texture unit in the associated Appearance
+ * object. The values in the array specify the texture coordinate
+ * set within this GeometryArray object that maps to the
+ * corresponding texture
+ * unit. All elements within the array must be less than
+ * <code>texCoordSetCount</code>. A negative value specifies that
+ * no texture coordinate set maps to the texture unit
+ * corresponding to the index. If there are more texture units in
+ * any associated Appearance object than elements in the mapping
+ * array, the extra elements are assumed to be -1. The same
+ * texture coordinate set may be used for more than one texture
+ * unit. Each texture unit in every associated Appearance must
+ * have a valid source of texture coordinates: either a
+ * non-negative texture coordinate set must be specified in the
+ * mapping array or texture coordinate generation must be enabled.
+ * Texture coordinate generation will take precedence for those
+ * texture units for which a texture coordinate set is specified
+ * and texture coordinate generation is enabled. If
+ * <code>vertexFormat</code> does not include one of
+ * <code>TEXTURE_COORDINATE_2</code>,
+ * <code>TEXTURE_COORDINATE_3</code> or
+ * <code>TEXTURE_COORDINATE_4</code>, the
+ * <code>texCoordSetMap</code> array is not used.<p>
+ *
+ * @param stripVertexCounts array that specifies
+ * the count of the number of vertices for each separate strip.
+ * The length of this array is the number of separate strips.
+ *
+ * @exception IllegalArgumentException if vertexCount is less than 3
+ * or any element in the stripVertexCounts array is less than 3
+ *
+ * @since Java 3D 1.2
+ */
+ public TriangleStripArray(int vertexCount,
+ int vertexFormat,
+ int texCoordSetCount,
+ int[] texCoordSetMap,
+ int stripVertexCounts[]) {
+
+ super(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap,
+ stripVertexCounts);
+
+ if (vertexCount < 3 )
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleStripArray0"));
+ }
+
+ /**
+ * Creates the retained mode TriangleStripArrayRetained object that this
+ * TriangleStripArray object will point to.
+ */
+ void createRetained() {
+ this.retained = new TriangleStripArrayRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+ */
+ public NodeComponent cloneNodeComponent() {
+ TriangleStripArrayRetained rt = (TriangleStripArrayRetained) retained;
+ int stripcounts[] = new int[rt.getNumStrips()];
+ rt.getStripVertexCounts(stripcounts);
+ int texSetCount = rt.getTexCoordSetCount();
+ TriangleStripArray t;
+ if (texSetCount == 0) {
+ t = new TriangleStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ stripcounts);
+ } else {
+ int texMap[] = new int[rt.getTexCoordSetMapLength()];
+ rt.getTexCoordSetMap(texMap);
+ t = new TriangleStripArray(rt.getVertexCount(),
+ rt.getVertexFormat(),
+ texSetCount,
+ texMap,
+ stripcounts);
+
+ }
+ t.duplicateNodeComponent(this);
+ return t;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java b/src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java
new file mode 100644
index 0000000..9547520
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java
@@ -0,0 +1,519 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+
+/**
+ * The TriangleStripArray object draws an array of vertices as a set of
+ * connected triangle strips. An array of per-strip vertex counts specifies
+ * where the separate strips appear in the vertex array.
+ * For every strip in the set,
+ * each vertex, beginning with the third vertex in the array,
+ * defines a triangle to be drawn using the current vertex and
+ * the two previous vertices.
+ */
+
+class TriangleStripArrayRetained extends GeometryStripArrayRetained {
+
+ TriangleStripArrayRetained() {
+ this.geoType = GEO_TYPE_TRI_STRIP_SET;
+ }
+
+ boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+ Point3d pnts[] = new Point3d[3];
+ double sdist[] = new double[1];
+ double minDist = Double.MAX_VALUE;
+ double x = 0, y = 0, z = 0;
+ int i = 0;
+ int j, end;
+
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ switch (pickShape.getPickType()) {
+ case PickShape.PICKRAY:
+ PickRay pickRay= (PickRay) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKSEGMENT:
+ PickSegment pickSegment = (PickSegment) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectSegment(pnts, pickSegment.start,
+ pickSegment.end, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox bbox = (BoundingBox)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, sdist,
+ iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope)
+ ((PickBounds) pickShape).bounds;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope,
+ sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCYLINDER:
+ PickCylinder pickCylinder= (PickCylinder) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKCONE:
+ PickCone pickCone= (PickCone) pickShape;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectCone(pnts, pickCone, sdist, iPnt)) {
+ if (dist == null) {
+ return true;
+ }
+ if (sdist[0] < minDist) {
+ minDist = sdist[0];
+ x = iPnt.x;
+ y = iPnt.y;
+ z = iPnt.z;
+ }
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKPOINT:
+ // Should not happen since API already check for this
+ throw new IllegalArgumentException(J3dI18N.getString("TriangleStripArrayRetained0"));
+ default:
+ throw new RuntimeException ("PickShape not supported for intersection");
+ }
+
+ if (minDist < Double.MAX_VALUE) {
+ dist[0] = minDist;
+ iPnt.x = x;
+ iPnt.y = y;
+ iPnt.z = z;
+ return true;
+ }
+ return false;
+ }
+
+ // intersect pnts[] with every triangle in this object
+ boolean intersect(Point3d[] pnts) {
+ int j, end;
+ Point3d[] points = new Point3d[3];
+ double dist[] = new double[1];
+ int i = 0;
+
+ points[0] = new Point3d();
+ points[1] = new Point3d();
+ points[2] = new Point3d();
+
+ switch (pnts.length) {
+ case 3: // Triangle
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2])) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 4: // Quad
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[1], pnts[2]) ||
+ intersectTriTri(points[0], points[1], points[2],
+ pnts[0], pnts[2], pnts[3])) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 2: // Line
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectSegment(points, pnts[0], pnts[1],
+ dist, null)) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ case 1: // Point
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, points[0]);
+ getVertexData(j++, points[1]);
+ while (j < end) {
+ getVertexData(j++, points[2]);
+ if (intersectTriPnt(points[0], points[1], points[2],
+ pnts[0])) {
+ return true;
+ }
+ points[0].set(points[1]);
+ points[1].set(points[2]);
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) {
+ int i = 0, j, end;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ thisToOtherVworld.transform(pnts[0]);
+ thisToOtherVworld.transform(pnts[1]);
+ while (j < end) {
+ getVertexData(j++, pnts[2]);
+ thisToOtherVworld.transform(pnts[2]);
+ if (geom.intersect(pnts)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ return false;
+ }
+
+ // the bounds argument is already transformed
+ boolean intersect(Bounds targetBound) {
+ int i = 0;
+ int j, end;
+ Point3d[] pnts = new Point3d[3];
+ pnts[0] = new Point3d();
+ pnts[1] = new Point3d();
+ pnts[2] = new Point3d();
+
+
+ switch(targetBound.getPickType()) {
+ case PickShape.PICKBOUNDINGBOX:
+ BoundingBox box = (BoundingBox) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while ( j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingBox(pnts, box, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGSPHERE:
+ BoundingSphere bsphere = (BoundingSphere) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while ( j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingSphere(pnts, bsphere, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ case PickShape.PICKBOUNDINGPOLYTOPE:
+ BoundingPolytope bpolytope = (BoundingPolytope) targetBound;
+
+ while (i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnts[0]);
+ getVertexData(j++, pnts[1]);
+ while ( j < end) {
+ getVertexData(j++, pnts[2]);
+ if (intersectBoundingPolytope(pnts, bpolytope, null, null)) {
+ return true;
+ }
+ pnts[0].set(pnts[1]);
+ pnts[1].set(pnts[2]);
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException("Bounds not supported for intersection "
+ + targetBound);
+ }
+ return false;
+ }
+
+ // From Graphics Gems IV (pg5) and Graphics Gems II, Pg170
+ void computeCentroid() {
+ Point3d pnt0 = getPoint3d();
+ Point3d pnt1 = getPoint3d();
+ Point3d pnt2 = getPoint3d();
+ Vector3d vec = getVector3d();
+ Vector3d normal = getVector3d();
+ Vector3d tmpvec = getVector3d();
+
+ double area, totalarea = 0;
+ int end, replaceIndex, j, i = 0;
+ centroid.x = 0;
+ centroid.y = 0;
+ centroid.z = 0;
+
+ while( i < stripVertexCounts.length) {
+ j = stripStartVertexIndices[i];
+ end = j + stripVertexCounts[i++];
+ getVertexData(j++, pnt0);
+ getVertexData(j++, pnt1);
+ replaceIndex = 2;
+ while (j < end) {
+ area = 0;
+ switch (replaceIndex) {
+ case 0:
+ getVertexData(j++, pnt0);
+ replaceIndex = 1;
+ break;
+ case 1:
+ getVertexData(j++, pnt1);
+ replaceIndex = 2;
+ break;
+ default:
+ getVertexData(j++, pnt2);
+ replaceIndex = 0;
+ }
+
+ // Determine the normal
+ vec.sub(pnt0, pnt1);
+ tmpvec.sub(pnt1, pnt2);
+
+ // Do the cross product
+ normal.cross(vec, tmpvec);
+ normal.normalize();
+ // If a degenerate triangle, don't include
+ if (Double.isNaN(normal.x + normal.y + normal.z))
+ continue;
+
+ tmpvec.set(0,0,0);
+
+ // compute the area
+ getCrossValue(pnt0, pnt1, tmpvec);
+ getCrossValue(pnt1, pnt2, tmpvec);
+ getCrossValue(pnt2, pnt0, tmpvec);
+ area = normal.dot(tmpvec);
+ totalarea += area;
+ centroid.x += (pnt0.x + pnt1.x + pnt2.x) * area;
+ centroid.y += (pnt0.y + pnt1.y + pnt2.y) * area;
+ centroid.z += (pnt0.z + pnt1.z + pnt2.z) * area;
+
+ }
+ }
+
+ if (totalarea != 0.0) {
+ area = 1.0/(3.0 * totalarea);
+ centroid.x *= area;
+ centroid.y *= area;
+ centroid.z *= area;
+ }
+
+ freeVector3d(tmpvec);
+ freeVector3d(vec);
+ freeVector3d(normal);
+ freePoint3d(pnt0);
+ freePoint3d(pnt1);
+ freePoint3d(pnt2);
+ }
+
+
+ int getClassType() {
+ return TRIANGLE_TYPE;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/UnorderList.java b/src/classes/share/javax/media/j3d/UnorderList.java
new file mode 100644
index 0000000..c1bdc75
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/UnorderList.java
@@ -0,0 +1,574 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.Arrays;
+
+/**
+ * A strongly type unorder array list.
+ * The operation add(Object o) & remove(int i) take O(1) time.
+ * The class is designed to optimize speed. So many reductance
+ * procedures call and range check as found in ArrayList are
+ * removed.
+ *
+ * <p>
+ * Use the following code to iterate through an array.
+ *
+ * <pre>
+ * UnorderList list = new UnorderList(YourClass.class);
+ * // add element here
+ *
+ * YourClass[] arr = (YourClass []) list.toArray();
+ * int size = list.arraySize();
+ * for (int i=0; i < size; i++) {
+ * YourClass obj = arr[i];
+ * ....
+ * }
+ * </pre>
+ *
+ * <p>
+ * Note:
+ * <ul>
+ * 1) The array return is a copied of internal array.<br>
+ * 2) Don't use arr.length , use list.arraySize();<br>
+ * 3) No need to do casting for individual element as in
+ * ArrayList.<br>
+ * 4) UnorderList is thread safe.
+ * </ul>
+ */
+
+class UnorderList implements Cloneable, java.io.Serializable {
+
+ /**
+ * The array buffer into which the elements of the ArrayList are stored.
+ * The capacity of the ArrayList is the length of this array buffer.
+ *
+ * It is non-private to enable compiler do inlining for get(),
+ * set(), remove() when -O flag turn on.
+ */
+ transient Object elementData[];
+
+ /**
+ * Clone copy of elementData return by toArray(true);
+ */
+ transient Object cloneData[];
+ // size of the above clone objec.
+ transient int cloneSize;
+
+ transient boolean isDirty = true;
+
+ /**
+ * Component Type of individual array element entry
+ */
+
+ Class componentType;
+
+ /**
+ * The size of the ArrayList (the number of elements it contains).
+ *
+ * We make it non-private to enable compiler do inlining for
+ * getSize() when -O flag turn on.
+ */
+ int size;
+
+
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ * and the class data Type
+ *
+ * @param initialCapacity the initial capacity of the list.
+ * @param componentType class type of element in the list.
+ */
+ UnorderList(int initialCapacity, Class componentType) {
+ this.componentType = componentType;
+ this.elementData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType, initialCapacity);
+ }
+
+ /**
+ * Constructs an empty list.
+ * @param componentType class type of element in the list.
+ */
+ UnorderList(Class componentType) {
+ this(10, componentType);
+ }
+
+
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ *
+ * @param initialCapacity the initial capacity of the list.
+ */
+ UnorderList(int initialCapacity) {
+ this(initialCapacity, Object.class);
+ }
+
+
+ /**
+ * Constructs an empty list.
+ * componentType default to Object.
+ */
+ UnorderList() {
+ this(10, Object.class);
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ final int size() {
+ return size;
+ }
+
+
+ /**
+ * Returns the size of entry use in toArray() number of elements
+ * in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ final int arraySize() {
+ return cloneSize;
+ }
+
+ /**
+ * Tests if this list has no elements.
+ *
+ * @return <tt>true</tt> if this list has no elements;
+ * <tt>false</tt> otherwise.
+ */
+ final boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this list contains the specified element.
+ *
+ * @param o element whose presence in this List is to be tested.
+ */
+ synchronized final boolean contains(Object o) {
+ if (o != null) { // common case first
+ for (int i=size-1; i >= 0; i--)
+ if (o.equals(elementData[i]))
+ return true;
+ } else {
+ for (int i=size-1; i >= 0; i--)
+ if (elementData[i]==null)
+ return true;
+ }
+ return false;
+
+ }
+
+
+ /**
+ * Add Object into the list if it is not already exists.
+ *
+ * @param o an object to add into the list
+ * @return true if object successfully add, false if duplicate found
+ */
+ synchronized final boolean addUnique(Object o) {
+ if (!contains(o)) {
+ add(o);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Searches for the last occurence of the given argument, testing
+ * for equality using the <tt>equals</tt> method.
+ *
+ * @param o an object.
+ * @return the index of the first occurrence of the argument in this
+ * list; returns <tt>-1</tt> if the object is not found.
+ * @see Object#equals(Object)
+ */
+ synchronized final int indexOf(Object o) {
+ if (o != null) { // common case first
+ for (int i=size-1; i >= 0; i--)
+ if (o.equals(elementData[i]))
+ return i;
+ } else {
+ for (int i=size-1; i >= 0; i--)
+ if (elementData[i]==null)
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
+ * elements themselves are not copied.)
+ *
+ * @return a clone of this <tt>ArrayList</tt> instance.
+ */
+ synchronized protected final Object clone() {
+ try {
+ UnorderList v = (UnorderList)super.clone();
+ v.elementData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType, size);
+ System.arraycopy(elementData, 0, v.elementData, 0, size);
+ isDirty = true; // can't use the old cloneData reference
+ return v;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+ }
+
+
+ /**
+ * Returns an array containing all of the elements in this list.
+ * The size of the array may longer than the actual size. Use
+ * arraySize() to retrieve the size.
+ * The array return is a copied of internal array. if copy
+ * is true.
+ *
+ * @return an array containing all of the elements in this list
+ */
+ synchronized final Object[] toArray(boolean copy) {
+ if (copy) {
+ if (isDirty) {
+ if ((cloneData == null) || cloneData.length < size) {
+ cloneData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType, size);
+ }
+ System.arraycopy(elementData, 0, cloneData, 0, size);
+ cloneSize = size;
+ isDirty = false;
+ }
+ return cloneData;
+ } else {
+ cloneSize = size;
+ return elementData;
+ }
+
+ }
+
+ /**
+ * Returns an array containing all of the elements in this list.
+ * The size of the array may longer than the actual size. Use
+ * arraySize() to retrieve the size.
+ * The array return is a copied of internal array. So another
+ * thread can continue add/delete the current list. However,
+ * it should be noticed that two call to toArray() may return
+ * the same copy.
+ *
+ * @return an array containing all of the elements in this list
+ */
+ synchronized final Object[] toArray() {
+ return toArray(true);
+ }
+
+
+ /**
+ * Returns an array containing elements starting from startElement
+ * all of the elements in this list. A new array of exact size
+ * is always allocated.
+ *
+ * @param startElement starting element to copy
+ *
+ * @return an array containing elements starting from
+ * startElement, null if element not found.
+ *
+ */
+ synchronized final Object[] toArray(Object startElement) {
+ int idx = indexOf(startElement);
+ if (idx < 0) {
+ return (Object[])java.lang.reflect.Array.newInstance(componentType, 0);
+ }
+
+ int s = size - idx;
+ Object data[] = (Object[])java.lang.reflect.Array.newInstance(componentType, s);
+ System.arraycopy(elementData, idx, data, 0, s);
+ return data;
+ }
+
+ // copy element to objs and clear the array
+ synchronized final void toArrayAndClear(Object[] objs) {
+ System.arraycopy(elementData, 0, objs, 0, size);
+ Arrays.fill(elementData, 0, size, null);
+ size = 0;
+ isDirty = true;
+ }
+
+
+ /**
+ * Trims the capacity of this <tt>ArrayList</tt> instance to be the
+ * list's current size. An application can use this operation to minimize
+ * the storage of an <tt>ArrayList</tt> instance.
+ */
+ synchronized final void trimToSize() {
+ if (elementData.length > size) {
+ Object oldData[] = elementData;
+ elementData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType,
+ size);
+ System.arraycopy(oldData, 0, elementData, 0, size);
+ }
+ }
+
+
+ // Positional Access Operations
+
+ /**
+ * Returns the element at the specified position in this list.
+ *
+ * @param index index of element to return.
+ * @return the element at the specified position in this list.
+ * @throws IndexOutOfBoundsException if index is out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final Object get(int index) {
+ return elementData[index];
+ }
+
+ /**
+ * Replaces the element at the specified position in this list with
+ * the specified element.
+ *
+ * @param index index of element to replace.
+ * @param element element to be stored at the specified position.
+ * @return the element previously at the specified position.
+ * @throws IndexOutOfBoundsException if index out of range
+ * <tt>(index &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final void set(int index, Object element) {
+ elementData[index] = element;
+ isDirty = true;
+ }
+
+ /**
+ * Appends the specified element to the end of this list.
+ * It is the user responsible to ensure that the element add is of
+ * the same type as array componentType.
+ *
+ * @param o element to be appended to this list.
+ */
+ synchronized final void add(Object o) {
+ if (elementData.length == size) {
+ Object oldData[] = elementData;
+ elementData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType,
+ (size << 1));
+ System.arraycopy(oldData, 0, elementData, 0, size);
+
+ }
+ elementData[size++] = o;
+ isDirty = true;
+ }
+
+
+ /**
+ * Removes the element at the specified position in this list.
+ * Replace the removed element by the last one.
+ *
+ * @param index the index of the element to removed.
+ * @throws IndexOutOfBoundsException if index out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final void remove(int index) {
+ elementData[index] = elementData[--size];
+ elementData[size] = null;
+ isDirty = true;
+ /*
+ if ((cloneData != null) && (index < cloneData.length)) {
+ cloneData[index] = null; // for gc
+ }
+ */
+ }
+
+
+ /**
+ * Removes the element at the specified position in this list.
+ * The order is keep.
+ *
+ * @param index the index of the element to removed.
+ * @throws IndexOutOfBoundsException if index out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final void removeOrdered(int index) {
+ size--;
+ if (index < size) {
+ System.arraycopy(elementData, index+1,
+ elementData, index, size-index);
+
+ }
+ // gc for last element
+ elementData[size] = null;
+ isDirty = true;
+ }
+
+
+ /**
+ * Removes the element at the last position in this list.
+ * @return The element remove
+ * @throws IndexOutOfBoundsException if array is empty
+ */
+ synchronized final Object removeLastElement() {
+ Object elm = elementData[--size];
+ elementData[size] = null;
+ isDirty = true;
+ /*
+ if ((cloneData != null) && (size < cloneData.length)) {
+ cloneData[size] = null; // for gc
+ }
+ */
+ return elm;
+ }
+
+
+ // Shift element of array from positin idx to position 0
+ // Note that idx < size, otherwise ArrayIndexOutOfBoundsException
+ // throws. The element remove are copy to objs.
+ synchronized final void shift(Object objs[], int idx) {
+ int oldsize = size;
+
+ System.arraycopy(elementData, 0, objs, 0, idx);
+ size -= idx;
+ if (size > 0) {
+ System.arraycopy(elementData, idx, elementData, 0, size);
+ }
+ Arrays.fill(elementData, size, oldsize, null);
+ }
+
+ /**
+ * Removes the specified element in this list.
+ * Replace the removed element by the last one.
+ *
+ * @param o the element to removed.
+ * @return true if object remove
+ * @throws IndexOutOfBoundsException if index out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final boolean remove(Object o) {
+ size--;
+ if (o != null) {
+ for (int i=size; i >= 0; i--) {
+ if (o.equals(elementData[i])) {
+ elementData[i] = elementData[size];
+ elementData[size] = null;
+ /*
+ if ((cloneData != null) && (i < cloneData.length)) {
+ cloneData[i] = null; // for gc
+ }
+ */
+ isDirty = true;
+ return true;
+ }
+ }
+ } else {
+ for (int i=size; i >= 0; i--)
+ if (elementData[i]==null) {
+ elementData[i] = elementData[size];
+ elementData[size] = null;
+ /*
+ if ((cloneData != null) && (i < cloneData.length)) {
+ cloneData[i] = null; // for gc
+ }
+ */
+ isDirty = true;
+ return true;
+ }
+ }
+ size++; // fail to remove
+ return false;
+ }
+
+
+ /**
+ * Removes all of the elements from this list. The list will
+ * be empty after this call returns.
+ */
+ synchronized final void clear() {
+ if (size > 0) {
+ Arrays.fill(elementData, 0, size, null);
+ size = 0;
+ isDirty = true;
+ }
+ }
+
+ synchronized final void clearMirror() {
+ if (cloneData != null) {
+ Arrays.fill(cloneData, 0, cloneData.length, null);
+ }
+ cloneSize = 0;
+ isDirty = true;
+ }
+
+ final Class getComponentType() {
+ return componentType;
+ }
+
+ synchronized public String toString() {
+ StringBuffer sb = new StringBuffer("Size = " + size + "\n[");
+ int len = size-1;
+ Object obj;
+
+ for (int i=0; i < size; i++) {
+ obj = elementData[i];
+ if (obj != null) {
+ sb.append(elementData[i].toString());
+ } else {
+ sb.append("NULL");
+ }
+ if (i != len) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]\n");
+ return sb.toString();
+ }
+
+ /**
+ * Save the state of the <tt>ArrayList</tt> instance to a stream (that
+ * is, serialize it).
+ *
+ * @serialData The length of the array backing the <tt>ArrayList</tt>
+ * instance is emitted (int), followed by all of its elements
+ * (each an <tt>Object</tt>) in the proper order.
+ */
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException{
+ // Write out element count, and any hidden stuff
+ s.defaultWriteObject();
+
+ // Write out array length
+ s.writeInt(elementData.length);
+
+ // Write out all elements in the proper order.
+ for (int i=0; i<size; i++)
+ s.writeObject(elementData[i]);
+
+ }
+
+ /**
+ * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
+ * deserialize it).
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ // Read in size, and any hidden stuff
+ s.defaultReadObject();
+
+ // Read in array length and allocate array
+ int arrayLength = s.readInt();
+ elementData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType, arrayLength);
+
+ // Read in all elements in the proper order.
+ for (int i=0; i<size; i++)
+ elementData[i] = s.readObject();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/UpdateTargets.java b/src/classes/share/javax/media/j3d/UpdateTargets.java
new file mode 100644
index 0000000..4328453
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/UpdateTargets.java
@@ -0,0 +1,99 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class UpdateTargets {
+
+ static int updateSwitchThreads[] = {
+ // GEO
+ J3dThread.UPDATE_RENDER | J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_GEOMETRY,
+
+ // ENV
+ J3dThread.UPDATE_RENDER | J3dThread.UPDATE_RENDERING_ENVIRONMENT,
+
+ // BEH
+ J3dThread.UPDATE_BEHAVIOR,
+
+ // SND
+ J3dThread.UPDATE_SOUND | J3dThread.SOUND_SCHEDULER,
+
+ // VPF
+ J3dThread.UPDATE_RENDER | J3dThread.UPDATE_BEHAVIOR |
+ J3dThread.UPDATE_SOUND | J3dThread.SOUND_SCHEDULER,
+
+ // BLN
+ J3dThread.UPDATE_RENDER | J3dThread.UPDATE_RENDERING_ENVIRONMENT |
+ J3dThread.UPDATE_BEHAVIOR | J3dThread.UPDATE_SOUND,
+
+ // GRP
+ 0
+ };
+
+
+ UnorderList[] targetList = new UnorderList[Targets.MAX_NODELIST];
+
+ int computeSwitchThreads() {
+ int switchThreads = 0;
+
+ for (int i=0; i < Targets.MAX_NODELIST; i++) {
+ if (targetList[i] != null) {
+ switchThreads |= updateSwitchThreads[i];
+ }
+ }
+ return switchThreads | J3dThread.UPDATE_TRANSFORM;
+ }
+
+ void addNode(Object node, int targetType) {
+ if(targetList[targetType] == null)
+ targetList[targetType] = new UnorderList(1);
+
+ targetList[targetType].add(node);
+ }
+
+
+ void addNodeArray(Object[] nodeArr, int targetType) {
+ if(targetList[targetType] == null)
+ targetList[targetType] = new UnorderList(1);
+
+ targetList[targetType].add(nodeArr);
+ }
+
+
+ void clearNodes() {
+ for(int i=0; i<Targets.MAX_NODELIST; i++) {
+ if (targetList[i] != null) {
+ targetList[i].clear();
+ }
+ }
+ }
+
+ void addCachedTargets(CachedTargets cachedTargets) {
+ for(int i=0; i<Targets.MAX_NODELIST; i++) {
+ if (cachedTargets.targetArr[i] != null ) {
+ addNodeArray(cachedTargets.targetArr[i], i);
+ }
+ }
+ }
+
+ void dump() {
+ for(int i=0; i<Targets.MAX_NODELIST; i++) {
+ if (targetList[i] != null) {
+ System.out.println(" " + CachedTargets.typeString[i]);
+ for(int j=0; j<targetList[i].size(); j++) {
+ System.out.println(" " + targetList[i].get(j));
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/VersionInfo.java b/src/classes/share/javax/media/j3d/VersionInfo.java
new file mode 100644
index 0000000..3e52f01
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/VersionInfo.java
@@ -0,0 +1,209 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The VersionInfo class contains strings that describe the implementation
+ * and specification version of Java 3D. These strings are made available
+ * as properties obtained from the VirtualUniverse class.
+ *
+ * <h4>NOTE TO DEVELOPERS:</h4>
+ *
+ * <p>
+ * Developers are required to do the following whenever they modify
+ * Java 3D:
+ *
+ * <ol>
+ * <li>The VENDOR_DEVELOPER string must be modified to
+ * indicate the name of the individuals or organizations who have
+ * modified the source code.</li>
+ *
+ * <li>The VERSION_DEV_STRING may be modified to indicate
+ * additional information about the particular build, but this is
+ * not required.</li>
+ *
+ * <li>The strings denoted as being unmodifiable must <i>not</i> be
+ * modified.</li>
+ * </ol>
+ *
+ * <p>
+ * Additionally, developers are required to comply with the terms
+ * of the Java 3D API specification, which prohibits releasing an
+ * implementation of the Java 3D API without first licensing and
+ * passing the TCK tests.
+ *
+ * @see VirtualUniverse#getProperties
+ */
+class VersionInfo extends Object {
+ /**
+ * Developer who has modified Java 3D.
+ * This string <i>must</i> be modified to indicate the name of the
+ * individual(s) or organization(s) who modified the code.
+ */
+ private static final String VENDOR_DEVELOPER = null;
+
+ /**
+ * String identifying the particular build of Java 3D, for
+ * example, beta1, build47, rc1, etc. This string may only
+ * contain letters, numbers, periods, dashes, or underscores. It
+ * must not contain any other characters or spaces.
+ *
+ * This will typically by null for final, released builds, but
+ * should be non-null for all other builds.
+ */
+ private static final String VERSION_BUILD = "build2";
+
+ /**
+ * Time and date stamp appended to the end of the version string.
+ * This is appended to the version string
+ * after the build identifier (and after the first space, which
+ * will automatically be added) and before the optional dev
+ * string. This string should be null if no time stamp is desired
+ * (it will be null for production builds).
+ */
+ private static final String VERSION_TIME_STAMP = J3dBuildInfo.getBuildTimeStamp();
+
+ /**
+ * An optional string appended to the end of the version string,
+ * after the time stamp. A space will be automatically prepended
+ * to this string. This string should be null if no dev string is
+ * desired.
+ */
+ private static final String VERSION_DEV_STRING = null;
+
+ // -------------------------------------------------------------------
+ // -------------------------------------------------------------------
+ // END OF DEVELOPER-MODIFIABLE PARAMETERS
+ // -------------------------------------------------------------------
+ // -------------------------------------------------------------------
+
+
+ // -------------------------------------------------------------------
+ // The following set of constants must not be modified by developers.
+ //
+ // Only qualified licensees of the Java 3D API specification and
+ // TCK tests, who are releasing their own implementation of Java 3D
+ // are permitted to change these constants.
+ // -------------------------------------------------------------------
+
+ /**
+ * Specification version (major and minor version only). This
+ * string must not be modified by developers.
+ */
+ private static final String SPECIFICATION_VERSION = "1.3";
+
+ /**
+ * Specification vendor. This should never change and must not
+ * be modified by developers.
+ */
+ private static final String SPECIFICATION_VENDOR = "Sun Microsystems, Inc.";
+
+ /**
+ * Primary implementation vendor. This should only be changed by a
+ * platform vendor who has licensed the TCK tests and who is
+ * releasing their own implementation of Java 3D.
+ */
+ private static final String VENDOR_PRIMARY = "Sun Microsystems, Inc.";
+
+ /**
+ * Base version number. This is the major.minor.subminor version
+ * number. Version qualifiers are specified separately. The
+ * major and minor version <i>must</i> be the same as the specification
+ * version.
+ */
+ private static final String VERSION_BASE = "1.3.2";
+
+ /**
+ * Qualifier indicating that the version of Java 3D is
+ * experimental. This must <i>not</i> be modified by deverlopers.
+ * All non-official builds <i>must</i> contain the string
+ * <code>"experimental"</code> as part of the release name that
+ * appears before the optional first space.
+ */
+ private static final String VERSION_SUFFIX = "experimental";
+
+ /**
+ * The composite version string. This is composed in the static
+ * initializer for this class.
+ */
+ private static final String VERSION;
+
+ /**
+ * The composite vendor string. This is composed in the static
+ * initializer for this class.
+ */
+ private static final String VENDOR;
+
+ // The static initializer composes the version and vendor strings
+ static {
+ // Assign the vendor by concatenating primary and developer
+ // vendor strings
+ String tmpVendor = VENDOR_PRIMARY;
+ if (VENDOR_DEVELOPER != null) {
+ tmpVendor += " & " + VENDOR_DEVELOPER;
+ }
+
+ String tmpVersion = VERSION_BASE;
+ if (VERSION_BUILD != null) {
+ tmpVersion += "-" + VERSION_BUILD;
+ }
+
+ if (VERSION_SUFFIX != null) {
+ tmpVersion += "-" + VERSION_SUFFIX;
+ }
+
+ if (VERSION_TIME_STAMP != null) {
+ tmpVersion += " " + VERSION_TIME_STAMP;
+ }
+
+ if (VERSION_DEV_STRING != null) {
+ tmpVersion += " " + VERSION_DEV_STRING;
+ }
+
+ VERSION = tmpVersion;
+ VENDOR = tmpVendor;
+ }
+
+ /**
+ * Returns the specification version string.
+ * @return the specification version string
+ */
+ static String getSpecificationVersion() {
+ return SPECIFICATION_VERSION;
+ }
+
+ /**
+ * Returns the specification vendor string.
+ * @return the specification vendor string
+ */
+ static String getSpecificationVendor() {
+ return SPECIFICATION_VENDOR;
+ }
+
+
+ /**
+ * Returns the implementation version string.
+ * @return the implementation version string
+ */
+ static String getVersion() {
+ return VERSION;
+ }
+
+ /**
+ * Returns the implementation vendor string.
+ * @return the implementation vendor string
+ */
+ static String getVendor() {
+ return VENDOR;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/VertexArrayRenderMethod.java b/src/classes/share/javax/media/j3d/VertexArrayRenderMethod.java
new file mode 100644
index 0000000..759b6fd
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/VertexArrayRenderMethod.java
@@ -0,0 +1,88 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The RenderMethod interface is used to create various ways to render
+ * different geometries.
+ */
+
+class VertexArrayRenderMethod implements RenderMethod {
+
+
+ public boolean render(RenderMolecule rm, Canvas3D cv, int pass,
+ RenderAtomListInfo ra, int dirtyBits) {
+
+ GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry();
+ geo.setVertexFormat((rm.useAlpha && ((geo.vertexFormat &
+ GeometryArray.COLOR) != 0)),
+ rm.textureBin.attributeBin.ignoreVertexColors, cv.ctx);
+
+ if (rm.doInfinite) {
+ cv.updateState(pass, dirtyBits);
+ while (ra != null) {
+ renderGeo(ra, rm, pass, cv);
+ ra = ra.next;
+ }
+ return true;
+ }
+
+ boolean isVisible = false; // True if any of the RAs is visible.
+ while (ra != null) {
+ if (cv.ra == ra.renderAtom) {
+ if (cv.raIsVisible) {
+ cv.updateState(pass, dirtyBits);
+ renderGeo(ra, rm, pass, cv);
+ isVisible = true;
+ }
+ }
+ else {
+ if (ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) {
+ cv.updateState(pass, dirtyBits);
+ cv.raIsVisible = true;
+ renderGeo(ra, rm, pass, cv);
+ isVisible = true;
+ }
+ else {
+ cv.raIsVisible = false;
+ }
+ cv.ra = ra.renderAtom;
+ }
+
+ ra = ra.next;
+ }
+ geo.disableGlobalAlpha(cv.ctx,
+ (rm.useAlpha && ((geo.vertexFormat &
+ GeometryArray.COLOR) != 0)),
+ rm.textureBin.attributeBin.ignoreVertexColors);
+ return isVisible;
+ }
+
+ void renderGeo(RenderAtomListInfo ra, RenderMolecule rm, int pass, Canvas3D cv) {
+ GeometryArrayRetained geo;
+ boolean useAlpha;
+
+ useAlpha = rm.useAlpha;
+
+ geo = (GeometryArrayRetained)ra.geometry();
+
+
+ geo.execute(cv, ra.renderAtom, rm.isNonUniformScale,
+ (useAlpha && ((geo.vertexFormat & GeometryArray.COLOR) != 0)) ,
+ rm.alpha,
+ rm.renderBin.multiScreen,
+ cv.screen.screen,
+ rm.textureBin.attributeBin.ignoreVertexColors,
+ pass);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/View.java b/src/classes/share/javax/media/j3d/View.java
new file mode 100644
index 0000000..51ed5e2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/View.java
@@ -0,0 +1,3360 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.lang.Math;
+import java.util.Vector;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.awt.*;
+import java.awt.event.*;
+import com.sun.j3d.utils.universe.*; // Needed for Support of DVR.
+
+/**
+ * The View object contains all parameters needed in rendering a
+ * three dimensional scene from one viewpoint. A view contains a list
+ * of Canvas3D objects that the view is rendered into. It exists outside
+ * of the scene graph, but attaches to a ViewPlatform leaf node object
+ * in the scene graph. It also contains a reference to a PhysicalBody
+ * and a PhysicalEnvironment object.
+ * <P>
+ * The View object is the main Java 3D object for controlling the
+ * Java 3D viewing model. All of the components that specify the
+ * view transform used to render to the 3D canvases are either contained
+ * in the View object or in objects that are referenced by the View
+ * object.
+ * <P>
+ * Java 3D allows applications to specify multiple simultaneously active
+ * View objects, each controlling its own set of canvases.
+ * <P>
+ * The Java 3D View object has several instance variables and methods,
+ * but most are calibration variables or user-helping functions. The
+ * viewing policies defined by the View object are described below.
+ * <P>
+ * <b>Policies</b><P>
+ *
+ * The View object defines the following policies:<P>
+ * <UL>
+ * <LI>View policy - informs Java 3D whether it should generate
+ * the view using the head-tracked system of transformations or the
+ * head-mounted system of transformations. These policies are attached
+ * to the Java 3D View object. There are two view policies:</LI><P>
+ * <UL>
+ * <LI>SCREEN_VIEW - specifies that Java 3D should compute a new
+ * viewpoint using the sequence of transforms appropriate to screen-based
+ * head-tracked display environments (fish-tank VR/portals/VR-desks).
+ * This is the default setting.</LI><P>
+ * <LI>HMD_VIEW - specifies that Java 3D should compute a new viewpoint
+ * using the sequence of transforms appropriate to head mounted display
+ * environments. This policy is not available in compatibility mode
+ * (see the setCompatibilityModeEnable method description).</LI><P>
+ * </UL>
+ * <LI>Projection policy - specifies whether Java 3D should generate
+ * a parallel projection or a perspective projection. This policy
+ * is attached to the Java 3D View object. There are two projection
+ * policies:</LI><P>
+ * <UL>
+ * <LI>PARALLEL_PROJECTION - specifies that a parallel projection
+ * transform is computed.</LI><P>
+ * <LI>PERSPECTIVE_PROJECTION - specifies that a perspective projection
+ * transform is computed. This is the default policy.</LI><P>
+ * </UL>
+ * <LI>Screen scale policy - specifies where the screen scale comes from.
+ * There are two screen scale policies:</LI><P>
+ * <UL>
+ * <LI>SCALE_SCREEN_SIZE - specifies that the scale is derived from the
+ * physical screen according to the following formula (this is the
+ * default mode):</LI>
+ * <UL>
+ * <code>screenScale = physicalScreenWidth / 2.0</code><P>
+ * </UL>
+ * <LI>SCALE_EXPLICIT - pecifies that the scale is taken directly from
+ * the user-provided <code>screenScale</code> attribute (see the
+ * setScreenScale method description).</LI><P>
+ * </UL>
+ * <LI>Window resize policy - specifies how Java 3D modifies the view
+ * when users resize windows. When users resize or move windows,
+ * Java 3D can choose to think of the window as attached either to
+ * the physical world or to the virtual world. The window resize
+ * policy allows an application to specify how the
+ * view model will handle resizing requests.
+ * There are two window resize policies:</LI><P>
+ * <UL>
+ * <LI>VIRTUAL_WORLD - implies that the original image remains the
+ * same size on the screen but the user sees more or less of the
+ * virtual world depending on whether the window grew or shrank
+ * in size.</LI><P>
+ * <LI>PHYSICAL_WORLD - implies that the original image continues
+ * to fill the window in the same way using more or less pixels
+ * depending on whether the window grew or shrank in size.</LI><P>
+ * </UL>
+ * <LI>Window movement policy - specifies what part of the virtual
+ * world Java 3D draws as a function of window placement on the screen.
+ * There are two window movement policies:</LI><P>
+ * <UL>
+ * <LI>VIRTUAL_WORLD - implies that the image seen in the window
+ * changes as the position of the window shifts on the screen.
+ * (This mode acts as if the window were a window into the virtual
+ * world.)</LI><P>
+ * <LI>PHYSICAL_WORLD - implies that the image seen in the window
+ * remains the same no matter where the user positions the window
+ * on the screen.</LI><P>
+ * </UL>
+ * <LI>Window eyepoint policy - comes into effect in a non-head-tracked
+ * environment. The policy tells Java 3D how to construct a new view
+ * frustum based on changes in the field of view and in the Canvas3D's
+ * location on the screen. The policy only comes into effect when the
+ * application changes a parameter that can change the placement of the
+ * eyepoint relative to the view frustum.
+ * There are three window eyepoint policies:</LI><P>
+ * <UL>
+ * <LI>RELATIVE_TO_SCREEN - tells Java 3D to interpret the eye's position
+ * relative to the entire screen. No matter where an end user moves a
+ * window (a Canvas3D), Java 3D continues to interpret the eye's position
+ * relative to the screen. This implies that the view frustum changes shape
+ * whenever an end user moves the location of a window on the screen.
+ * In this mode, the field of view is read-only.</LI><P>
+ * <LI>RELATIVE_TO_WINDOW - specifies that Java 3D should interpret the
+ * eye's position information relative to the window (Canvas3D). No matter
+ * where an end user moves a window (a Canvas3D), Java 3D continues to
+ * interpret the eye's position relative to that window. This implies
+ * that the frustum remains the same no matter where the end user
+ * moves the window on the screen. In this mode, the field of view
+ * is read-only.</LI><P>
+ * <LI>RELATIVE_TO_FIELD_OF_VIEW - tells Java 3D that it should
+ * modify the eyepoint position so it is located at the appropriate
+ * place relative to the window to match the specified field of view.
+ * This implies that the view frustum will change whenever the
+ * application changes the field of view. In this mode, the eye
+ * position is read-only. This is the default setting.</LI><P>
+ * <LI>RELATIVE_TO_COEXISTENCE - tells Java 3D to interpret the eye's
+ * position in coexistence coordinates. In this mode, the eye position
+ * is taken from the view (rather than the Canvas3D) and transformed from
+ * coexistence coordinates to image plate coordinates for each
+ * Canvas3D. The resulting eye position is relative to the screen. As
+ * in RELATIVE_TO_SCREEN mode, this implies that the view frustum
+ * changes shape whenever an end user moves the location of a window
+ * on the screen. In this mode, the field of view is
+ * read-only.</LI><P>
+ * </UL>
+ * <LI>Front and back clip policies - specifies how Java 3D
+ * interprets clipping distances to both the near and far clip
+ * planes. The policies can contain one of four values specifying
+ * whether a distance measurement should be interpreted in
+ * the physical or the virtual world and whether that distance
+ * measurement should be interpreted relative to the physical
+ * eyepoint or the physical screen.
+ * The front and back clip policies
+ * are specified separately. The front clip policy determines
+ * where Java 3D places the front clipping plane. The back clip
+ * policy determines where Java 3D places the back clipping plane.
+ * The values for both front and back clipping planes are:</LI><P>
+ * <UL>
+ * <LI>VIRTUAL_EYE - specifies that the associated distance is from
+ * the eye and in units of virtual distance.</LI><P>
+ * <LI>PHYSICAL_EYE - specifies that the associated distance is from
+ * the eye and in units of physical distance (in meters).</LI><P>
+ * <LI>VIRTUAL_SCREEN - specifies that the associated distance is
+ * from the screen and in units of virtual distance. </LI><P>
+ * <LI>PHYSICAL_SCREEN - specifies that the associated distance is
+ * from the screen and in units of physical distance (in meters).
+ * This is the default policy for both front and back clipping.</LI><P>
+ * </UL>
+ * <LI>Visibility policy - specifies how visible and invisible objects
+ * are drawn. There are three visibility policies:</LI><P>
+ * <UL>
+ * <LI>VISIBILITY_DRAW_VISIBLE - only visible objects are drawn
+ * (this is the default).</LI><P>
+ * <LI>VISIBILITY_DRAW_INVISIBLE - only invisible objects are drawn.</LI><P>
+ * <LI>VISIBILITY_DRAW_ALL - both visible and invisible
+ * objects are drawn. </LI><P>
+ * </UL>
+ * <LI>Transparency sorting policy - specifies whether and how
+ * transparent objects are sorted. Sorting multiple transparent
+ * objects is necessary to avoid artifacts caused by overlapping
+ * transparent objects. There are two transparency sorting
+ * policies:</LI><P>
+ * <UL>
+ * <LI>TRANSPARENCY_SORT_NONE - no depth sorting of transparent
+ * objects is performed (this is the default). Transparent objects
+ * are drawn after opaque objects, but are not sorted from back to
+ * front.</LI><P>
+ * <LI>TRANSPARENCY_SORT_GEOMETRY - transparent objects are
+ * depth-sorted on a per-geometry basis. Each geometry object of each
+ * transparent Shape3D node is drawn from back to front. Note that
+ * this policy will not split geometry into smaller pieces, so
+ * intersecting or intertwined objects may not be sorted
+ * correctly. The method used for determining which geometry is closer
+ * is implementation dependent.</LI><P>
+ * </UL>
+ * </UL>
+ * <b>Projection and Clip Parameters</b><P>
+ * The projection and clip parameters determine the view model's
+ * field of view and the front and back clipping distances.<P>
+ * <UL>
+ * <LI>Field of view - specifies the view model's horizontal
+ * field of view in radians, when in the default non-head-tracked
+ * mode. This value is ignored when the view model is operating
+ * in head-tracked mode, or when the Canvas3D's window eyepoint
+ * policy is set to a value other than the default setting of
+ * RELATIVE_TO_FIELD_OF_VIEW.</LI><P>
+ * <LI>Front clip distance - specifies the distance away from the
+ * clip origin, specified by the front clip policy variable, in
+ * the direction of gaze where objects stop disappearing. Objects
+ * closer than the clip origin (eye or screen)
+ * plus the front clip distance are not drawn. Measurements are
+ * done in the space (physical or virtual) that is specified by
+ * the associated front clip policy parameter.</LI><P>
+ * <LI>Back clip distance - specifies the distance away from the
+ * clip origin (specified by the back clip policy variable) in the
+ * direction of gaze where objects begin disappearing. Objects
+ * farther away from the clip origin (eye or
+ * screen) plus the back clip distance are not drawn.
+ * Measurements are done in the space (physical or virtual) that
+ * is specified by the associated back clip policy
+ * parameter. The View object's back clip distance is ignored
+ * if the scene graph contains an active Clip leaf node.</LI><P>
+ * There are several considerations to take into account when
+ * choosing values for the front and back clip distances.<P>
+ * <UL>
+ * <LI>The front clip distance must be greater than 0.0 in physical
+ * eye coordinates.</LI><P>
+ * <LI>The front clipping plane must be in front of the back clipping
+ * plane, that is, the front clip distance must be less than the
+ * back clip distance in physical eye coordinates.</LI><P>
+ * <LI>The front and back clip distances, in physical eye coordinates,
+ * must be less than the largest positive single-precision floating
+ * point value, Float.MAX_VALUE. In practice, since these physical
+ * eye coordinate distances are in meters, the values
+ * should be much less than that.</LI><P>
+ * <LI>The ratio of the back distance divided by the front distance,
+ * in physical eye coordinates, affects Z-buffer precision. This ratio
+ * should be less than about 3000 to accommodate 16-bit Z-buffers.
+ * Values of 100 to less than 1000 will produce better results.</LI><P>
+ * </UL>
+ * Violating any of the above rules will result in undefined behavior.
+ * In many cases, no picture will be drawn.<P>
+ * </UL>
+ * <b>Frame Start Time, Duration, and Number</b><P>
+ *
+ * There are five methods used to get information about system
+ * execution and performance:<P>
+ * <UL>
+ * <code>getCurrentFrameStartTime</code> returns the time at which
+ * the most recent rendering frame started.<P>
+ * <code>getLastFrameDuration</code> returns the duration, in milliseconds, of
+ * the most recently completed rendering frame.<P>
+ * <code>getFrameNumber</code> returns the frame number for this view.<P>
+ * <code>getMaxFrameStartTimes</code> retrieves the implementation-dependent
+ * maximum number of frames whose start times will be recorded by
+ * the system.<P>
+ * <code>getFrameStartTimes</code> copies the last k frame start time values
+ * into the user-specified array.<P>
+ * </UL>
+ * <b>View Traversal and Behavior Scheduling</b><P>
+ * The following methods control the traversal, the rendering, and
+ * the execution of the behavior scheduler for this view:<P>
+ * <UL>
+ * <code>startBehaviorScheduler</code> starts the behavior scheduler
+ * running after it has been stopped.<P>
+ * <code>stopBehaviorScheduler</code> stops the behavior scheduler after all
+ * currently-scheduled behaviors are executed.<P>
+ * <code>isBehaviorSchedulerRunning</code> retrieves a flag that indicates
+ * whether the behavior scheduler is currently running.<P>
+ * <code>startView</code> starts traversing this view and starts the renderers
+ * associated with all canvases attached to this view.<P>
+ * <code>stopView</code> stops traversing this view after the current state of
+ * the scene graph is reflected on all canvases attached to this
+ * view.<P>
+ * <code>isViewRunning</code> returns a flag indicating whether the traverser
+ * is currently running on this view.<P>
+ * </UL>
+ * Note: The above six methods are heavy-weight methods intended
+ * for verification and image capture (recording). They are not
+ * intended to be used for flow control.<P>
+ *
+ * <b>Scene Antialiasing</b><P>
+ *
+ * The following methods set and retrieve the scene antialiasing
+ * flag. Scene antialiasing is either enabled or disabled for this
+ * view. If enabled, the entire scene will be antialiased on each
+ * canvas in which scene antialiasing is available. Scene
+ * antialiasing is disabled by default.<P>
+ * <UL>
+ * <code>setSceneAntialiasingEnable</code> sets the scene antialiasing flag.<P>
+ * <code>getSceneAntialiasingEnable</code> returns the scene antialiasing
+ * flag.<P>
+ * </UL>
+ * Note that line and point antialiasing are independent of scene
+ * antialiasing. If antialiasing is enabled for lines and points,
+ * the lines and points will be antialiased prior to scene antialiasing.
+ * If scene antialiasing is disabled, antialiased lines and points will
+ * still be antialiased.
+ * <p>
+ * <b>Note:</b> Scene antialiasing is ignored in pure immediate mode,
+ * but is supported in mixed-immediate mode.
+ * <p>
+ * <b>Depth Buffer</b><P>
+ *
+ * The following two methods enable and disable automatic freezing
+ * of the depth buffer for objects rendered during the transparent
+ * rendering pass (that is, objects rendered using alpha blending)
+ * for this view. If enabled, depth buffer writes are disabled
+ * during the transparent rendering pass regardless of the value
+ * of the depth-buffer-write-enable flag in the RenderingAttributes
+ * object for a particular node. This flag is enabled by default.<P>
+ * <UL>
+ * <code>setDepthBufferFreezeTransparent</code> enables depth buffer freezing.<P>
+ * <code>getDepthBufferFreezeTransparent</code> retrieves the depth buffer
+ * flag.<P>
+ * </UL>
+ * Transparent objects include BLENDED transparent primitives
+ * and antialiased lines
+ * and points. Transparent objects do not include opaque objects
+ * or primitives rendered with SCREEN_DOOR transparency.<p>
+ *
+ * <b>Sensors</b><P>
+ *
+ * The following methods retrieve the sensor's location in the
+ * virtual world:<P>
+ * <UL>
+ * <code>getSensorToVworld</code> takes the sensor's last reading and
+ * generates a sensor-to-vworld coordinate system transform. This
+ * Transform3D object takes points in that sensor's local coordinate
+ * system and transforms them into virtual world coordinates.<P>
+ *
+ * <code>getSensorHotSpotInVworld</code> retrieves the specified sensor's
+ * last hotspot location in virtual world coordinates.<P>
+ * </UL>
+ *
+ * <b>Compatibility Mode</b><P>
+ *
+ * A camera-based view model allows application programmers to think
+ * about the images displayed on the computer screen as if a virtual
+ * camera took those images. Such a view model allows application
+ * programmers to position and orient a virtual camera within a
+ * virtual scene, to manipulate some parameters of the virtual
+ * camera's lens (specify its field of view), and to specify the
+ * locations of the near and far clipping planes.<P>
+ * Java 3D allows applications to enable compatibility mode for
+ * room-mounted, non-head-tracked display environments, or to disable
+ * compatibility mode using the following methods. Camera-based
+ * viewing functions are only available in compatibility mode.<P>
+ * <UL>
+ * <code>setCompatibilityModeEnable</code> turns compatibility mode on or off.
+ * Compatibility mode is disabled by default.<P>
+ * <code>getCompatabilityModeEnable</code> returns the compatibility mode
+ * enable flag.<P>
+ * </UL>
+ * Use of these view-compatibility functions will disable some of
+ * Java 3D's view model features and limit the portability of Java
+ * 3D programs. These methods are primarily intended to help
+ * jump-start porting of existing applications.<P>
+ *
+ * Setting the Viewing Transform<P>
+ *
+ * The View object provides the following compatibility-mode
+ * methods that operate on the viewing transform.<P>
+ * <UL>
+ * <code>setVpcToEc</code> a compatibility mode method that
+ * specifies the ViewPlatform
+ * coordinates (VPC) to eye coordinates viewing transform.<P>
+ * <code>getVpcToEc</code> returns the VPC.<P>
+ * </UL>
+ * Setting the Projection Transform
+ * <p>
+ * The View object provides the following compatibility-mode
+ * methods that operate on the projection transform:<P>
+ * <UL>
+ * The <code>setLeftProjection</code> and <code>setRightProjection</code>
+ * methods specify
+ * a viewing frustum for the left and right eye that transforms
+ * points in eye coordinates to clipping coordinates.<P>
+ *
+ * The <code>getLeftProjection</code> and <code>getRightProjection</code>
+ * methods return
+ * the viewing frustum for the left and right eye.<P>
+ * </UL>
+ *
+ * @see Canvas3D
+ * @see PhysicalBody
+ * @see PhysicalEnvironment
+ * @see ViewPlatform
+ * @see TransparencyAttributes
+ */
+
+public class View extends Object {
+ /**
+ * Specifies a policy whereby the origin of physical or virtual
+ * coordinates is relative to the position of the nominal head.
+ * When used as a view attach policy, this sets the origin of view
+ * platform coordinates to be at the eyepoint.
+ * @see ViewPlatform#setViewAttachPolicy
+ * @see PhysicalEnvironment#setCoexistenceCenterInPworldPolicy
+ */
+ public static final int NOMINAL_HEAD = 0;
+
+ /**
+ * Specifies a policy whereby the origin of physical or virtual
+ * coordinates is relative to the position of the nominal feet.
+ * When used as a view attach policy, this sets the origin of view
+ * platform coordinates to be at the ground plane.
+ * @see ViewPlatform#setViewAttachPolicy
+ * @see PhysicalEnvironment#setCoexistenceCenterInPworldPolicy
+ */
+ public static final int NOMINAL_FEET = 1;
+
+ /**
+ * Specifies a policy whereby the origin of physical or virtual
+ * coordinates is relative to the screen.
+ * When used as a view attach policy, this sets the origin of view
+ * platform coordinates to be at the center of the window or screen,
+ * in effect, allowing the user to view objects from an optimal viewpoint.
+ * @see ViewPlatform#setViewAttachPolicy
+ * @see PhysicalEnvironment#setCoexistenceCenterInPworldPolicy
+ */
+ public static final int NOMINAL_SCREEN = 2;
+
+ /**
+ * Specifies that the screen scale for this view is derived from
+ * the physical screen size. This scale factor is computed as follows:
+ * <ul>
+ * physical_screen_width / 2.0
+ * </ul>
+ * This allows an application to define a world in a normalized
+ * [-1,1] space and view it on a screen of any size.
+ * @see #setScreenScalePolicy
+ */
+ public static final int SCALE_SCREEN_SIZE = 0;
+
+ /**
+ * Specifies that the screen scale for this view is taken directly
+ * from the user-provided screenScale parameter.
+ * @see #setScreenScalePolicy
+ * @see #setScreenScale
+ */
+ public static final int SCALE_EXPLICIT = 1;
+
+ /**
+ * Specifies that the associated distance is measured
+ * from the screen in virtual world coordinates.
+ * Policy for interpreting clip plane distances.
+ * Used in specifying the policy in frontClipPolicy and backClipPolicy.
+ * @see #setFrontClipPolicy
+ * @see #setBackClipPolicy
+ */
+ public static final int VIRTUAL_SCREEN = 0;
+
+ /**
+ * Specifies that the associated distance is measured
+ * from the screen in meters.
+ * Policy for interpreting clip plane distances.
+ * Used in specifying the policy in frontClipPolicy and backClipPolicy.
+ * @see #setFrontClipPolicy
+ * @see #setBackClipPolicy
+ */
+ public static final int PHYSICAL_SCREEN = 1;
+
+ /**
+ * Specifies that the associated distance is measured
+ * from the eye in virtual world coordinates.
+ * Policy for interpreting clip plane distances.
+ * Used in specifying the policy in frontClipPolicy and backClipPolicy.
+ * @see #setFrontClipPolicy
+ * @see #setBackClipPolicy
+ */
+ public static final int VIRTUAL_EYE = 2;
+
+ /**
+ * Specifies that the associated distance is measured
+ * from the eye in meters.
+ * Policy for interpreting clip plane distances.
+ * Used in specifying the policy in frontClipPolicy and backClipPolicy.
+ * @see #setFrontClipPolicy
+ * @see #setBackClipPolicy
+ */
+ public static final int PHYSICAL_EYE = 3;
+
+ /**
+ * Policy for resizing and moving windows.
+ * Used in specifying windowResizePolicy and windowMovementPolicy.
+ * VIRTUAL_WORLD specifies that the associated action takes place
+ * in the virtual world as well as in the physical world.
+ * @see #setWindowResizePolicy
+ * @see #setWindowMovementPolicy
+ */
+ public static final int VIRTUAL_WORLD = 0;
+
+ /**
+ * Policy for resizing and moving windows.
+ * Used in specifying windowResizePolicy and windowMovementPolicy.
+ * PHYSICAL_WORLD specifies that the specified action takes place
+ * only in the physical world.
+ * @see #setWindowResizePolicy
+ * @see #setWindowMovementPolicy
+ */
+ public static final int PHYSICAL_WORLD = 1;
+
+ /**
+ * Policy for placing the eyepoint in non-head-tracked modes.
+ * Specifies that Java 3D should interpret the
+ * given fixed eyepoint position as relative to the entire screen.
+ * This implies
+ * that the view frustum shape will change whenever a
+ * user moves the location of a window on the screen.
+ * @see #setWindowEyepointPolicy
+ */
+ public static final int RELATIVE_TO_SCREEN = 0;
+
+ /**
+ * Policy for placing the eyepoint in non-head-tracked modes.
+ * Specifies that Java 3D should interpret the
+ * given fixed-eyepoint position as relative to the window.
+ * @see #setWindowEyepointPolicy
+ */
+ public static final int RELATIVE_TO_WINDOW = 1;
+
+ /**
+ * Policy for placing the eyepoint in non-head-tracked modes.
+ * Specifies that Java 3D should
+ * modify the position of the eyepoint to match any changes in field
+ * of view; the view frustum will change whenever the application
+ * program changes the field of view.
+ * <p>
+ * NOTE: when this policy is specified, the Z coordinate of
+ * the derived eyepoint is used in place of
+ * nominalEyeOffsetFromNominalScreen.
+ * @see #setWindowEyepointPolicy
+ */
+ public static final int RELATIVE_TO_FIELD_OF_VIEW = 2;
+
+ /**
+ * Policy for placing the eyepoint in non-head-tracked modes.
+ * Specifies that Java 3D should interpret the fixed eyepoint
+ * position in the view as relative to the origin
+ * of coexistence coordinates. This eyepoint is transformed from
+ * coexistence coordinates to image plate coordinates for each
+ * Canvas3D.
+ * As in RELATIVE_TO_SCREEN mode, this implies
+ * that the view frustum shape will change whenever a
+ * user moves the location of a window on the screen.
+ * @see #setWindowEyepointPolicy
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int RELATIVE_TO_COEXISTENCE = 3;
+
+ /**
+ * Specifies that monoscopic view generated should be the view as seen
+ * from the left eye.
+ * @see Canvas3D#setMonoscopicViewPolicy
+ */
+ public static final int LEFT_EYE_VIEW = 0;
+
+ /**
+ * Specifies that monoscopic view generated should be the view as seen
+ * from the right eye.
+ * @see Canvas3D#setMonoscopicViewPolicy
+ */
+ public static final int RIGHT_EYE_VIEW = 1;
+
+ /**
+ * Specifies that monoscopic view generated should be the view as seen
+ * from the 'center eye', the fictional eye half-way between the left and
+ * right eye.
+ * @see Canvas3D#setMonoscopicViewPolicy
+ */
+ public static final int CYCLOPEAN_EYE_VIEW = 2;
+
+ /**
+ * Specifies that the viewing environment for this view is a
+ * standard screen-based display environment.
+ * In this mode, Java 3D will compute new viewpoints
+ * using that sequence of transforms appropriate to screen-based,
+ * display environments, that may or may not include head tracking
+ * (e.g., a monoscopic screen, fish-tank VR, portals, VR-desks).
+ * This is the default mode.
+ * @see #setViewPolicy
+ */
+ public static final int SCREEN_VIEW = 0;
+
+ /**
+ * Specifies that the viewing environment for this view is a
+ * head-mounted display environment.
+ * In this mode, Java 3D will compute new viewpoints
+ * using that sequence of transforms appropriate to head-mounted display
+ * environments. These environments are generally head-tracked.
+ * @see #setViewPolicy
+ */
+ public static final int HMD_VIEW = 1;
+
+ /**
+ * Specifies that Java 3D should generate a parallel projection matrix
+ * for this View.
+ * @see #setProjectionPolicy
+ */
+ public static final int PARALLEL_PROJECTION = 0;
+
+ /**
+ * Specifies that Java 3D should generate a perspective projection matrix
+ * for this View.
+ * This is the default mode.
+ * @see #setProjectionPolicy
+ */
+ public static final int PERSPECTIVE_PROJECTION = 1;
+
+ /**
+ * Policy that specifies that only visible objects should be drawn.
+ * This is the default mode.
+ * @see #setVisibilityPolicy
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int VISIBILITY_DRAW_VISIBLE = 0;
+
+ /**
+ * Policy that specifies that only invisible objects should be drawn.
+ * @see #setVisibilityPolicy
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int VISIBILITY_DRAW_INVISIBLE = 1;
+
+ /**
+ * Policy that specifies that both visible and invisible objects
+ * should be drawn.
+ * @see #setVisibilityPolicy
+ *
+ * @since Java 3D 1.2
+ */
+ public static final int VISIBILITY_DRAW_ALL = 2;
+
+ /**
+ * Policy that specifies that no sorting of transparent objects
+ * is done.
+ * This is the default mode.
+ * @see #setTransparencySortingPolicy
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int TRANSPARENCY_SORT_NONE = 0;
+
+ /**
+ * Policy that specifies that transparent objects
+ * are sorted from back to front on a per-geometry basis.
+ * @see #setTransparencySortingPolicy
+ *
+ * @since Java 3D 1.3
+ */
+ public static final int TRANSPARENCY_SORT_GEOMETRY = 1;
+
+
+ //
+ // The AWT window for display.
+ //
+ // This object can be queried to obtain:
+ // screen width in pixels
+ // screen height in pixels
+ // window width in pixels
+ // window height in pixels
+ // window upper left corner location in pixels relative to screen
+ //
+ // Use getCanvases() to access this
+ private Vector canvases = new Vector(3);
+
+ //
+ // The current universe associated with this view
+ //
+ VirtualUniverse universe = null;
+
+ //
+ // The RenderBin associated with this view.
+ //
+ RenderBin renderBin = null;
+
+ // This is the SoundScheduler associated with this view.
+ SoundScheduler soundScheduler = null;
+
+ // This is the thread associated with this view.
+ SoundRenderer soundRenderer = new SoundRenderer();
+
+ // AudioDevice enumerator current position
+ // AudioDeviceEnumerator allAudioEnumerator = null;
+
+ // These are used for tracking the frame times
+ static final int NUMBER_FRAME_START_TIMES = 10;
+
+ long[] frameStartTimes = new long[NUMBER_FRAME_START_TIMES];
+ long[] frameNumbers = new long[NUMBER_FRAME_START_TIMES];
+ int currentFrameIndex = 0;
+
+ // These are the values that are set at the end of each frame
+ long currentFrameStartTime = 0;
+ long currentFrameDuration = 0;
+ long currentFrameNumber = 0;
+
+ // These are the ones that get updated directly by MC
+ long frameNumber = 0;
+ long startTime = 0;
+ long stopTime = 0;
+
+ // Support dynamic video resize -- DVR.
+ Viewer viewer = null; // Cached the associate viewer of this view.
+ boolean firstTime = true;
+ float dvrFactor = 1.0f;
+ boolean dvrResizeCompensation = true;
+
+ // User adjustable minimum frame cycle time
+ long minFrameCycleTime;
+
+ // True when stopBehaviorScheduler invoke
+ boolean stopBehavior;
+
+ //
+ // View cache for this view.
+ //
+ ViewCache viewCache = null;
+
+ // Compatibility mode related field has changed.
+ // { compatibilityModeEnable, compatVpcToEc, compatLeftProjection,
+ // compatRightProjection }
+ static final int COMPATIBILITY_MODE_DIRTY = 0x01;
+ // ScreenScalePolicy field has changed.
+ static final int SCREEN_SCALE_POLICY_DIRTY = 0x02;
+ // Screen scale field has changed.
+ static final int SCREEN_SCALE_DIRTY = 0x04;
+ // Window Resize Policy field has changed.
+ static final int WINDOW_RESIZE_POLICY_DIRTY = 0x08;
+ // View Policy eye in image plate field has changed.
+ static final int VIEW_POLICY_DIRTY = 0x10;
+ // Clip related field has changed.
+ // { frontClipDistance, backClipDistance, frontClipPolicy, backClipPolicy }
+ static final int CLIP_DIRTY = 0x20;
+ // Projection Policy field has changed.
+ static final int PROJECTION_POLICY_DIRTY = 0x40;
+ // Window Movement Policy field has changed.
+ static final int WINDOW_MOVEMENT_POLICY_DIRTY = 0x80;
+ // Window Eye Point Policy field has changed.
+ static final int WINDOW_EYE_POINT_POLICY_DIRTY = 0x100;
+ // Monoscopic View Policy field has changed.
+ static final int MONOSCOPIC_VIEW_POLICY_DIRTY = 0x200;
+ // Field Of View field has changed.
+ static final int FIELD_OF_VIEW_DIRTY = 0x400;
+ // Tracking Enable field has changed.
+ static final int TRACKING_ENABLE_DIRTY = 0x800;
+ // User Head To Vworld Enable field has changed.
+ static final int USER_HEAD_TO_VWORLD_ENABLE_DIRTY = 0x1000;
+ // coexistenceCenteringEnable flag has changed.
+ static final int COEXISTENCE_CENTERING_ENABLE_DIRTY = 0x2000;
+ // leftManualEyeInCoexistence has changed.
+ static final int LEFT_MANUAL_EYE_IN_COEXISTENCE_DIRTY = 0x4000;
+ // rightManualEyeInCoexistence has changed.
+ static final int RIGHT_MANUAL_EYE_IN_COEXISTENCE_DIRTY = 0x8000;
+ // visibilityPolicy has changed.
+ static final int VISIBILITY_POLICY_DIRTY = 0x10000;
+
+ // This is not from View object. It is here for the purpose
+ // keeping all ViewCache's dirty mask bit declaration in one place.
+ // ViewPlatformRetained viewAttach Policy field has changed.
+ static final int VPR_VIEW_ATTACH_POLICY_DIRTY = 0x10000;
+ static final int VPR_VIEWPLATFORM_DIRTY = 0x20000;
+
+ // PhysicalEnvironment fields has changed.
+ static final int PE_COE_TO_TRACKER_BASE_DIRTY = 0x100000;
+ static final int PE_TRACKING_AVAILABLE_DIRTY = 0x200000;
+ static final int PE_COE_CENTER_IN_PWORLD_POLICY_DIRTY = 0x400000;
+
+ // PhysicalBody fields has changed.
+ static final int PB_EYE_POSITION_DIRTY = 0x1000000;
+ static final int PB_EAR_POSITION_DIRTY = 0x2000000;
+ static final int PB_NOMINAL_EYE_HEIGHT_FROM_GROUND_DIRTY = 0x4000000;
+ static final int PB_NOMINAL_EYE_OFFSET_FROM_NOMINAL_SCREEN_DIRTY = 0x8000000;
+
+
+ // Mask that indicates this View's view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int vDirtyMask = (COMPATIBILITY_MODE_DIRTY | SCREEN_SCALE_POLICY_DIRTY
+ | SCREEN_SCALE_DIRTY | WINDOW_RESIZE_POLICY_DIRTY
+ | VIEW_POLICY_DIRTY | CLIP_DIRTY
+ | PROJECTION_POLICY_DIRTY | WINDOW_MOVEMENT_POLICY_DIRTY
+ | WINDOW_EYE_POINT_POLICY_DIRTY | MONOSCOPIC_VIEW_POLICY_DIRTY
+ | FIELD_OF_VIEW_DIRTY | TRACKING_ENABLE_DIRTY
+ | USER_HEAD_TO_VWORLD_ENABLE_DIRTY
+ | COEXISTENCE_CENTERING_ENABLE_DIRTY
+ | LEFT_MANUAL_EYE_IN_COEXISTENCE_DIRTY
+ | RIGHT_MANUAL_EYE_IN_COEXISTENCE_DIRTY
+ | VISIBILITY_POLICY_DIRTY);
+
+
+ //
+ // This object contains a specification of the user's physical body.
+ //
+ // Attributes of this object are defined in head coordinates and
+ // include information such as the location of the user's eyes and
+ // ears.
+ // The origin is defined to be halfway between the left and right eye
+ // in the plane of the face.
+ // The x-axis extends to the right (of the head looking out from the head).
+ // The y-axis extends up. The z-axis extends to the rear of the head.
+ //
+ PhysicalBody physicalBody;
+
+ // This object contains a specification of the physical environment.
+ PhysicalEnvironment physicalEnvironment;
+
+ // View model compatibility mode flag
+ boolean compatibilityModeEnable = false;
+
+ // View model coexistenceCenteringEnable flag
+ boolean coexistenceCenteringEnable = true;
+
+ Point3d leftManualEyeInCoexistence = new Point3d();
+ Point3d rightManualEyeInCoexistence = new Point3d();
+
+ //
+ // Indicates which major mode of view computation to use:
+ // HMD mode or screen/fish-tank-VR mode.
+ //
+ int viewPolicy = SCREEN_VIEW;
+
+ // The current projection policy (parallel versus perspective)
+ int projectionPolicy = PERSPECTIVE_PROJECTION;
+
+ //
+ // The view model's field of view.
+ //
+ double fieldOfView = 45.0 * Math.PI / 180.0;
+
+ //
+ // The distance away from the clip origin
+ // in the direction of gaze for the front and back clip planes.
+ // The default values are in meters.
+ //
+ double frontClipDistance = 0.1;
+ double backClipDistance = 10.0;
+
+ // This variable specifies where the screen scale comes from
+ int screenScalePolicy = SCALE_SCREEN_SIZE;
+
+ // The screen scale value used when the screen scale policy is
+ // SCALE_EXPLICIT
+ double screenScale = 1.0;
+
+ //
+ // This variable specifies how Java 3D modifies the view when
+ // the window is resized (VIRTUAL_WORLD or PHYSICAL_WORLD).
+ //
+ int windowResizePolicy = PHYSICAL_WORLD;
+
+ //
+ // This variable specifies how Java 3D modifies the view when
+ // the window is moved (VIRTUAL_WORLD or PHYSICAL_WORLD).
+ //
+ int windowMovementPolicy = PHYSICAL_WORLD;
+
+ //
+ // Specifies how Java 3D handles the predefined eyepoint in
+ // non-head-tracked environment (RELATIVE_TO_SCREEN,
+ // RELATIVE_TO_WINDOW, RELATIVE_TO_FIELD_OF_VIEW, or
+ // RELATIVE_TO_COEXISTENCE)
+ //
+ int windowEyepointPolicy = RELATIVE_TO_FIELD_OF_VIEW;
+
+ //
+ // Specifies how Java 3D generates monoscopic view
+ // (LEFT_EYE_VIEW, RIGHT_EYE_VIEW, or CYCLOPEAN_EYE_VIEW).
+ //
+ int monoscopicViewPolicy = CYCLOPEAN_EYE_VIEW;
+
+ /**
+ * Defines the policy for placing the front clipping plane.
+ * Legal values include PHYSICAL_EYE, PHYSICAL_SCREEN,
+ * VIRTUAL_EYE, and VIRTUAL_SCREEN.
+ */
+ int frontClipPolicy = PHYSICAL_EYE;
+
+ /**
+ * Defines the policy for placing the back clipping plane.
+ */
+ int backClipPolicy = PHYSICAL_EYE;
+
+ /**
+ * Defines the visibility policy.
+ */
+ int visibilityPolicy = VISIBILITY_DRAW_VISIBLE;
+
+ /**
+ * Defines the transparency sorting policy.
+ */
+ int transparencySortingPolicy = TRANSPARENCY_SORT_NONE;
+
+ /**
+ * Flag to enable tracking, if so allowed by the trackingAvailable flag.
+ */
+ boolean trackingEnable = false;
+
+ /**
+ * This setting enables the continuous updating by Java 3D of the
+ * userHeadToVworld transform.
+ */
+ boolean userHeadToVworldEnable = false;
+
+ /**
+ * The view platform currently associated with this view.
+ */
+ private ViewPlatform viewPlatform = null;
+
+ // The current compatibility mode view transform
+ Transform3D compatVpcToEc = new Transform3D();
+
+ // The current compatibility mode projection transforms
+ Transform3D compatLeftProjection = new Transform3D();
+ Transform3D compatRightProjection = new Transform3D();
+
+ // The long id of this view - used for dirty bit evaluation in the scene graph
+ Integer viewId = null;
+ int viewIndex = -1;
+
+ // A boolean that indicates whether or not this is the primary view
+ boolean primaryView = false;
+
+ // A boolean that indicates whether or not this view is active as
+ // seen by MasterControl
+ boolean active = false;
+
+ // A boolean that indicates whether or not this view is active as
+ // seen by this view. There is a delay before MasterControl set
+ // active flag, so a local activeStatus is used. Otherwise
+ // activate event may lost if it proceed by deactivate event
+ // but MC not yet set active to false. This happens in
+ // viewplatform detach and attach.
+ boolean activeStatus = false;
+
+ // This boolean indicates whether or not the view is running. It
+ // is used for startView/stopView
+ volatile boolean isRunning = true;
+
+ // A flag to indicate that we are in a canvas callback routine
+ boolean inCanvasCallback = false;
+
+ //
+ // Flag to enable depth buffer freeze during trasparent rendering pass
+ //
+ boolean depthBufferFreezeTransparent = true;
+
+ //
+ // Flag to enable scene antialiasing
+ //
+ boolean sceneAntialiasingEnable = false;
+
+ //
+ // Flag to enable local eye lighting
+ //
+ boolean localEyeLightingEnable = false;
+
+ // Array Lists to track the screens and canvases associated with this View.
+ // use getScreens() to access this
+ private ArrayList screenList = new ArrayList();
+
+ // use getCanvasList() to access this
+ private ArrayList canvasList = new ArrayList();
+
+ private Canvas3D[][] cachedCanvasList;
+ private Canvas3D[] cachedCanvases;
+ private Canvas3D[] cachedOffScreenCanvases;
+ private Screen3D[] cachedScreens;
+ private int longestScreenList = 0;
+ private boolean canvasesDirty = true;
+
+ // Flag to notify user thread when renderOnce is finished
+ volatile boolean renderOnceFinish = true;
+
+ // Lock to synchronize start/stop/renderOnce call
+ private Object startStopViewLock = new Object();
+
+ // Lock for evaluateActive() only. This is used to prevent
+ // using lock this which will cause deadlock when MC call
+ // snapshot which waitForMC() in user thread.
+ private Object evaluateLock = new Object();
+
+ /**
+ * use for stop view, when stopview, set to count -1,
+ * when reach 1, call stopView() in MC and reset to -1.
+ */
+ int stopViewCount = -1;
+
+ /**
+ * False if current frame cycle time less than minimum frame cycle time
+ */
+ boolean isMinCycleTimeAchieve = true;
+
+ // Time to sleep if minimum frame cycle time not achieve
+ long sleepTime = 0;
+
+ // use in pure immediate mode to tell whether this view rendering
+ // thread is added in MC renderThreadData
+ volatile boolean inRenderThreadData = false;
+
+ // use to notify MC that render bin has run once, and is ready for
+ // renderer to render
+ boolean renderBinReady = false;
+
+ // No of time setUniverse() is invoke
+ long universeCount = 0;
+
+ // The universe count when UNREGISTER_VIEW request is post,
+ // this is used to distingish whether new setUniverse() is
+ // invoked after UNREGISTER_VIEW request post to avoid
+ // resetting the newly set universe.
+ long resetUnivCount = 0;
+
+ // This notify user thread waitForMC() to continue when
+ // MC finish unregisterView
+ volatile boolean doneUnregister = false;
+
+ static final int TRANSP_SORT_POLICY_CHANGED = 0x0001;
+ static final int OTHER_ATTRS_CHANGED = 0x0002;
+
+ /**
+ * Constructs a View object with default parameters. The default
+ * values are as follows:
+ * <ul>
+ * view policy : SCREEN_VIEW<br>
+ * projection policy : PERSPECTIVE_PROJECTION<br>
+ * screen scale policy : SCALE_SCREEN_SIZE<br>
+ * window resize policy : PHYSICAL_WORLD<br>
+ * window movement policy : PHYSICAL_WORLD<br>
+ * window eyepoint policy : RELATIVE_TO_FIELD_OF_VIEW<br>
+ * monoscopic view policy : CYCLOPEAN_EYE_VIEW<br>
+ * front clip policy : PHYSICAL_EYE<br>
+ * back clip policy : PHYSICAL_EYE<br>
+ * visibility policy : VISIBILITY_DRAW_VISIBLE<br>
+ * transparency sorting policy : TRANSPARENCY_SORT_NONE<br>
+ * coexistenceCentering flag : true<br>
+ * compatibility mode : false<br>
+ * left projection : identity<br>
+ * right projection : identity<br>
+ * vpc to ec transform : identity<br>
+ * physical body : null<br>
+ * physical environment : null<br>
+ * screen scale : 1.0<br>
+ * field of view : PI/4<br>
+ * left manual eye in coexistence : (-0.033, 0.0, 0.4572)<br>
+ * right manual eye in coexistence : (0.033, 0.0, 0.4572)<br>
+ * front clip distance : 0.1<br>
+ * back clip distance : 10.0<br>
+ * tracking enable : false<br>
+ * user head to vworld enable : false<br>
+ * list of Canvas3D objects : empty<br>
+ * depth buffer freeze transparent : true<br>
+ * scene antialiasing : false<br>
+ * local eye lighting : false<br>
+ * view platform : null<br>
+ * behavior scheduler running : true<br>
+ * view running : true<br>
+ * minimum frame cycle time : 0<br>
+ * </ul>
+ */
+ public View() {
+ viewCache = new ViewCache(this);
+ }
+
+ /**
+ * Sets the policy for view computation.
+ * This variable specifies how Java 3D uses its transforms in
+ * computing new viewpoints.
+ * <UL>
+ * <LI>SCREEN_VIEW specifies that Java 3D should compute a new viewpoint
+ * using the sequence of transforms appropriate to screen-based
+ * head-tracked display environments (fish-tank VR/portals/VR-desks).
+ * </LI>
+ * <LI>HMD_VIEW specifies that Java 3D should compute a new viewpoint
+ * using the sequence of transforms appropriate to head mounted
+ * display environments.
+ * </LI>
+ * </UL>
+ * The default view policy is SCREEN_VIEW.
+ * @param policy the new policy, one of SCREEN_VIEW or HMD_VIEW
+ * @exception IllegalArgumentException if policy is a value other than
+ * SCREEN_VIEW or HMD_VIEW
+ * @exception IllegalStateException if the specified policy
+ * is HMD_VIEW and if any canvas associated with this view is
+ * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW
+ */
+ public void setViewPolicy(int policy) {
+ if (policy != HMD_VIEW &&
+ policy != SCREEN_VIEW) {
+
+ throw new IllegalArgumentException(J3dI18N.getString("View0"));
+ }
+ if(policy == HMD_VIEW) {
+ // Check the following :
+ // 1) If the view is in HMD mode and there exists a canvas in
+ // CYCLOPEAN_EYE_VIEW mode then throw exception.
+ synchronized (canvasList) {
+ for (int i=canvases.size()-1; i>=0; i--) {
+ Canvas3D c3d = (Canvas3D)canvases.elementAt(i);
+
+ if ((c3d.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
+ (!c3d.useStereo)){
+ throw new
+ IllegalStateException(J3dI18N.getString("View31"));
+ }
+ }
+ }
+ }
+ synchronized(this) {
+ this.viewPolicy = policy;
+ vDirtyMask |= View.VIEW_POLICY_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Retrieves the current view computation policy for this View.
+ * @return one of: SCREEN_VIEW or HMD_VIEW.
+ */
+ public int getViewPolicy() {
+ return this.viewPolicy;
+ }
+
+ /**
+ * Sets the projection policy for this View.
+ * This variable specifies the type of projection transform that
+ * will be generated. A value of PARALLEL_PROJECTION specifies that
+ * a parallel projection transform is generated. A value of
+ * PERSPECTIVE_PROJECTION specifies that
+ * a perspective projection transform is generated.
+ * The default projection policy is PERSPECTIVE.
+ * @param policy the new policy, one of PARALLEL_PROJECTION or
+ * PERSPECTIVE_PROJECTION
+ * @exception IllegalArgumentException if policy is a value other than
+ * PARALLEL_PROJECTION or PERSPECTIVE_PROJECTION
+ */
+ public void setProjectionPolicy(int policy) {
+ if (policy != PERSPECTIVE_PROJECTION &&
+ policy != PARALLEL_PROJECTION) {
+
+ throw new IllegalArgumentException(J3dI18N.getString("View1"));
+ }
+ synchronized(this) {
+ this.projectionPolicy = policy;
+ vDirtyMask |= View.PROJECTION_POLICY_DIRTY;
+ }
+
+ repaint();
+ }
+
+ /**
+ * Retrieves the current projection policy for this View.
+ * @return one of: PARALLEL_PROJECTION or PERSPECTIVE_PROJECTION.
+ */
+ public int getProjectionPolicy() {
+ return this.projectionPolicy;
+ }
+
+ /**
+ * Sets the screen scale policy for this view.
+ * This policy specifies how the screen scale is derived.
+ * The value is either SCALE_SCREEN_SIZE or SCALE_EXPLICIT.
+ * A value of SCALE_SCREEN_SIZE specifies that the scale is derived
+ * from the size of the physical screen. A value of SCALE_EXPLICIT
+ * specifies that the scale is taken directly from the screenScale
+ * parameter.
+ * The default screen scale policy is SCALE_SCREEN_SIZE.
+ * @param policy the new policy, one of SCALE_SCREEN_SIZE or
+ * SCALE_EXPLICIT.
+ */
+ public void setScreenScalePolicy(int policy) {
+
+ synchronized(this) {
+ this.screenScalePolicy = policy;
+ vDirtyMask |= View.SCREEN_SCALE_POLICY_DIRTY;
+ }
+
+ repaint();
+ }
+
+ /**
+ * Returns the current screen scale policy, one of:
+ * SCALE_SCREEN_SIZE or SCALE_EXPLICIT.
+ * @return the current screen scale policy
+ */
+ public int getScreenScalePolicy() {
+ return this.screenScalePolicy;
+ }
+
+ /**
+ * Sets the window resize policy.
+ * This variable specifies how Java 3D modifies the view when
+ * users resize windows. The variable can contain one of
+ * VIRTUAL_WORLD or PHYSICAL_WORLD.
+ * A value of VIRTUAL_WORLD implies that the original image
+ * remains the same size on the screen but the user sees more
+ * or less of the virtual world depending on whether the window
+ * grew or shrank in size.
+ * A value of PHYSICAL_WORLD implies that the original image
+ * continues to fill the window in the same way using more or
+ * less pixels depending on whether the window grew or shrank
+ * in size.
+ * The default window resize policy is PHYSICAL_WORLD.
+ * @param policy the new policy, one of VIRTUAL_WORLD or PHYSICAL_WORLD
+ */
+ public void setWindowResizePolicy(int policy) {
+
+ synchronized(this) {
+ this.windowResizePolicy = policy;
+ vDirtyMask |= View.WINDOW_RESIZE_POLICY_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns the current window resize policy, one of:
+ * VIRTUAL_WORLD or PHYSICAL_WORLD.
+ * @return the current window resize policy
+ */
+ public int getWindowResizePolicy() {
+ return this.windowResizePolicy;
+ }
+
+ /**
+ * Sets the window movement policy.
+ * This variable specifies what part of the virtual world Java 3D
+ * draws as a function of window placement on the screen. The
+ * variable can contain one of VIRTUAL_WORLD or PHYSICAL_WORLD.
+ * A value of VIRTUAL_WORLD implies that the image seen in the
+ * window changes as the position of the window shifts on the
+ * screen. (This mode acts as if the window were a window into
+ * the virtual world.)
+ * A value of PHYSICAL_WORLD implies that the image seen in the
+ * window remains the same no matter where the user positions
+ * the window on the screen.
+ * The default window movement policy is PHYSICAL_WORLD.
+ * @param policy the new policy, one of VIRTUAL_WORLD or PHYSICAL_WORLD
+ */
+ public void setWindowMovementPolicy(int policy) {
+
+ synchronized(this) {
+ this.windowMovementPolicy = policy;
+ vDirtyMask |= View.WINDOW_MOVEMENT_POLICY_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns the current window movement policy,
+ * one of: VIRTUAL_WORLD or PHYSICAL_WORLD.
+ * @return the current window movement policy
+ */
+ public int getWindowMovementPolicy() {
+ return this.windowMovementPolicy;
+ }
+
+ /**
+ * Sets the view model's window eyepoint policy.
+ * This variable specifies how Java 3D handles the predefined eye
+ * point in a non-head-tracked environment. The variable can contain
+ * one of:
+ * <UL>
+ * <LI>RELATIVE_TO_SCREEN, Java 3D should interpret the
+ * given fixed-eyepoint position as relative to the screen (this
+ * implies that the view frustum shape will change whenever a
+ * user moves the location of a window on the screen).
+ * </LI>
+ * <LI>RELATIVE_TO_WINDOW, Java 3D should interpret the
+ * given fixed-eyepoint position as relative to the window. In this
+ * mode, the X and Y values are taken as the center of the window and
+ * the Z value is taken from the canvas eyepoint position.
+ * </LI>
+ * <LI>RELATIVE_TO_FIELD_OF_VIEW, Java 3D should
+ * modify the position of the eyepoint to match any changes in field
+ * of view (the view frustum will change whenever the application
+ * program changes the field of view).
+ * </LI>
+ * <LI>RELATIVE_TO_COEXISTENCE, Java 3D should interpret the eye's
+ * position in coexistence coordinates. In this mode, the eye position
+ * is taken from the view (rather than the Canvas3D) and transformed from
+ * coexistence coordinates to image plate coordinates for each
+ * Canvas3D. The resulting eye position is relative to the screen (this
+ * implies that the view frustum shape will change whenever a
+ * user moves the location of a window on the screen).
+ * </LI>
+ * </UL>
+ * The default window eyepoint policy is RELATIVE_TO_FIELD_OF_VIEW.
+ * @param policy the new policy, one of RELATIVE_TO_SCREEN,
+ * RELATIVE_TO_WINDOW, RELATIVE_TO_FIELD_OF_VIEW, or
+ * RELATIVE_TO_COEXISTENCE
+ */
+ public void setWindowEyepointPolicy(int policy) {
+ synchronized(this) {
+ this.windowEyepointPolicy = policy;
+ vDirtyMask |= View.WINDOW_EYE_POINT_POLICY_DIRTY;
+ }
+
+ repaint();
+ }
+
+ /**
+ * Returns the current window eyepoint policy, one of:
+ * RELATIVE_TO_SCREEN, RELATIVE_TO_WINDOW, RELATIVE_TO_FIELD_OF_VIEW or
+ * RELATIVE_TO_COEXISTENCE.
+ * @return the current window eyepoint policy
+ */
+ public int getWindowEyepointPolicy() {
+ return this.windowEyepointPolicy;
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>Canvas3D.setMonoscopicViewPolicy</code>
+ */
+ public void setMonoscopicViewPolicy(int policy) {
+ synchronized(this) {
+ this.monoscopicViewPolicy = policy;
+ vDirtyMask |= View.MONOSCOPIC_VIEW_POLICY_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>Canvas3D.getMonoscopicViewPolicy</code>
+ */
+ public int getMonoscopicViewPolicy() {
+ return this.monoscopicViewPolicy;
+ }
+
+ /**
+ * Sets the coexistenceCentering enable flag to true or false.
+ * If the coexistenceCentering flag is true, the center of
+ * coexistence in image plate coordinates, as specified by the
+ * trackerBaseToImagePlate transform, is translated to the center
+ * of either the window or the screen in image plate coordinates,
+ * according to the value of windowMovementPolicy.
+ *
+ * <p>
+ * By default, coexistenceCentering is enabled. It should be
+ * disabled if the trackerBaseToImagePlate calibration transform
+ * is set to a value other than the identity (for example, when
+ * rendering to multiple screens or when head tracking is
+ * enabled). This flag is ignored for HMD mode, or when the
+ * coexistenceCenterInPworldPolicy is <i>not</i>
+ * NOMINAL_SCREEN.
+ *
+ * @param flag the new coexistenceCentering enable flag
+ *
+ * @since Java 3D 1.2
+ */
+ public void setCoexistenceCenteringEnable(boolean flag) {
+ synchronized(this) {
+ this.coexistenceCenteringEnable = flag;
+ vDirtyMask |= View.COEXISTENCE_CENTERING_ENABLE_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Retrieves the coexistenceCentering enable flag.
+ *
+ * @return the current coexistenceCentering enable flag
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean getCoexistenceCenteringEnable() {
+ return this.coexistenceCenteringEnable;
+ }
+
+ /**
+ * Sets the compatibility mode enable flag to true or false.
+ * Compatibility mode is disabled by default.
+ * @param flag the new compatibility mode enable flag
+ */
+ public void setCompatibilityModeEnable(boolean flag) {
+ synchronized(this) {
+ this.compatibilityModeEnable = flag;
+ vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Retrieves the compatibility mode enable flag.
+ * @return the current compatibility mode enable flag
+ */
+ public boolean getCompatibilityModeEnable() {
+ return this.compatibilityModeEnable;
+ }
+
+ /**
+ * Compatibility mode method that specifies a viewing frustum for
+ * the left eye that transforms points in Eye Coordinates (EC) to
+ * Clipping Coordinates (CC).
+ * If compatibility mode is disabled, then this transform is not used;
+ * the actual projection is derived from other values.
+ * In monoscopic mode, only the left eye projection matrix is used.
+ * @param projection the new left eye projection transform
+ * @exception RestrictedAccessException if compatibility mode is disabled.
+ */
+ public void setLeftProjection(Transform3D projection) {
+ if (!compatibilityModeEnable) {
+ throw new RestrictedAccessException(J3dI18N.getString("View2"));
+ }
+
+ synchronized(this) {
+ compatLeftProjection.setWithLock(projection);
+ vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Compatibility mode method that specifies a viewing frustum for
+ * the right eye that transforms points in Eye Coordinates (EC) to
+ * Clipping Coordinates (CC).
+ * If compatibility mode is disabled, then this transform is not used;
+ * the actual projection is derived from other values.
+ * In monoscopic mode, the right eye projection matrix is ignored.
+ * @param projection the new right eye projection transform
+ * @exception RestrictedAccessException if compatibility mode is disabled.
+ */
+ public void setRightProjection(Transform3D projection) {
+ if (!compatibilityModeEnable) {
+ throw new RestrictedAccessException(J3dI18N.getString("View2"));
+ }
+
+ synchronized(this) {
+ compatRightProjection.setWithLock(projection);
+ vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
+ }
+
+ repaint();
+ }
+
+ /**
+ * Compatibility mode method that retrieves the current
+ * compatibility mode projection transform for the left eye and
+ * places it into the specified object.
+ * @param projection the Transform3D object that will receive the
+ * projection
+ * @exception RestrictedAccessException if compatibility mode is disabled.
+ */
+ public void getLeftProjection(Transform3D projection) {
+ if (!compatibilityModeEnable) {
+ throw new RestrictedAccessException(J3dI18N.getString("View4"));
+ }
+
+ projection.set(compatLeftProjection);
+ }
+
+ /**
+ * Compatibility mode method that retrieves the current
+ * compatibility mode projection transform for the right eye and
+ * places it into the specified object.
+ * @param projection the Transform3D object that will receive the
+ * projection
+ * @exception RestrictedAccessException if compatibility mode is disabled.
+ */
+ public void getRightProjection(Transform3D projection) {
+ if (!compatibilityModeEnable) {
+ throw new RestrictedAccessException(J3dI18N.getString("View4"));
+ }
+
+ projection.set(compatRightProjection);
+ }
+
+ /**
+ * Compatibility mode method that specifies the ViewPlatform
+ * Coordinates (VPC) to Eye Coordinates (EC) transform.
+ * If compatibility mode is disabled, then this transform
+ * is derived from other values and is read-only.
+ * @param vpcToEc the new VPC to EC transform
+ * @exception RestrictedAccessException if compatibility mode is disabled.
+ * @exception BadTransformException if the transform is not affine.
+ */
+ public void setVpcToEc(Transform3D vpcToEc) {
+ if (!compatibilityModeEnable) {
+ throw new RestrictedAccessException(J3dI18N.getString("View6"));
+ }
+
+ if (!vpcToEc.isAffine()) {
+ throw new BadTransformException(J3dI18N.getString("View7"));
+ }
+
+ synchronized(this) {
+ compatVpcToEc.setWithLock(vpcToEc);
+ vDirtyMask |= View.COMPATIBILITY_MODE_DIRTY;
+ }
+
+ repaint();
+ }
+
+ /**
+ * Compatibility mode method that retrieves the current
+ * ViewPlatform Coordinates (VPC) system to
+ * Eye Coordinates (EC) transform and copies it into the specified
+ * object.
+ * @param vpcToEc the object that will receive the vpcToEc transform.
+ * @exception RestrictedAccessException if compatibility mode is disabled.
+ */
+ public void getVpcToEc(Transform3D vpcToEc) {
+ if (!compatibilityModeEnable) {
+ throw new RestrictedAccessException(J3dI18N.getString("View8"));
+ }
+
+ vpcToEc.set(compatVpcToEc);
+ }
+
+ /**
+ * Sets the view model's physical body to the PhysicalBody object provided.
+ * Java 3D uses the parameters in the PhysicalBody to ensure accurate
+ * image and sound generation when in head-tracked mode.
+ * @param physicalBody the new PhysicalBody object
+ */
+ public void setPhysicalBody(PhysicalBody physicalBody) {
+ // need to synchronize variable activateStatus
+ synchronized (canvasList) {
+ if (activeStatus) {
+ if (this.physicalBody != null) {
+ this.physicalBody.removeUser(this);
+ }
+ physicalBody.addUser(this);
+ }
+ }
+ this.physicalBody = physicalBody;
+ repaint();
+ }
+
+ /**
+ * Returns a reference to the view model's PhysicalBody object.
+ * @return the view object's PhysicalBody object
+ */
+ public PhysicalBody getPhysicalBody() {
+ return this.physicalBody;
+ }
+
+ /**
+ * Sets the view model's physical environment to the PhysicalEnvironment
+ * object provided.
+ * @param physicalEnvironment the new PhysicalEnvironment object
+ */
+ public void setPhysicalEnvironment(PhysicalEnvironment physicalEnvironment) {
+ synchronized (canvasList) {
+ if (activeStatus) {
+ if (this.physicalEnvironment != null) {
+ this.physicalEnvironment.removeUser(this);
+ }
+ physicalEnvironment.addUser(this);
+ }
+ }
+ this.physicalEnvironment = physicalEnvironment;
+
+
+ if ((viewPlatform != null) && viewPlatform.isLive()) {
+ VirtualUniverse.mc.postRequest(MasterControl.PHYSICAL_ENV_CHANGE, this);
+ }
+ repaint();
+ }
+
+ /**
+ * Returns a reference to the view model's PhysicalEnvironment object.
+ * @return the view object's PhysicalEnvironment object
+ */
+ public PhysicalEnvironment getPhysicalEnvironment() {
+ return this.physicalEnvironment;
+ }
+
+ /**
+ * Sets the screen scale value for this view.
+ * This is used when the screen scale policy is SCALE_EXPLICIT.
+ * The default value is 1.0 (i.e., unscaled).
+ * @param scale the new screen scale
+ */
+ public void setScreenScale(double scale) {
+ synchronized(this) {
+ this.screenScale = scale;
+ vDirtyMask |= View.SCREEN_SCALE_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns the current screen scale value
+ * @return the current screen scale value
+ */
+ public double getScreenScale() {
+ return this.screenScale;
+ }
+
+ /**
+ * Sets the field of view used to compute the projection transform.
+ * This is used when head tracking is disabled and when the Canvas3D's
+ * windowEyepointPolicy is RELATIVE_TO_FIELD_OF_VIEW.
+ * @param fieldOfView the new field of view in radians
+ */
+ public void setFieldOfView(double fieldOfView) {
+ synchronized(this) {
+ this.fieldOfView = fieldOfView;
+ vDirtyMask |= View.FIELD_OF_VIEW_DIRTY;
+ }
+ repaint();
+
+ }
+
+ /**
+ * Returns the current field of view.
+ * @return the current field of view in radians
+ */
+ public double getFieldOfView() {
+ return this.fieldOfView;
+ }
+
+
+ /**
+ * Sets the position of the manual left eye in coexistence
+ * coordinates. This value determines eye placement when a head
+ * tracker is not in use and the application is directly controlling
+ * the eye position in coexistence coordinates. This value is
+ * ignored when in head-tracked mode or when the
+ * windowEyePointPolicy is <i>not</i> RELATIVE_TO_COEXISTENCE.
+ *
+ * @param position the new manual left eye position
+ *
+ * @since Java 3D 1.2
+ */
+ public void setLeftManualEyeInCoexistence(Point3d position) {
+ synchronized(this) {
+ leftManualEyeInCoexistence.set(position);
+ vDirtyMask |= View.LEFT_MANUAL_EYE_IN_COEXISTENCE_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Sets the position of the manual right eye in coexistence
+ * coordinates. This value determines eye placement when a head
+ * tracker is not in use and the application is directly controlling
+ * the eye position in coexistence coordinates. This value is
+ * ignored when in head-tracked mode or when the
+ * windowEyePointPolicy is <i>not</i> RELATIVE_TO_COEXISTENCE.
+ *
+ * @param position the new manual right eye position
+ *
+ * @since Java 3D 1.2
+ */
+ public void setRightManualEyeInCoexistence(Point3d position) {
+ synchronized(this) {
+ rightManualEyeInCoexistence.set(position);
+ vDirtyMask |= View.RIGHT_MANUAL_EYE_IN_COEXISTENCE_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Retrieves the position of the user-specified, manual left eye
+ * in coexistence
+ * coordinates and copies that value into the object provided.
+ * @param position the object that will receive the position
+ *
+ * @since Java 3D 1.2
+ */
+ public void getLeftManualEyeInCoexistence(Point3d position) {
+ position.set(leftManualEyeInCoexistence);
+ }
+
+ /**
+ * Retrieves the position of the user-specified, manual right eye
+ * in coexistence
+ * coordinates and copies that value into the object provided.
+ * @param position the object that will receive the position
+ *
+ * @since Java 3D 1.2
+ */
+ public void getRightManualEyeInCoexistence(Point3d position) {
+ position.set(rightManualEyeInCoexistence);
+ }
+
+
+ /**
+ * Sets the view model's front clip distance.
+ * This value specifies the distance away from the eyepoint
+ * in the direction of gaze where objects stop disappearing.
+ * Objects closer to the eye than the front clip
+ * distance are not drawn. The default value is 0.1 meters.
+ * <p>
+ * There are several considerations that need to be taken into
+ * account when choosing values for the front and back clip
+ * distances.
+ * <ul>
+ * <li>The front clip distance must be greater than
+ * 0.0 in physical eye coordinates.</li>
+ * <li>The front clipping plane must be in front of the
+ * back clipping plane, that is, the front clip distance
+ * must be less than the back clip distance in physical eye
+ * coordinates.</li>
+ * <li>The front and back clip distances, in physical
+ * eye coordinates, must be less than the largest positive
+ * single-precision floating point value, <code>Float.MAX_VALUE</code>.
+ * In practice, since these physical eye coordinate distances are in
+ * meters, the values should be <i>much</i> less than that.
+ * <li>The ratio of the back distance divided by the front distance,
+ * in physical eye coordinates, affects Z-buffer precision. This
+ * ratio should be less than about 3000 in order to accommodate 16-bit
+ * Z-buffers. Values of 100 to less than 1000 will produce better
+ * results.</li>
+ * </ul>
+ * Violating any of the above rules will result in undefined
+ * behavior. In many cases, no picture will be drawn.
+ *
+ * @param distance the new front clip distance
+ * @see #setBackClipDistance
+ */
+ public void setFrontClipDistance(double distance) {
+ synchronized(this) {
+ this.frontClipDistance = distance;
+ vDirtyMask |= View.CLIP_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns the view model's front clip distance.
+ * @return the current front clip distance
+ */
+ public double getFrontClipDistance() {
+ return this.frontClipDistance;
+ }
+
+ /**
+ * Sets the view model's back clip distance.
+ * The parameter specifies the distance from the eyepoint
+ * in the direction of gaze to where objects begin disappearing.
+ * Objects farther away from the eye than the
+ * back clip distance are not drawn.
+ * The default value is 10.0 meters.
+ * <p>
+ * There are several considerations that need to be taken into
+ * account when choosing values for the front and back clip
+ * distances. These are enumerated in the description of
+ * <a href=#setFrontClipDistance(double)>setFrontClipDistance</a>.
+ * <p>
+ * Note that this attribute is only used if there is no Clip node
+ * that is in scope of the view platform associated with this view.
+ * @param distance the new back clip distance
+ * @see #setFrontClipDistance
+ * @see Clip#setBackDistance
+ */
+ public void setBackClipDistance(double distance) {
+ synchronized(this) {
+ this.backClipDistance = distance;
+ vDirtyMask |= View.CLIP_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns the view model's back clip distance.
+ * @return the current back clip distance
+ */
+ public double getBackClipDistance() {
+ return this.backClipDistance;
+ }
+
+ /**
+ * Retrieves the user-head to virtual-world transform
+ * and copies that value into the transform provided.
+ * @param t the Transform3D object that will receive the transform
+ */
+ public void getUserHeadToVworld(Transform3D t) {
+
+ if( userHeadToVworldEnable ) {
+
+ // get the calculated userHeadToVworld transform
+ // from the view cache.
+ // grab the first canvas -- not sure for multiple canvases
+ Canvas3D canvas = (Canvas3D) this.canvases.firstElement();
+ synchronized(canvas.canvasViewCache) {
+ t.set(canvas.canvasViewCache.getHeadToVworld());
+ }
+ }else {
+ throw new RestrictedAccessException(J3dI18N.getString("View9"));
+ }
+ }
+
+ /**
+ * Sets the view model's front clip policy, the policy Java 3D uses
+ * in computing where to place the front clip plane. The variable
+ * can contain one of:
+ * <UL>
+ * <LI>VIRTUAL_EYE, to specify that the associated distance is
+ * from the eye and in units of virtual distance
+ * </LI>
+ * <LI>PHYSICAL_EYE, to specify that the associated distance is
+ * from the eye and in units of physical distance (meters)
+ * </LI>
+ * <LI>VIRTUAL_SCREEN, to specify that the associated distance is
+ * from the screen and in units of virtual distance
+ * </LI>
+ * <LI>PHYSICAL_SCREEN, to specify that the associated distance is
+ * from the screen and in units of physical distance (meters)
+ * </LI>
+ * </UL>
+ * The default front clip policy is PHYSICAL_EYE.
+ * @param policy the new policy, one of PHYSICAL_EYE, PHYSICAL_SCREEN,
+ * VIRTUAL_EYE, or VIRTUAL_SCREEN
+ */
+ public void setFrontClipPolicy(int policy) {
+ synchronized(this) {
+ this.frontClipPolicy = policy;
+ vDirtyMask |= View.CLIP_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns the view model's current front clip policy.
+ * @return one of:
+ * VIRTUAL_EYE, PHYSICAL_EYE, VIRTUAL_SCREEN, or PHYSICAL_SCREEN
+ */
+ public int getFrontClipPolicy() {
+ return this.frontClipPolicy;
+ }
+
+ /**
+ * Sets the view model's back clip policy, the policy Java 3D uses
+ * in computing where to place the back clip plane. The variable
+ * can contain one of:
+ * <UL>
+ * <LI>VIRTUAL_EYE, to specify that the associated distance is
+ * from the eye and in units of virtual distance
+ * </LI>
+ * <LI>PHYSICAL_EYE, to specify that the associated distance is
+ * from the eye and in units of physical distance (meters)
+ * </LI>
+ * <LI>VIRTUAL_SCREEN, to specify that the associated distance is
+ * from the screen and in units of virtual distance
+ * </LI>
+ * <LI>PHYSICAL_SCREEN, to specify that the associated distance is
+ * from the screen and in units of physical distance (meters)
+ * </LI>
+ * </UL>
+ * The default back clip policy is PHYSICAL_EYE.
+ * @param policy the new policy, one of PHYSICAL_EYE, PHYSICAL_SCREEN,
+ * VIRTUAL_EYE, or VIRTUAL_SCREEN
+ */
+ public void setBackClipPolicy(int policy) {
+ synchronized(this) {
+ this.backClipPolicy = policy;
+ vDirtyMask |= View.CLIP_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns the view model's current back clip policy.
+ * @return one of:
+ * VIRTUAL_EYE, PHYSICAL_EYE, VIRTUAL_SCREEN, or PHYSICAL_SCREEN
+ */
+ public int getBackClipPolicy() {
+ return this.backClipPolicy;
+ }
+
+ /**
+ * Sets the visibility policy for this view. This attribute
+ * is one of:
+ * <UL>
+ * <LI>VISIBILITY_DRAW_VISIBLE, to specify that only visible objects
+ * are drawn.
+ * </LI>
+ * <LI>VISIBILITY_DRAW_INVISIBLE, to specify that only invisible objects
+ * are drawn.
+ * </LI>
+ * <LI>VISIBILITY_DRAW_ALL, to specify that both visible and
+ * invisible objects are drawn.
+ * </LI>
+ * </UL>
+ * The default visibility policy is VISIBILITY_DRAW_VISIBLE.
+ *
+ * @param policy the new policy, one of VISIBILITY_DRAW_VISIBLE,
+ * VISIBILITY_DRAW_INVISIBLE, or VISIBILITY_DRAW_ALL.
+ *
+ * @see RenderingAttributes#setVisible
+ *
+ * @since Java 3D 1.2
+ */
+ public void setVisibilityPolicy(int policy) {
+
+ synchronized(this) {
+ this.visibilityPolicy = policy;
+ vDirtyMask |= View.VISIBILITY_POLICY_DIRTY;
+ }
+
+ if (activeStatus && isRunning) {
+
+ J3dMessage vpMessage = VirtualUniverse.mc.getMessage();
+ vpMessage.universe = universe;
+ vpMessage.view = this;
+ vpMessage.type = J3dMessage.UPDATE_VIEW;
+ vpMessage.threads = J3dThread.UPDATE_RENDER;
+ vpMessage.args[0] = this;
+ synchronized(((ViewPlatformRetained)viewPlatform.retained).sphere) {
+ vpMessage.args[1] = new Float(((ViewPlatformRetained)viewPlatform.
+ retained).sphere.radius);
+ }
+ vpMessage.args[2] = new Integer(OTHER_ATTRS_CHANGED);
+ vpMessage.args[3] = new Integer(transparencySortingPolicy);
+ VirtualUniverse.mc.processMessage(vpMessage);
+ }
+ }
+
+ /**
+ * Retrieves the current visibility policy.
+ * @return one of:
+ * VISIBILITY_DRAW_VISIBLE,
+ * VISIBILITY_DRAW_INVISIBLE, or VISIBILITY_DRAW_ALL.
+ *
+ * @since Java 3D 1.2
+ */
+ public int getVisibilityPolicy() {
+ return this.visibilityPolicy;
+ }
+
+ /**
+ * Sets the transparency sorting policy for this view. This attribute
+ * is one of:
+ *
+ * <UL>
+ * <LI>TRANSPARENCY_SORT_NONE, to specify that no depth sorting of
+ * transparent objects is performed. Transparent objects are
+ * drawn after opaque objects, but are not sorted from back to
+ * front.</LI>
+ *
+ * <LI>TRANSPARENCY_SORT_GEOMETRY, to specify that transparent
+ * objects are depth-sorted on a per-geometry basis. Each
+ * geometry object of each transparent Shape3D node is drawn from
+ * back to front. Note that this policy will not split geometry
+ * into smaller pieces, so intersecting or intertwined objects may
+ * not be sorted correctly.</LI>
+ * </UL>
+ *
+ * The default policy is TRANSPARENCY_SORT_NONE.
+ *
+ * @param policy the new policy, one of TRANSPARENCY_SORT_NONE
+ * or TRANSPARENCY_SORT_GEOMETRY.
+ *
+ * @since Java 3D 1.3
+ */
+ public void setTransparencySortingPolicy(int policy) {
+ if (policy == transparencySortingPolicy) {
+ return;
+ }
+
+ transparencySortingPolicy = policy;
+ if (activeStatus && isRunning) {
+
+ J3dMessage vpMessage = VirtualUniverse.mc.getMessage();
+ vpMessage.universe = universe;
+ vpMessage.view = this;
+ vpMessage.type = J3dMessage.UPDATE_VIEW;
+ vpMessage.threads = J3dThread.UPDATE_RENDER;
+ vpMessage.args[0] = this;
+ vpMessage.args[1] = null;
+ vpMessage.args[2] = new Integer(TRANSP_SORT_POLICY_CHANGED);
+ vpMessage.args[3] = new Integer(policy);
+ VirtualUniverse.mc.processMessage(vpMessage);
+ }
+ }
+
+ /**
+ * Retrieves the current transparency sorting policy.
+ * @return one of:
+ * TRANSPARENCY_SORT_NONE or TRANSPARENCY_SORT_GEOMETRY.
+ *
+ * @since Java 3D 1.3
+ */
+ public int getTransparencySortingPolicy() {
+ return this.transparencySortingPolicy;
+ }
+
+ /**
+ * Turns head tracking on or off for this view.
+ * @param flag specifies whether head tracking is enabled or
+ * disabled for this view
+ */
+ public void setTrackingEnable(boolean flag) {
+
+ synchronized(this) {
+ this.trackingEnable = flag;
+ vDirtyMask |= View.TRACKING_ENABLE_DIRTY;
+ }
+
+ repaint();
+ }
+
+ /**
+ * Returns a status flag indicating whether or not head tracking
+ * is enabled.
+ * @return a flag telling whether head tracking is enabled
+ */
+ public boolean getTrackingEnable() {
+ return this.trackingEnable;
+ }
+
+ /**
+ * Turns on or off the continuous
+ * updating of the userHeadToVworld transform.
+ * @param flag enables or disables continuous updating
+ */
+ public void setUserHeadToVworldEnable(boolean flag) {
+
+ synchronized(this) {
+ userHeadToVworldEnable = flag;
+ vDirtyMask |= View.USER_HEAD_TO_VWORLD_ENABLE_DIRTY;
+ }
+ repaint();
+ }
+
+ /**
+ * Returns a status flag indicating whether or not
+ * Java 3D is continuously updating the userHeadToVworldEnable transform.
+ * @return a flag indicating if continuously updating userHeadToVworld
+ */
+ public boolean getUserHeadToVworldEnable() {
+ return userHeadToVworldEnable;
+ }
+
+ /**
+ * Computes the sensor to virtual-world transform
+ * and copies that value into the transform provided.
+ * The computed transforms takes points in the sensor's coordinate
+ * system and produces the point's corresponding value in
+ * virtual-world coordinates.
+ * @param sensor the sensor in question
+ * @param t the object that will receive the transform
+ */
+ public void getSensorToVworld(Sensor sensor, Transform3D t) {
+ // grab the first canvas -- not sure for multiple canvases
+ Canvas3D canvas = (Canvas3D) this.canvases.firstElement();
+ Transform3D localTrans = new Transform3D();
+ synchronized(canvas.canvasViewCache) {
+ t.set(canvas.canvasViewCache.getVworldToTrackerBase());
+ }
+ t.invert();
+ sensor.getRead(localTrans);
+ t.mul(localTrans);
+ }
+
+ /**
+ * Retrieves the position of the specified Sensor's
+ * hotspot in virtual-world coordinates
+ * and copies that value into the position provided.
+ * This value is derived from other values and is read-only.
+ * @param sensor the sensor in question
+ * @param position the variable that will receive the position
+ */
+ public void getSensorHotspotInVworld(Sensor sensor,
+ Point3f position) {
+
+ Canvas3D canvas = (Canvas3D) this.canvases.firstElement();
+ Transform3D sensorToVworld = new Transform3D();
+ Point3d hotspot3d = new Point3d();
+
+ getSensorToVworld(sensor, sensorToVworld);
+ sensor.getHotspot(hotspot3d);
+ position.set(hotspot3d);
+ sensorToVworld.transform(position);
+ }
+
+ /**
+ * Retrieves the position of the specified Sensor's
+ * hotspot in virtual-world coordinates
+ * and copies that value into the position provided.
+ * This value is derived from other values and is read-only.
+ * @param sensor the sensor in question
+ * @param position the variable that will receive the position
+ */
+ public void getSensorHotspotInVworld(Sensor sensor,
+ Point3d position) {
+
+ Canvas3D canvas = (Canvas3D) this.canvases.firstElement();
+ Transform3D sensorToVworld = new Transform3D();
+
+ getSensorToVworld(sensor, sensorToVworld);
+ sensor.getHotspot(position);
+ sensorToVworld.transform(position);
+ }
+
+ /**
+ * Sets given Canvas3D at the given index position.
+ * @param canvas3D the given Canvas3D to be set
+ * @param index the position to be set
+ * @exception IllegalStateException if the specified canvas is
+ * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW,
+ * and the viewPolicy for this view is HMD_VIEW
+ * @exception IllegalSharingException if the specified canvas is
+ * associated with another view
+ */
+ public void setCanvas3D(Canvas3D canvas3D, int index) {
+
+ if((viewPolicy == HMD_VIEW) &&
+ (canvas3D.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
+ (!canvas3D.useStereo)){
+
+ throw new
+ IllegalStateException(J3dI18N.getString("View31"));
+ }
+
+ Canvas3D cv;
+
+ synchronized(canvasList) {
+ if (canvas3D.getView() != null)
+ throw new IllegalSharingException(J3dI18N.getString("View10"));
+ cv = (Canvas3D) canvases.elementAt(index);
+ canvases.setElementAt(canvas3D, index);
+ removeFromCanvasList(cv);
+ addToCanvasList(canvas3D);
+ canvasesDirty = true;
+ }
+
+ canvas3D.setView(this);
+ cv.setView(null);
+
+ if (canvas3D.added) {
+ evaluateActive();
+ }
+ if (cv.added) {
+ evaluateActive();
+ }
+
+ }
+
+ /**
+ * Gets the Canvas3D at the specified index position.
+ * @param index the position from which to get Canvas3D object
+ * @return the Canvas3D at the sprcified index position
+ */
+ public Canvas3D getCanvas3D(int index){
+ return (Canvas3D) this.canvases.elementAt(index);
+ }
+
+ /**
+ * Gets the enumeration object of all the Canvas3Ds.
+ * @return the enumeration object of all the Canvas3Ds.
+ */
+ public Enumeration getAllCanvas3Ds(){
+ return canvases.elements();
+ }
+
+ /**
+ * Returns the number of Canvas3Ds in this View.
+ * @return the number of Canvas3Ds in this View
+ *
+ * @since Java 3D 1.2
+ */
+ public int numCanvas3Ds() {
+ return canvases.size();
+ }
+
+ /**
+ * Adds the given Canvas3D at the end of the list.
+ * @param canvas3D the Canvas3D to be added
+ * @exception IllegalStateException if the specified canvas is
+ * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW,
+ * and the viewPolicy for this view is HMD_VIEW
+ * @exception IllegalSharingException if the specified canvas is
+ * associated with another view
+ */
+ public void addCanvas3D(Canvas3D canvas3D){
+
+ if((viewPolicy == HMD_VIEW) &&
+ (canvas3D.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
+ (!canvas3D.useStereo)) {
+ throw new
+ IllegalStateException(J3dI18N.getString("View31"));
+ }
+
+ synchronized(canvasList) {
+ if (canvas3D.getView() != null)
+ throw new IllegalSharingException(J3dI18N.getString("View10"));
+ canvases.addElement(canvas3D);
+ addToCanvasList(canvas3D);
+ canvasesDirty = true;
+ }
+
+ canvas3D.setView(this);
+
+ if (canvas3D.added) {
+ if ((canvas3D.visible || canvas3D.offScreen) &&
+ canvas3D.firstPaintCalled) {
+ canvas3D.active = true;
+ }
+ evaluateActive();
+ }
+ }
+
+ /**
+ * Inserts the Canvas3D at the given index position.
+ * @param canvas3D the Canvas3D to be inserted
+ * @param index the position to be inserted at
+ * @exception IllegalStateException if the specified canvas is
+ * a stereo canvas with a monoscopicEyePolicy of CYCLOPEAN_EYE_VIEW,
+ * and the viewPolicy for this view is HMD_VIEW
+ * @exception IllegalSharingException if the specified canvas is
+ * associated with another view
+ */
+ public void insertCanvas3D(Canvas3D canvas3D, int index){
+
+ if((viewPolicy == HMD_VIEW) &&
+ (canvas3D.monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) &&
+ (!canvas3D.useStereo)) {
+ throw new
+ IllegalStateException(J3dI18N.getString("View31"));
+ }
+
+ synchronized(canvasList) {
+ if (canvas3D.getView() != null)
+ throw new IllegalSharingException(J3dI18N.getString("View10"));
+ this.canvases.insertElementAt(canvas3D, index);
+ addToCanvasList(canvas3D);
+ canvasesDirty = true;
+ }
+
+ canvas3D.setView(this);
+
+ if (canvas3D.added) {
+ if ((canvas3D.visible || canvas3D.offScreen) &&
+ canvas3D.firstPaintCalled) {
+ canvas3D.active = true;
+ }
+ evaluateActive();
+ }
+ }
+
+ /**
+ * Removes the Canvas3D from the given index position.
+ * @param index the position of Canvas3D object to be removed
+ */
+ public void removeCanvas3D(int index) {
+ // index -1 is possible if the view is unregistered first
+ // because viewPlatform is clearLived,
+ // and then removeCanvas from the view
+ if (index == -1)
+ return;
+
+ Canvas3D cv;
+
+ synchronized(canvasList) {
+ cv = (Canvas3D) canvases.elementAt(index);
+
+ canvases.removeElementAt(index);
+ removeFromCanvasList(cv);
+ canvasesDirty = true;
+ }
+
+ // reset canvas will set view to null also
+ VirtualUniverse.mc.postRequest(MasterControl.RESET_CANVAS,
+ cv);
+ cv.pendingView = null;
+
+ computeCanvasesCached();
+
+ if (cv.added) {
+ cv.active = false;
+ evaluateActive();
+ }
+ if (universe != null) {
+ universe.waitForMC();
+ }
+ }
+
+
+ /**
+ * Retrieves the index of the specified Canvas3D in
+ * this View's list of Canvas3Ds
+ *
+ * @param canvas3D the Canvas3D to be looked up.
+ * @return the index of the specified Canvas3D;
+ * returns -1 if the object is not in the list.
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfCanvas3D(Canvas3D canvas3D) {
+ return canvases.indexOf(canvas3D);
+ }
+
+
+ /**
+ * Removes the specified Canvas3D from this View's
+ * list of Canvas3Ds.
+ * If the specified object is not in the list, the list is not modified.
+ *
+ * @param canvas3D the Canvas3D to be removed.
+ */
+ public void removeCanvas3D(Canvas3D canvas3D) {
+ removeCanvas3D(canvases.indexOf(canvas3D));
+ }
+
+
+ /**
+ * Removes all Canvas3Ds from this View.
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllCanvas3Ds() {
+ synchronized(canvasList) {
+ int numCanvases = canvases.size();
+
+ // Remove in reverse order to ensure valid indices
+ for (int index = numCanvases - 1; index >= 0; index--) {
+ Canvas3D cv;
+
+ cv = (Canvas3D) canvases.elementAt(index);
+
+ canvases.removeElementAt(index);
+ removeFromCanvasList(cv);
+ canvasesDirty = true;
+
+ // reset canvas will set view to null also
+ VirtualUniverse.mc.postRequest(MasterControl.RESET_CANVAS,
+ cv);
+ cv.pendingView = null;
+
+ if (cv.added) {
+ cv.active = false;
+ }
+ }
+
+ computeCanvasesCached();
+ }
+
+ evaluateActive();
+
+ if (universe != null) {
+ universe.waitForMC();
+ }
+ }
+
+
+ // This adds this canvas and its screen to the screen list.
+ // Locks are already acquired before this is called.
+ private void addToCanvasList(Canvas3D c) {
+
+ for (int i=screenList.size()-1; i>=0; i--) {
+ if ((Screen3D)screenList.get(i) == c.screen) {
+ // This is the right screen slot
+ ((ArrayList)canvasList.get(i)).add(c);
+ canvasesDirty = true;
+ return;
+ }
+ }
+
+ // Add a screen slot
+ screenList.add(c.screen);
+ ArrayList clist = new ArrayList();
+ canvasList.add(clist);
+ clist.add(c);
+ canvasesDirty = true;
+ }
+
+ // This removes this canvas and its screen from the screen list
+ // Locks are already acquired before this is called.
+ private void removeFromCanvasList(Canvas3D c) {
+
+ for (int i=screenList.size()-1; i>=0; i--) {
+ if ((Screen3D) screenList.get(i) == c.screen) {
+ // This is the right screen slot
+ ArrayList clist = (ArrayList)canvasList.get(i);
+ clist.remove(clist.indexOf(c));
+
+ if (clist.size() == 0) {
+ canvasList.remove(i);
+ screenList.remove(i);
+ canvasesDirty = true;
+ }
+ return;
+ }
+ }
+ }
+
+ // Locks are already acquired before this is called.
+ void computeCanvasesCached() {
+
+ synchronized (canvasList) {
+ ArrayList cv;
+ int len = canvases.size();
+ int numOffScreenCanvases = 0;
+
+ Canvas3D newCachedCanvases[] = new Canvas3D[len];
+ for (int i=0; i < len; i++) {
+ newCachedCanvases[i] = (Canvas3D) canvases.get(i);
+ if (newCachedCanvases[i].offScreen)
+ numOffScreenCanvases++;
+ }
+ // Do this in one instruction so there is no need to
+ // synchronized getCanvases()
+
+ if (numOffScreenCanvases > 0) {
+ cachedOffScreenCanvases = new Canvas3D[numOffScreenCanvases];
+ numOffScreenCanvases = 0;
+ }
+
+ cachedCanvases = newCachedCanvases;
+ len = 0;
+ longestScreenList = 0;
+ cachedCanvasList = new Canvas3D[canvasList.size()][0];
+ for (int i=0; i < cachedCanvasList.length; i++) {
+ cv = (ArrayList) canvasList.get(i);
+ len = cv.size();
+ cachedCanvasList[i] = new Canvas3D[len];
+ for (int j=0; j < len; j++) {
+ cachedCanvasList[i][j] = (Canvas3D) cv.get(j);
+ }
+
+ if (cachedCanvasList[i][0].offScreen) {
+ for (int j = 0; j < len; j++) {
+ cachedOffScreenCanvases[numOffScreenCanvases++]=
+ cachedCanvasList[i][j];
+ }
+ }
+
+ if (len > longestScreenList) {
+ longestScreenList = len;
+ }
+ }
+ len = screenList.size();
+ Screen3D newCachedScreens[] = new Screen3D[len];
+
+ for (int i=0; i < len; i++) {
+ newCachedScreens[i] = (Screen3D) screenList.get(i);
+ }
+ // Do this in one instruction so there is no need to
+ // synchronized getScreens()
+ cachedScreens = newCachedScreens;
+ canvasesDirty = false;
+ }
+ }
+
+ // This creates a 2 dimentional list of canvases
+ // ONLY MC can call this procedure with canCompute=true,
+ // since MC want the result return by
+ // evaluateCanvases and updateWorkThreads agree to each other,
+ // so only evaluateCanvases can compute a new list.
+ // Other threads should use getCanvasList(false).
+ Canvas3D[][] getCanvasList(boolean canCompute) {
+ if (canvasesDirty && canCompute) {
+ computeCanvasesCached();
+ }
+ return cachedCanvasList;
+ }
+
+ // assume getCanvasList is called before
+ int getLongestScreenList() {
+ return longestScreenList;
+ }
+
+ // assume getCanvasList is called before
+ Canvas3D[] getCanvases() {
+ return cachedCanvases;
+ }
+
+ Canvas3D[] getOffScreenCanvases() {
+ return cachedOffScreenCanvases;
+ }
+
+ // assume getCanvasList is called before
+ Screen3D[] getScreens() {
+ return cachedScreens;
+ }
+
+ Canvas3D getFirstCanvas() {
+ synchronized (canvasList) {
+ if (canvases.size() > 0) {
+ return (Canvas3D) canvases.elementAt(0);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * This method returns the time at which the most recent rendering
+ * frame started. It is defined as the number of milliseconds
+ * since January 1, 1970 00:00:00 GMT.
+ * Since multiple canvases might be attached to this View,
+ * the start of a frame is defined as the point in time just prior
+ * to clearing any canvas attached to this view.
+ * @return the time at which the most recent rendering frame started
+ */
+ public long getCurrentFrameStartTime() {
+ synchronized (frameStartTimes) {
+ return currentFrameStartTime;
+ }
+ }
+
+ /**
+ * This method returns the duration, in milliseconds, of the most
+ * recently completed rendering frame. The time taken to render
+ * all canvases attached to this view is measured. This duration
+ * is computed as the difference between the start of the most recently
+ * completed frame and the end of that frame.
+ * Since multiple canvases might be attached to this View,
+ * the start of a frame is defined as the point in time just prior
+ * to clearing any canvas attached to this view--before preRender
+ * is called for any canvas. Similarly, the end of a frame is
+ * defined as the point in time just after swapping the buffer for
+ * all canvases--after postSwap is called for all canvases.
+ * Note that since the frame duration is measured from start to stop
+ * for this view only, the value returned is not the same as
+ * frame rate; it measures only the rendering time for this view.
+ *
+ * @return the duration, in milliseconds, of the most recently
+ * completed rendering frame
+ */
+ public long getLastFrameDuration() {
+ synchronized (frameStartTimes) {
+ return currentFrameDuration;
+ }
+ }
+
+ /**
+ * This method returns the frame number for this view. The frame
+ * number starts at 0 and is incremented at the start of each
+ * frame--prior to clearing all the canvases attached to this
+ * view.
+ *
+ * @return the current frame number for this view
+ */
+ public long getFrameNumber() {
+ synchronized (frameStartTimes) {
+ return currentFrameNumber;
+ }
+ }
+
+ /**
+ * Retrieves the implementation-dependent maximum number of
+ * frames whose start times will be recorded by the system. This
+ * value is guaranteed to be at least 10 for all implementations
+ * of the Java 3D API.
+ * @return the maximum number of frame start times recorded
+ */
+ public static int getMaxFrameStartTimes() {
+ return (NUMBER_FRAME_START_TIMES);
+ }
+
+ /**
+ * Copies the last <i>k</i> frame start time values into
+ * the user-specified array. The most recent frame start time is
+ * copied to location 0 of the array, the next most recent frame
+ * start time is copied into location 1 of the array, and so forth.
+ * If times.length is smaller than
+ * maxFrameStartTimes, then only the last times.length values are
+ * copied. If times.length is greater than maxFrameStartTimes,
+ * then all array elements after index maxFrameStartTimes-1 are
+ * set to 0.
+ *
+ * @return the frame number of the most recent frame in the array
+ *
+ * @see #setMinimumFrameCycleTime
+ */
+ public long getFrameStartTimes(long[] times) {
+ int index, i, loopCount;
+ long lastFrameNumber;
+
+ synchronized (frameStartTimes) {
+ index = currentFrameIndex - 1;
+ if (index < 0) {
+ index = NUMBER_FRAME_START_TIMES - 1;
+ }
+ lastFrameNumber = frameNumbers[index];
+
+ if (times.length <= NUMBER_FRAME_START_TIMES) {
+ loopCount = times.length;
+ } else {
+ loopCount = NUMBER_FRAME_START_TIMES;
+ }
+
+ for (i=0; i<loopCount; i++) {
+ times[i] = frameStartTimes[index];
+ index--;
+ if (index < 0) {
+ index = NUMBER_FRAME_START_TIMES - 1;
+ }
+ }
+
+ if (times.length > NUMBER_FRAME_START_TIMES) {
+ for (; i<times.length; i++) {
+ times[i] = 0;
+ }
+ }
+ }
+
+ return (lastFrameNumber);
+ }
+
+ /**
+ * Sets the minimum frame cycle time, in milliseconds, for this
+ * view. The Java 3D renderer will ensure that the time between
+ * the start of each successive frame is at least the specified
+ * number of milliseconds. The default value is 0.
+ *
+ * @param minimumTime the minimum number of milliseconds between
+ * successive frames
+ *
+ * @exception IllegalArgumentException if <code>minimumTime < 0</code>
+ *
+ * @see #getFrameStartTimes
+ *
+ * @since Java 3D 1.2
+ */
+ public void setMinimumFrameCycleTime(long minimumTime) {
+ if (minimumTime < 0L)
+ throw new IllegalArgumentException(J3dI18N.getString("View27"));
+
+ minFrameCycleTime = minimumTime;
+ VirtualUniverse.mc.setWork();
+ }
+
+ /**
+ * Retrieves the minimum frame cycle time, in milliseconds, for this view.
+ * @return the minimum frame cycle time for this view.
+ *
+ * @see #getFrameStartTimes
+ *
+ * @since Java 3D 1.2
+ */
+ public long getMinimumFrameCycleTime() {
+ return minFrameCycleTime;
+ }
+
+
+ /**
+ * This adds a frame time to the this of frame times
+ */
+ void setFrameTimingValues() {
+
+ synchronized (frameStartTimes) {
+ if (currentFrameIndex == NUMBER_FRAME_START_TIMES) {
+ currentFrameIndex = 0;
+ }
+
+ frameNumbers[currentFrameIndex] = frameNumber;
+
+ frameStartTimes[currentFrameIndex++] = startTime;
+ currentFrameStartTime = startTime;
+ currentFrameDuration = stopTime - startTime;
+ currentFrameNumber = frameNumber;
+ }
+ }
+
+ /**
+ * Return true if maximum fps impose by user reach
+ */
+ void computeCycleTime() {
+ if (minFrameCycleTime == 0) {
+ isMinCycleTimeAchieve = true;
+ sleepTime = 0;
+ } else {
+ sleepTime = minFrameCycleTime -
+ (System.currentTimeMillis() - startTime);
+ isMinCycleTimeAchieve = (sleepTime <= 0);
+ }
+ }
+
+
+ /**
+ * Enables or disables automatic freezing of the depth buffer for
+ * objects rendered
+ * during the transparent rendering pass (i.e., objects rendered
+ * using alpha blending) for this view.
+ * If enabled, depth buffer writes will be disabled during the
+ * transparent rendering pass regardless of the value of
+ * the depth buffer write enable flag in the RenderingAttributes
+ * object for a particular node.
+ * This flag is enabled by default.
+ * @param flag indicates whether automatic freezing of the depth buffer
+ * for transparent/antialiased objects is enabled.
+ * @see RenderingAttributes#setDepthBufferWriteEnable
+ */
+ public void setDepthBufferFreezeTransparent(boolean flag) {
+ depthBufferFreezeTransparent = flag;
+ repaint();
+ }
+
+ /**
+ * Retrieves the current value of the depth buffer freeze transparent
+ * flag for this view.
+ * @return a flag that indicates whether or not the depth
+ * buffer is automatically frozen during the transparent rendering pass.
+ */
+ public boolean getDepthBufferFreezeTransparent() {
+ return depthBufferFreezeTransparent;
+ }
+
+ /**
+ * Enables or disables scene antialiasing for this view.
+ * If enabled, the entire scene will be antialiased on
+ * each canvas in which scene antialiasing is available.
+ * Scene antialiasing is disabled by default.
+ * <p>
+ * NOTE: Scene antialiasing is ignored in pure immediate mode,
+ * but is supported in mixed-immediate mode.
+ * @param flag indicates whether scene antialiasing is enabled
+ *
+ * @see Canvas3D#queryProperties
+ */
+ public void setSceneAntialiasingEnable(boolean flag) {
+ sceneAntialiasingEnable = flag;
+ repaint();
+ }
+
+ /**
+ * Returns a flag that indicates whether or not scene antialiasing
+ * is enabled for this view.
+ * @return a flag that indicates whether scene antialiasing is enabled
+ */
+ public boolean getSceneAntialiasingEnable() {
+ return sceneAntialiasingEnable;
+ }
+
+ /**
+ * Sets a flag that indicates whether the local eyepoint is used in
+ * lighting calculations for perspective projections.
+ * If this flag is set to true, the view vector is calculated per-vertex
+ * based on the direction from the actual eyepoint to the vertex.
+ * If this flag is set to false, a single view vector is computed from
+ * the eyepoint to the center of the view frustum. This is
+ * called infinite eye lighting.
+ * Local eye lighting is disabled by default, and is ignored for
+ * parallel projections.
+ * @param flag indicates whether local eye lighting is enabled
+ */
+ public void setLocalEyeLightingEnable(boolean flag) {
+ localEyeLightingEnable = flag;
+ repaint();
+ }
+
+ /**
+ * Retrieves a flag that indicates whether or not local eye lighting
+ * is enabled for this view.
+ * @return a flag that indicates whether local eye lighting is enabled
+ */
+ public boolean getLocalEyeLightingEnable() {
+ return localEyeLightingEnable;
+ }
+
+ /**
+ * Attach viewPlatform structure to this view.
+ * @param vp the viewPlatform to be attached
+ */
+ public void attachViewPlatform(ViewPlatform vp) {
+
+ if ((vp != null) && (vp == viewPlatform)) {
+ return;
+ }
+
+ if (viewPlatform != null) {
+ ((ViewPlatformRetained)viewPlatform.retained).removeView(this);
+ if (viewPlatform.isLive()) {
+ synchronized (evaluateLock) {
+ viewPlatform = null;
+ // cleanup View stuff for the old platform
+ evaluateActive();
+ viewPlatform = vp;
+ }
+ if (universe != null) {
+ universe.waitForMC();
+ }
+ } else {
+ viewPlatform = vp;
+ }
+ } else {
+ viewPlatform = vp;
+ }
+ if (vp != null) {
+ if (vp.isLive()) {
+ checkView();
+ setUniverse(((ViewPlatformRetained)vp.retained).universe);
+ }
+ ((ViewPlatformRetained)vp.retained).setView(this);
+ }
+
+ evaluateActive();
+ if ((vp == null) && (universe != null)) {
+ universe.waitForMC();
+ }
+ }
+
+ /**
+ * Retrieves the currently attached ViewPlatform object
+ * @return the currently attached ViewPlatform
+ */
+ public ViewPlatform getViewPlatform() {
+ return viewPlatform;
+ }
+
+ /**
+ * Checks view parameters for consistency
+ */
+ void checkView() {
+ if (physicalBody == null)
+ throw new IllegalStateException(J3dI18N.getString("View13"));
+
+ if (physicalEnvironment == null)
+ throw new IllegalStateException(J3dI18N.getString("View14"));
+ }
+
+
+ /**
+ * Stops the behavior scheduler after all
+ * currently scheduled behaviors are executed. Any frame-based
+ * behaviors scheduled to wake up on the next frame will be
+ * executed at least once before the behavior scheduler is
+ * stopped.
+ * <p>
+ * NOTE: This is a heavy-weight method
+ * intended for verification and image capture (recording); it
+ * is <i>not</i> intended to be used for flow control.
+ * @return a pair of integers that specify the beginning and ending
+ * time (in milliseconds since January 1, 1970 00:00:00 GMT)
+ * of the behavior scheduler's last pass
+ * @exception IllegalStateException if this method is called
+ * from a Behavior method or from any Canvas3D render callback
+ * method
+ */
+ public final long[] stopBehaviorScheduler() {
+ long[] intervalTime = new long[2];
+
+ if (checkBehaviorSchedulerState("View15", "View16")) {
+ if (activeStatus && isRunning &&
+ (universe.behaviorScheduler != null)) {
+ // view is active
+ universe.behaviorScheduler.stopBehaviorScheduler(intervalTime);
+ } else {
+ if ((universe != null) &&
+ (universe.behaviorScheduler != null)) {
+ universe.behaviorScheduler.userStop = true;
+ }
+ }
+ }
+ stopBehavior = true;
+ return intervalTime;
+ }
+
+ /**
+ * Starts the behavior scheduler running after it has been stopped.
+ * @exception IllegalStateException if this method is called
+ * from a Behavior method or from any Canvas3D render callback
+ * method
+ */
+ public final void startBehaviorScheduler() {
+ if (checkBehaviorSchedulerState("View17", "View18")) {
+ if (activeStatus && isRunning &&
+ (universe.behaviorScheduler != null)) {
+ universe.behaviorScheduler.startBehaviorScheduler();
+
+ } else {
+ if ((universe != null) &&
+ (universe.behaviorScheduler != null)) {
+ universe.behaviorScheduler.userStop = false;
+ }
+ }
+ }
+
+ stopBehavior = false;
+ }
+
+ /**
+ * Check if BehaviorScheduler is in valid state to start/stop
+ * itself.
+ * @param s1 Exception String if method is called from a Canvas3D
+ * @param s2 Exception String if method is called from a Behavior method
+ * @return true if viewPlatform is live
+ * @exception IllegalStateException if this method is called
+ * from a Behavior method or from any Canvas3D render callback
+ * method
+ *
+ */
+ boolean checkBehaviorSchedulerState(String s1, String s2) {
+ Thread me = Thread.currentThread();
+
+ if (inCanvasCallback) {
+ synchronized (canvasList) {
+ for (int i=canvases.size()-1; i>=0; i--) {
+ if (((Canvas3D)canvases.elementAt(i)).screen.renderer == me) {
+ throw new IllegalStateException(J3dI18N.getString(s1));
+ }
+ }
+ }
+ }
+
+ if ((viewPlatform != null) && viewPlatform.isLive()) {
+ if (universe.inBehavior && (universe.behaviorScheduler == me)) {
+ throw new IllegalStateException(J3dI18N.getString(s2));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves a flag that indicates whether the behavior scheduler is
+ * currently running.
+ * @return true if the behavior scheduler is running, false otherwise
+ * @exception IllegalStateException if this method is called
+ * from a Behavior method or from any Canvas3D render callback
+ * method
+ */
+ public final boolean isBehaviorSchedulerRunning() {
+ return (((universe != null) && !stopBehavior &&
+ (universe.behaviorScheduler != null)) ?
+ !universe.behaviorScheduler.userStop : false);
+ }
+
+ /**
+ * Stops traversing the scene graph for this
+ * view after the current state of the scene graph is reflected on
+ * all canvases attached to this view. The renderers associated
+ * with these canvases are also stopped.
+ * <p>
+ * NOTE: This is a heavy-weight method
+ * intended for verification and image capture (recording); it
+ * is <i>not</i> intended to be used for flow control.
+ * @exception IllegalStateException if this method is called
+ * from a Behavior method or from any Canvas3D render callback
+ * method
+ */
+ public final void stopView() {
+ checkViewState("View19", "View20");
+ synchronized (startStopViewLock) {
+ if (activeStatus && isRunning) {
+ VirtualUniverse.mc.postRequest(MasterControl.STOP_VIEW, this);
+ while (isRunning) {
+ MasterControl.threadYield();
+ }
+ } else {
+ isRunning = false;
+ }
+ }
+ }
+
+ /**
+ * Starts
+ * traversing this view, and starts the renderers associated
+ * with all canvases attached to this view.
+ * @exception IllegalStateException if this method is called
+ * from a Behavior method or from any Canvas3D render callback
+ * method
+ */
+ public final void startView() {
+
+ checkViewState("View21", "View22");
+ synchronized (startStopViewLock) {
+ if (activeStatus && !isRunning) {
+ VirtualUniverse.mc.postRequest(MasterControl.START_VIEW, this);
+ while (!isRunning) {
+ MasterControl.threadYield();
+ }
+ VirtualUniverse.mc.sendRunMessage(this,
+ J3dThread.RENDER_THREAD);
+ } else {
+ isRunning = true;
+ }
+ }
+
+ }
+
+ /**
+ * This will throw IllegalStateException if not in valid state
+ * for start/stop request.
+ */
+ void checkViewState(String s1, String s2) throws IllegalStateException {
+ if (inCanvasCallback) {
+ Thread me = Thread.currentThread();
+ synchronized (canvasList) {
+ for (int i= canvases.size()-1; i>=0; i--) {
+ Canvas3D cv = (Canvas3D)canvases.elementAt(i);
+ if (cv.screen.renderer == me) {
+ throw new
+ IllegalStateException(J3dI18N.getString(s1));
+ }
+ }
+ }
+ }
+
+ if ((viewPlatform != null) && viewPlatform.isLive()) {
+ if (universe.inBehavior &&
+ (Thread.currentThread() == universe.behaviorScheduler)) {
+ throw new IllegalStateException(J3dI18N.getString(s2));
+ }
+ }
+ }
+
+ /**
+ * Retrieves a flag that indicates whether the traverser is
+ * currently running on this view.
+ * @return true if the traverser is running, false otherwise
+ * @exception IllegalStateException if this method is called
+ * from a Behavior method or from any Canvas3D render callback
+ * method
+ */
+ public final boolean isViewRunning() {
+ return isRunning;
+ }
+
+ /**
+ * Renders one frame for a stopped View. Functionally, this
+ * method is equivalent to <code>startView()</code> followed by
+ * <code>stopview()</code>, except that it is atomic, which
+ * guarantees that only one frame is rendered.
+ *
+ * @exception IllegalStateException if this method is called from
+ * a Behavior method or from any Canvas3D render callback, or if
+ * the view is currently running.
+ *
+ * @since Java 3D 1.2
+ */
+ public void renderOnce() {
+ checkViewState("View28", "View29");
+ synchronized (startStopViewLock) {
+ if (isRunning) {
+ throw new IllegalStateException(J3dI18N.getString("View30"));
+ }
+ renderOnceFinish = false;
+ VirtualUniverse.mc.postRequest(MasterControl.RENDER_ONCE, this);
+ while (!renderOnceFinish) {
+ MasterControl.threadYield();
+ }
+ renderOnceFinish = true;
+ }
+ }
+
+ /**
+ * Requests that this View be scheduled for rendering as soon as
+ * possible. The repaint method may return before the frame has
+ * been rendered. If the view is stopped, or if the view is
+ * continuously running (for example, due to a free-running
+ * interpolator), this method will have no effect. Most
+ * applications will not need to call this method, since any
+ * update to the scene graph or to viewing parameters will
+ * automatically cause all affected views to be rendered.
+ *
+ * @since Java 3D 1.2
+ */
+ public void repaint() {
+ if (activeStatus && isRunning) {
+ VirtualUniverse.mc.sendRunMessage(this,
+ J3dThread.RENDER_THREAD);
+ }
+ }
+
+
+ /**
+ * Update the view cache associated with this view. Also, shapshot
+ * the per-screen parameters associated with all screens attached
+ * to this view.
+ */
+ final void updateViewCache() {
+
+
+ // DVR support
+ // This is a back door in j3d to provide DVR support.
+ // A better place to put this code segment is in
+ // ViewCache.snapshot(). Since it consists of some
+ // back door code, I've decided to put it here to isolate
+ // it from the rest of view snapshot code.
+ if(firstTime) {
+ // System.out.println("View : First Time is " + firstTime);
+ // viewer = Viewer.getViewer(this);
+ // Since we've the handler to the viewer, we can remove the entry
+ // now to avoid confusion and prevent memory leak problem.
+ viewer = Viewer.removeViewerMapEntry(this);
+ firstTime = false;
+ }
+
+ if(viewer != null) {
+ if(viewer.isDvrEnabled()) {
+ dvrFactor = viewer.getDvrFactor();
+ dvrResizeCompensation =
+ viewer.getDvrResizeCompensationEnable();
+ /*
+ System.out.println("View : dvrFactor is " + dvrFactor);
+ System.out.println("View : dvrResizeCompensation is " +
+ dvrResizeCompensation);
+ */
+ }
+ else {
+ // Reset back to default.
+ dvrFactor = 1.0f;
+ dvrResizeCompensation = true;
+
+ }
+ }
+ // End of back door -- DVR.
+
+ synchronized(this) {
+ viewCache.snapshot();
+ viewCache.computeDerivedData();
+ }
+
+ // Just take the brute force approach and snapshot the
+ // parameters for each screen attached to each canvas. We won't
+ // worry about whether a screen is cached more than once.
+ // Eventually, dirty bits will take care of this.
+
+ synchronized (canvasList) {
+ int i = canvases.size()-1;
+ while (i>=0) {
+ Screen3D scr =
+ ((Canvas3D)canvases.elementAt(i--)).getScreen3D();
+ if (scr != null)
+ scr.updateViewCache();
+ }
+ }
+ }
+
+
+ /**
+ * This routine activates or deactivates a view based on various information
+ */
+ void evaluateActive() {
+
+ synchronized (evaluateLock) {
+ if (universe == null) {
+ return;
+ }
+
+ if ((viewPlatform == null) ||
+ !viewPlatform.isLive() ||
+ !((ViewPlatformRetained)viewPlatform.retained).switchState.currentSwitchOn) {
+ if (activeStatus) {
+ deactivate();
+ activeStatus = false;
+ }
+ // Destroy threads from MC
+ if (VirtualUniverse.mc.isRegistered(this) &&
+ (universe.isEmpty() ||
+ (canvases.isEmpty() &&
+ ((viewPlatform == null) ||
+ !viewPlatform.isLive())))) {
+ // We can't wait until MC finish unregister view
+ // here because user thread may
+ // holds the universe.sceneGraphLock if branch
+ // or locale remove in clearLive(). In this way
+ // There is deadlock since MC also need need
+ // sceneGraphLock in some threads
+ // (e.g. TransformStructure update thread)
+ universe.unRegViewWaiting = this;
+ resetUnivCount = universeCount;
+ VirtualUniverse.mc.postRequest(
+ MasterControl.UNREGISTER_VIEW, this);
+ }
+ } else {
+
+ // We're on a live view platform. See what the canvases say
+ // If view not register, MC will register it automatically
+
+ int i;
+ VirtualUniverse u = null;
+ synchronized (canvasList) {
+
+ for (i=canvases.size()-1; i>=0; i--) {
+ Canvas3D cv = (Canvas3D)canvases.elementAt(i);
+ if (cv.active) {
+
+ if (!activeStatus && (universeCount > resetUnivCount)) {
+ u = universe;
+ }
+ break;
+ }
+ }
+ }
+
+ // We should do this outside canvasList lock,
+ // otherwise it may cause deadlock with MC
+ if (u != null) {
+ activate(u);
+ activeStatus = true;
+ return;
+ }
+
+
+ if ((i < 0) && activeStatus) {
+ deactivate();
+ activeStatus = false;
+ return;
+ }
+
+ if (VirtualUniverse.mc.isRegistered(this)) {
+ // notify MC that canvases state for this view changed
+ VirtualUniverse.mc.postRequest(
+ MasterControl.REEVALUATE_CANVAS, this);
+ }
+ }
+ }
+ }
+
+ void setUniverse(VirtualUniverse universe) {
+
+ synchronized (VirtualUniverse.mc.requestObjList) {
+ if ((renderBin == null) ||
+ (renderBin.universe != universe)) {
+ if (renderBin != null) {
+ renderBin.cleanup();
+ }
+ renderBin = new RenderBin(universe, this);
+ renderBin.universe = universe;
+ }
+
+
+ if ((soundScheduler == null) ||
+ (soundScheduler.universe != universe)) {
+ // create a sound scheduler for this view, with this universe
+ if (soundScheduler != null) {
+ soundScheduler.cleanup();
+ }
+ soundScheduler = new SoundScheduler(universe, this);
+ }
+
+
+ // This has to be the last call before
+ // RenderBin and SoundScheduler construct. Since it is
+ // possible that canvas receive paint call and invoked
+ // evaluateActive in another thread - which check for
+ // universe == null and may let it pass before soundScheduler
+ // and renderBin initialize.
+ universeCount++;
+ this.universe = universe;
+ }
+ evaluateActive();
+ }
+
+ /**
+ * This activates all traversers and renderers associated with this view.
+ */
+ void activate(VirtualUniverse universe) {
+
+ universe.checkForEnableEvents();
+
+ if (physicalBody != null) {
+ physicalBody.addUser(this);
+ }
+
+ if (!VirtualUniverse.mc.isRegistered(this)) {
+ universe.regViewWaiting = this;
+ }
+
+ VirtualUniverse.mc.postRequest(MasterControl.ACTIVATE_VIEW,
+ this);
+
+ if (!universe.isSceneGraphLock) {
+ universe.waitForMC();
+ }
+ if (soundScheduler != null) {
+ soundScheduler.reset(this);
+ }
+
+ J3dMessage vpMessage = VirtualUniverse.mc.getMessage();
+ vpMessage.universe = universe;
+ vpMessage.view = this;
+ vpMessage.type = J3dMessage.UPDATE_VIEW;
+ vpMessage.threads =
+ J3dThread.SOUND_SCHEDULER |
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_BEHAVIOR;
+ vpMessage.args[0] = this;
+ synchronized(((ViewPlatformRetained)viewPlatform.retained).sphere) {
+ vpMessage.args[1] = new Float(((ViewPlatformRetained)viewPlatform.retained).sphere.radius);
+ }
+ vpMessage.args[2] = new Integer(OTHER_ATTRS_CHANGED);
+ vpMessage.args[3] = new Integer(transparencySortingPolicy);
+ VirtualUniverse.mc.processMessage(vpMessage);
+ }
+
+ /**
+ * This deactivates all traversers and renderers associated with this view.
+ */
+ void deactivate() {
+ VirtualUniverse.mc.postRequest(MasterControl.DEACTIVATE_VIEW, this);
+ if (physicalBody != null) {
+ physicalBody.removeUser(this);
+ }
+
+ // This is a temporary fix for bug 4267395
+ // TODO:cleanup in RenderBin after View detach
+ // universe.addViewIdToFreeList(viewId);
+
+ J3dMessage vpMessage = VirtualUniverse.mc.getMessage();
+ vpMessage.universe = universe;
+ vpMessage.view = this;
+ vpMessage.type = J3dMessage.UPDATE_VIEW;
+ vpMessage.threads =
+ J3dThread.SOUND_SCHEDULER |
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_BEHAVIOR;
+ vpMessage.args[0] = this;
+ if (viewPlatform != null) {
+ synchronized(((ViewPlatformRetained)viewPlatform.retained).sphere) {
+ vpMessage.args[1] = new Float(((ViewPlatformRetained)viewPlatform.retained).sphere.radius);
+ }
+ } else {
+ vpMessage.args[1] = new Float(0);
+ }
+ vpMessage.args[2] = new Integer(OTHER_ATTRS_CHANGED);
+ vpMessage.args[3] = new Integer(transparencySortingPolicy);
+ VirtualUniverse.mc.processMessage(vpMessage);
+
+ }
+
+ void cleanupViewId() {
+ universe.addViewIdToFreeList(viewId);
+ viewId = null;
+ }
+
+
+ void assignViewId () {
+ if (viewId == null) {
+ viewId = universe.getViewId();
+ viewIndex = viewId.intValue();
+ }
+ }
+
+ /**
+ * This method passes window event to SoundScheduler
+ */
+ void sendEventToSoundScheduler(AWTEvent evt) {
+ if (soundScheduler != null) {
+ soundScheduler.receiveAWTEvent(evt);
+ }
+ }
+
+ void reset() {
+
+ for (int i=0; i < canvases.size(); i++) {
+ ((Canvas3D) canvases.get(i)).reset();
+ }
+
+ // reset the renderBinReady flag
+ renderBinReady = false;
+
+ soundScheduler.cleanup();
+ soundScheduler = null;
+ soundRenderer = new SoundRenderer();
+
+ viewCache = new ViewCache(this);
+ getCanvasList(true);
+ cleanupViewId();
+ renderBin.cleanup();
+ renderBin = null;
+ universe = null;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ViewCache.java b/src/classes/share/javax/media/j3d/ViewCache.java
new file mode 100644
index 0000000..c04883c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ViewCache.java
@@ -0,0 +1,343 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ViewCache class is used to cache all data, both API data and derived
+ * data, that is independent of the Canvas3D and Screen3D.
+ */
+class ViewCache extends Object {
+ // The view associated with this view cache
+ View view;
+
+ //
+ // API/INPUT DATA
+ //
+
+ // *********************
+ // * From ViewPlatform *
+ // *********************
+ int viewAttachPolicy;
+
+ // *********************
+ // * From PhysicalBody *
+ // *********************
+
+ /**
+ * The user's left eye's position in head coordinates.
+ */
+ Point3d leftEyePosInHead = new Point3d();
+
+ /**
+ * The user's right eye's position in head coordinates.
+ */
+ Point3d rightEyePosInHead = new Point3d();
+
+ /**
+ * The user's left ear's position in head coordinates.
+ */
+ Point3d leftEarPosInHead = new Point3d();
+
+ /**
+ * The user's right ear's position in head coordinates.
+ */
+ Point3d rightEarPosInHead = new Point3d();
+
+ /**
+ * The user's nominal eye height as measured
+ * from the ground plane.
+ */
+ double nominalEyeHeightFromGround;
+
+ /**
+ * The amount to offset the system's
+ * viewpoint from the user's current eye-point. This offset
+ * distance allows an "Over the shoulder" view of the scene
+ * as seen by the user.
+ */
+ double nominalEyeOffsetFromNominalScreen;
+
+ // Head to head-tracker coordinate system transform.
+ // If head tracking is enabled, this transform is a calibration
+ // constant. If head tracking is not enabled, this transform is
+ // not used.
+ // This is only used in SCREEN_VIEW mode.
+ Transform3D headToHeadTracker = new Transform3D();
+
+ // ****************************
+ // * From PhysicalEnvironment *
+ // ****************************
+
+ // Coexistence coordinate system to tracker-base coordinate
+ // system transform. If head tracking is enabled, this transform
+ // is a calibration constant. If head tracking is not enabled,
+ // this transform is not used.
+ // This is used in both SCREEN_VIEW and HMD_VIEW modes.
+ Transform3D coexistenceToTrackerBase = new Transform3D();
+
+ // Transform generated by the head tracker to transform
+ // from tracker base to head tracker coordinates (and the inverse).
+ Transform3D headTrackerToTrackerBase = new Transform3D();
+ Transform3D trackerBaseToHeadTracker = new Transform3D();
+
+ //
+ // Indicates whether the underlying hardware implementation
+ // supports tracking.
+ //
+ boolean trackingAvailable;
+
+ // Sensor index for head tracker
+ int headIndex;
+
+ //
+ // This variable specifies the policy Java 3D will use in placing
+ // the user's eye position relative to the user's head position
+ // (NOMINAL_SCREEN, NOMINAL_HEAD, or NOMINAL_FEET).
+ // It is used in the calibration process.
+ //
+ int coexistenceCenterInPworldPolicy;
+
+
+ // *************
+ // * From View *
+ // *************
+
+ // View model compatibility mode flag
+ boolean compatibilityModeEnable;
+
+ // coexistenceCenteringEnable flag
+ boolean coexistenceCenteringEnable;
+
+ Point3d leftManualEyeInCoexistence = new Point3d();
+ Point3d rightManualEyeInCoexistence = new Point3d();
+
+ // Indicates which major mode of view computation to use:
+ // HMD mode or screen/fish-tank-VR mode.
+ int viewPolicy;
+
+ // The current projection policy (parallel versus perspective)
+ int projectionPolicy;
+
+ // The current screen scale policy and scale value
+ int screenScalePolicy;
+ double screenScale;
+
+ // The current window resize, movement and eyepoint policies
+ int windowResizePolicy;
+ int windowMovementPolicy;
+ int windowEyepointPolicy;
+
+ // The current monoscopic view policy
+ int monoscopicViewPolicy;
+
+ // The view model's field of view.
+ double fieldOfView;
+
+ // The distance away from the clip origin
+ // in the direction of gaze for the front and back clip planes.
+ double frontClipDistance;
+ double backClipDistance;
+
+ // Front and back clip policies
+ int frontClipPolicy;
+ int backClipPolicy;
+
+ // ViewPlatform of this view
+ ViewPlatformRetained vpRetained;
+
+ /**
+ * Defines the visibility policy.
+ */
+ int visibilityPolicy;
+
+ // Flag to enable tracking, if so allowed by the trackingAvailable flag.
+ boolean trackingEnable;
+
+ // This setting enables the continuous updating by Java 3D of the
+ // userHeadToVworld transform.
+ boolean userHeadToVworldEnable;
+
+ // The current compatibility mode view transform
+ Transform3D compatVpcToEc = new Transform3D();
+
+ // The current compatibility mode projection transforms
+ Transform3D compatLeftProjection = new Transform3D();
+ Transform3D compatRightProjection = new Transform3D();
+
+ // Mask that indicates ViewCache's view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int vcDirtyMask = 0;
+
+ //
+ // DERIVED DATA
+ //
+
+ // Flag indicating that head tracking will be used
+ private boolean doHeadTracking;
+
+ //
+ // Matrix to transform from user-head to
+ // virtual-world coordinates. This matrix is a read-only
+ // value that Java 3D generates continuously, but only if enabled
+ // by userHeadToVworldEnableFlag.
+ //
+ Transform3D userHeadToVworld = new Transform3D();
+
+
+ /**
+ * Take snapshot of all per-view API parameters and input values.
+ */
+ synchronized void snapshot() {
+
+ // View parameters
+ vcDirtyMask = view.vDirtyMask;
+ view.vDirtyMask = 0;
+ compatibilityModeEnable = view.compatibilityModeEnable;
+ coexistenceCenteringEnable = view.coexistenceCenteringEnable;
+ leftManualEyeInCoexistence.set(view.leftManualEyeInCoexistence);
+ rightManualEyeInCoexistence.set(view.rightManualEyeInCoexistence);
+ viewPolicy = view.viewPolicy;
+ projectionPolicy = view.projectionPolicy;
+ screenScalePolicy = view.screenScalePolicy;
+ windowResizePolicy = view.windowResizePolicy;
+ windowMovementPolicy = view.windowMovementPolicy;
+ windowEyepointPolicy = view.windowEyepointPolicy;
+ monoscopicViewPolicy = view.monoscopicViewPolicy;
+
+ fieldOfView = view.fieldOfView;
+ screenScale = view.screenScale;
+
+ frontClipDistance = view.frontClipDistance;
+ backClipDistance = view.backClipDistance;
+ frontClipPolicy = view.frontClipPolicy;
+ backClipPolicy = view.backClipPolicy;
+
+ visibilityPolicy = view.visibilityPolicy;
+
+ trackingEnable = view.trackingEnable;
+ userHeadToVworldEnable = view.userHeadToVworldEnable;
+
+ view.compatVpcToEc.getWithLock(compatVpcToEc);
+ view.compatLeftProjection.getWithLock(compatLeftProjection);
+ view.compatRightProjection.getWithLock(compatRightProjection);
+
+ // ViewPlatform parameters
+ ViewPlatform vpp = view.getViewPlatform();
+
+ if (vpp == null) {
+ // This happens when user attach a null viewplatform
+ // and MC still call updateViewCache() in run before
+ // the viewDeactivate request get.
+ return;
+ }
+
+ vpRetained = (ViewPlatformRetained) vpp.retained;
+
+ synchronized(vpRetained) {
+ vcDirtyMask |= vpRetained.vprDirtyMask;
+ vpRetained.vprDirtyMask = 0;
+ viewAttachPolicy = vpRetained.viewAttachPolicy;
+ // System.out.println("ViewCache snapshot vcDirtyMask " + vcDirtyMask );
+ }
+
+ // PhysicalEnvironment parameters
+ PhysicalEnvironment env = view.getPhysicalEnvironment();
+
+ synchronized(env) {
+ vcDirtyMask |= env.peDirtyMask;
+ env.peDirtyMask = 0;
+
+ env.coexistenceToTrackerBase.getWithLock(coexistenceToTrackerBase);
+ trackingAvailable = env.trackingAvailable;
+ coexistenceCenterInPworldPolicy = env.coexistenceCenterInPworldPolicy;
+
+ // NOTE: this is really derived data, but we need it here in order
+ // to avoid reading head tracked data when no tracker is available
+ // and enabled.
+ doHeadTracking = trackingEnable && trackingAvailable;
+
+ if (doHeadTracking) {
+ headIndex = env.getHeadIndex();
+ env.getSensor(headIndex).getRead(headTrackerToTrackerBase);
+ vcDirtyMask |= View.TRACKING_ENABLE_DIRTY;
+ }
+ else {
+ headTrackerToTrackerBase.setIdentity();
+ }
+ }
+
+ // PhysicalBody parameters
+ PhysicalBody body = view.getPhysicalBody();
+
+ synchronized(body) {
+ vcDirtyMask |= body.pbDirtyMask;
+ body.pbDirtyMask = 0;
+
+ leftEyePosInHead.set(body.leftEyePosition);
+ rightEyePosInHead.set(body.rightEyePosition);
+ leftEarPosInHead.set(body.leftEarPosition);
+ rightEarPosInHead.set(body.rightEarPosition);
+
+ nominalEyeHeightFromGround = body.nominalEyeHeightFromGround;
+ nominalEyeOffsetFromNominalScreen =
+ body.nominalEyeOffsetFromNominalScreen;
+ }
+
+ body.headToHeadTracker.getWithLock(headToHeadTracker);
+ }
+
+
+ /**
+ * Compute derived data using the snapshot of the per-view data.
+ */
+ synchronized void computeDerivedData() {
+ if (doHeadTracking) {
+ trackerBaseToHeadTracker.invert(headTrackerToTrackerBase);
+ //System.out.println("trackerBaseToHeadTracker: ");
+ //System.out.println(trackerBaseToHeadTracker);
+ }
+ else {
+ trackerBaseToHeadTracker.setIdentity();
+ }
+
+ // TODO: implement head to vworld tracking if userHeadToVworldEnable
+ // is set
+ userHeadToVworld.setIdentity();
+ }
+
+
+ // Get methods for returning derived data values.
+ // Eventually, these get functions will cause some of the parameters
+ // to be lazily evaluated.
+ //
+ // NOTE that in the case of Transform3D, and Tuple objects, a reference
+ // to the actual derived data is returned. In these cases, the caller
+ // must ensure that the returned data is not modified.
+
+ boolean getDoHeadTracking() {
+ return doHeadTracking;
+ }
+
+ /**
+ * Constructs and initializes a ViewCache object.
+ */
+ ViewCache(View view) {
+ this.view = view;
+
+ if (false)
+ System.out.println("Constructed a ViewCache");
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ViewPlatform.java b/src/classes/share/javax/media/j3d/ViewPlatform.java
new file mode 100644
index 0000000..6436b42
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ViewPlatform.java
@@ -0,0 +1,265 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+
+/**
+ * The ViewPlatform leaf node object controls the position, orientation
+ * and scale of the viewer. It is the node in the scene graph that a
+ * View object connects to. A viewer navigates through the virtual
+ * universe by changing the transform in the scene graph hierarchy above
+ * the ViewPlatform.
+ * <p>
+ * <b>The View Attach Policy</b>
+ * <p>
+ * The actual view that Java 3D's renderer draws depends on the view
+ * attach policy specified within the currently attached ViewPlatform.
+ * The view attach policy, set by the setViewAttachPolicy
+ * method, is one of the following:
+ * <p>
+ * <UL>
+ * <LI>View.NOMINAL_HEAD - ensures that the end-user's nominal eye
+ * position in the physical world corresponds to the virtual eye's
+ * nominal eye position in the virtual world (the ViewPlatform's origin).
+ * In essence, this policy tells Java 3D to position the virtual eyepoint
+ * relative to the ViewPlatform origin in the same way as the physical
+ * eyepoint is positioned relative to its nominal physical-world
+ * origin. Deviations in the physical eye's position and orientation from
+ * nominal in the physical world generate corresponding deviations of the
+ * virtual eye's position and orientation in the virtual world. This
+ * is the default view attach policy.</LI>
+ * <p>
+ * <LI>View.NOMINAL_FEET - ensures that the end-user's virtual feet
+ * always touch the virtual ground. This policy tells Java 3D to compute
+ * the physical-to-virtual-world correspondence in a way that enforces
+ * this constraint. Java 3D does so by appropriately offsetting the
+ * physical eye's position by the end-user's physical height. Java 3D
+ * uses the nominalEyeHeightFromGround parameter found in the
+ * PhysicalBody object to perform this computation.</LI>
+ * <p>
+ * <LI>View.NOMINAL_SCREEN - allows an application to always have
+ * the virtual eyepoint appear at some "viewable" distance from a point
+ * of interest. This policy tells Java 3D to compute the
+ * physical-to-virtual-world correspondence in a way
+ * that ensures that the renderer moves the nominal virtual eyepoint
+ * away from the point of interest by the amount specified by the
+ * nominalEyeOffsetFromNominalScreen parameter found in the
+ * PhysicalBody object.</LI></UL>
+ * <p>
+ * <b>Activation Radius</b>
+ * <p>
+ * The ViewPlatform's activation radius defines an activation
+ * volume surrounding the center of the ViewPlatform. This activation
+ * volume is a spherical region that intersects with the scheduling regions
+ * and application regions
+ * of other leaf node objects to determine which of those objects may
+ * affect rendering. Only active view platforms--that is, view platforms
+ * attached to a View--will be used to schedule or select other leaf nodes.
+ * <p>
+ * Different leaf objects interact with the ViewPlatform's activation
+ * volume differently. The Background, Clip, and Soundscape leaf objects
+ * each define a set of attributes and an application region in which
+ * those attributes are applied. If more than one node of a given type
+ * (Background, Clip, or Soundscape) intersects an active ViewPlatform's
+ * activation volume, the "most appropriate" node is selected for that View.
+ * Sound leaf objects and Behavior objects become active when
+ * their scheduling region intersects an active ViewPlatform's activation
+ * volume.
+ * <p>
+ * The activation radius is in view platform coordinates. For the
+ * default screen scale policy of SCALE_SCREEN_SIZE, the
+ * activationRadius parameter value is multiplied by half the
+ * monitor screen size to derive the actual activation radius. For example,
+ * for the default screen size of 0.35 meters, and the default activation
+ * radius value of 62, the actual activation radius would be 10.85
+ * meters.
+ * <p>
+ * <UL>
+ * <code>62 * 0.35 / 2 = 10.85</code>
+ * </UL>
+ * <p>
+ *
+ * @see View
+ */
+
+public class ViewPlatform extends Leaf {
+
+ /**
+ * Specifies that the ViewPlatform allows read access to its view
+ * attach policy information at runtime.
+ */
+ public static final int
+ ALLOW_POLICY_READ = CapabilityBits.VIEW_PLATFORM_ALLOW_POLICY_READ;
+
+ /**
+ * Specifies that the ViewPlatform allows write access to its view
+ * attach policy information at runtime.
+ */
+ public static final int
+ ALLOW_POLICY_WRITE = CapabilityBits.VIEW_PLATFORM_ALLOW_POLICY_WRITE;
+
+ /**
+ * Constructs a ViewPlatform object with default parameters.
+ * The default values are as follows:
+ * <ul>
+ * view attach policy : View.NOMINAL_HEAD<br>
+ * activation radius : 62<br>
+ * </ul>
+ */
+ public ViewPlatform() {
+ }
+
+ /**
+ * Creates the retained mode ViewPlatformRetained object that this
+ * ViewPlatform component object will point to.
+ */
+ void createRetained() {
+ this.retained = new ViewPlatformRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Sets the view attach policy that determines the coexistence center
+ * in the virtual world. This policy determines how Java 3D places the
+ * view platform relative to the position of the user's head, one of
+ * View.NOMINAL_SCREEN, View.NOMINAL_HEAD, or View.NOMINAL_FEET.
+ * The default policy is View.NOMINAL_HEAD.
+ * @param policy the new policy
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ * @see View#NOMINAL_SCREEN
+ * @see View#NOMINAL_HEAD
+ * @see View#NOMINAL_FEET
+ */
+ public void setViewAttachPolicy(int policy) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POLICY_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewPlatform0"));
+
+ switch (policy) {
+ case View.NOMINAL_SCREEN:
+ case View.NOMINAL_HEAD:
+ case View.NOMINAL_FEET:
+ break;
+
+ default:
+ throw new IllegalArgumentException(J3dI18N.getString("ViewPlatform1"));
+ }
+
+ ((ViewPlatformRetained)this.retained).setViewAttachPolicy(policy);
+ }
+
+ /**
+ * Returns the current coexistence center in virtual-world policy.
+ * @return one of: View.NOMINAL_SCREEN, View.NOMINAL_HEAD, or
+ * View.NOMINAL_FEET
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int getViewAttachPolicy() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_POLICY_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewPlatform2"));
+
+ return ((ViewPlatformRetained)this.retained).getViewAttachPolicy();
+ }
+
+ /**
+ * Set the ViewPlatform's activation radius which defines an activation
+ * volume around the view platform.
+ * @param activationRadius the new activation radius
+ */
+ public void setActivationRadius(float activationRadius) {
+ ((ViewPlatformRetained)this.retained).setActivationRadius(activationRadius);
+ }
+
+ /**
+ * Get the ViewPlatform's activation radius.
+ * @return the ViewPlatform activation radius
+ */
+ public float getActivationRadius() {
+ return ((ViewPlatformRetained)this.retained).getActivationRadius();
+ }
+
+
+ /**
+ * 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) {
+ ViewPlatform v = new ViewPlatform();
+ v.duplicateNode(this, forceDuplicate);
+ return v;
+ }
+
+
+ /**
+ * Copies all ViewPlatform information from <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>duplicateNode</code> method. This routine does
+ * the actual duplication of all "local data" (any data defined in
+ * this object). It then will duplicate the retained side of the
+ * tree if this method was called from its own 2 parameter
+ * <code>duplicateNode</code> method. This is designate by setting the
+ * <code>duplicateRetained</code> flag to <code>true</code>.
+ * Without this flag a <code>duplicateNode</code> method would not
+ * whether or not to duplicate the retained side of the object.
+ *
+ * @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.
+ * @param duplicateRetained set to <code>true</code> when this
+ * method is should initiate the duplicateRetained call. This
+ * call walks up a nodes superclasses so it should only be called
+ * once from the class of the original node.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ViewPlatformRetained attr =
+ (ViewPlatformRetained) originalNode.retained;
+ ViewPlatformRetained rt = (ViewPlatformRetained) retained;
+
+ rt.setActivationRadius(attr.getActivationRadius());
+ rt.setViewAttachPolicy(attr.getViewAttachPolicy());
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ViewPlatformRetained.java b/src/classes/share/javax/media/j3d/ViewPlatformRetained.java
new file mode 100644
index 0000000..8d7c031
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ViewPlatformRetained.java
@@ -0,0 +1,410 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+/**
+ * ViewPlatform object (retained side)
+ */
+
+class ViewPlatformRetained extends LeafRetained {
+
+ // different types of IndexedUnorderedSet that use in BehaviorStructure
+ static final int VP_IN_BS_LIST = 0;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 1;
+
+ /**
+ * This variable specifies the policy Java 3D will use in placing the
+ * user's eye point as a function of head position. The variable can
+ * contain one of NOMINAL_SCREEN, NOMINAL_HEAD, or NOMINAL_FEET.
+ */
+ int viewAttachPolicy = View.NOMINAL_HEAD;
+
+ /**
+ * The list of views associated with this view platform.
+ * Use getViewList() to access this variable.
+ */
+ private ArrayList viewList = new ArrayList();
+
+ /**
+ * Cached list of viewList for synchronization
+ */
+ private View views[] = null;
+
+ // The locale that this node is decended from
+ Locale locale = null;
+
+ // dirty flag for viewList
+ boolean viewListDirty = true;
+
+ /**
+ * The current cached view platform transform (vworldToVpc) and
+ * its inverse (vpcToVworld).
+ */
+ Transform3D vworldToVpc = null;
+ Transform3D vpcToVworld = new Transform3D();
+
+
+ /**
+ * The activation radius. The value is chosen so that when using a
+ * default screen scale and field of view, the entire view frustum
+ * is enclosed.
+ */
+
+ /**
+ * Position used for placing this view platform.
+ */
+ BoundingSphere sphere =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 62.0f);
+
+ /**
+ * This is the cached bounding sphere used for the activation volume.
+ */
+ BoundingSphere schedSphere;
+ Point3d center = new Point3d();
+ final static Point3d zeroPoint = new Point3d();
+
+ // Mask that indicates this ViewPlatformRetained's view dependence info. has changed,
+ // and CanvasViewCache may need to recompute the final view matries.
+ int vprDirtyMask = (View.VPR_VIEW_ATTACH_POLICY_DIRTY
+ | View.VPR_VIEWPLATFORM_DIRTY);
+
+ static final Object emptyObj[] = new Object[0];
+ static final Transform3D identity = new Transform3D();
+
+ ViewPlatformRetained() {
+ this.nodeType = NodeRetained.VIEWPLATFORM;
+ localBounds = new BoundingBox();
+ ((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
+ ((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
+ IndexedUnorderSet.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ schedSphere = (BoundingSphere) sphere.clone();
+ }
+
+ /**
+ * Sets the coexistence center in virtual world policy.
+ * This setting determines how Java 3D places the
+ * user's eye point as a function of head position. The variable can
+ * contain one of NOMINAL_SCREEN, NOMINAL_HEAD, or NOMINAL_FEET.
+ * @param policy the new policy, one of NOMINAL_SCREEN, NOMINAL_HEAD,
+ * or NOMINAL_FEET
+ */
+ void setViewAttachPolicy(int policy) {
+ synchronized(this) {
+ this.viewAttachPolicy = policy;
+ vprDirtyMask |= View.VPR_VIEW_ATTACH_POLICY_DIRTY;
+ }
+
+ if (source != null && source.isLive()) {
+ repaint();
+ }
+ }
+
+
+ void repaint() {
+ View views[] = getViewList();
+ for (int i=views.length-1; i >=0; i--) {
+ views[i].repaint();
+ }
+ }
+
+ /**
+ * Returns the current coexistence center in virtual-world policy.
+ * @return one of: NOMINAL_SCREEN, NOMINAL_HEAD, or NOMINAL_FEET
+ */
+ int getViewAttachPolicy() {
+ return this.viewAttachPolicy;
+ }
+
+ /**
+ * Set the ViewPlatform's activation radius
+ */
+ void setActivationRadius(float activationRadius) {
+ sphere.setRadius(activationRadius);
+
+ if (source != null && source.isLive()) {
+ repaint();
+ }
+ // Notify behavior scheduler & RenderBin
+ if (source.isLive()) {
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.UPDATE_VIEWPLATFORM;
+ message.threads = J3dThread.UPDATE_RENDER|J3dThread.UPDATE_BEHAVIOR;
+ message.universe = universe;
+ message.args[0] = this;
+ message.args[1] = new Float(activationRadius);
+ VirtualUniverse.mc.processMessage(message);
+ } else {
+ schedSphere.setRadius(activationRadius);
+ }
+
+ }
+
+ /**
+ * Get the ViewPlatform's activation radius
+ */
+ float getActivationRadius() {
+ return (float) sphere.getRadius();
+ }
+
+ /**
+ * This sets the view that is associated with this view platform.
+ */
+ // TODO: This must be changed to a list of views!
+ void setView(View v) {
+ synchronized (viewList) {
+ if (!viewList.contains(v)) {
+ viewList.add(v);
+ }
+ viewListDirty = true;
+ }
+ }
+
+ void removeView(View v) {
+ synchronized (viewList) {
+ if (viewList.contains(v)) {
+ viewList.remove(viewList.indexOf(v));
+ }
+ viewListDirty = true;
+ }
+ }
+
+ Transform3D getVworldToVpc() {
+ if (vworldToVpc == null)
+ vworldToVpc = VirtualUniverse.mc.getTransform3D(null);
+ vworldToVpc.set(getCurrentLocalToVworld(null));
+ vworldToVpc.invert();
+ return vworldToVpc;
+ }
+
+ Transform3D getVpcToVworld() {
+ vpcToVworld .set(getCurrentLocalToVworld(null));
+ return vpcToVworld;
+ }
+
+
+ void evaluateViewPlatformTransform() {
+ if (vworldToVpc != null) {
+ FreeListManager.freeObject(FreeListManager.TRANSFORM3D,
+ vworldToVpc);
+ }
+ // clear cache so that next time getVworldToVpc() can recompute
+ vworldToVpc = null;
+ }
+
+ /**
+ * Evaluate the view platform transform by traversing *up* the tree from
+ * this ViewPlatform node, computing the composite model transform
+ * along the way. Because we are traversing bottom to top, we must
+ * multiply each TransformGroup's matrix on the left by the
+ * composite transform on the right (rather than the other way
+ * around as is usually done). Once we have the composite model
+ * transform for this ViewPlatform--the vpcToVworld transform--we
+ * simply invert it to get the vworldToVpc transform.
+ */
+ void evaluateInitViewPlatformTransform(NodeRetained node, Transform3D trans) {
+ if (node instanceof TransformGroupRetained) {
+ Transform3D tmpTrans = new Transform3D();
+ TransformGroupRetained tgr = (TransformGroupRetained)node;
+ tgr.transform.getWithLock(tmpTrans);
+ trans.mul(tmpTrans, trans);
+ }
+
+ NodeRetained parent = node.getParent();
+ if (parent != null) {
+ // Not at the top yet.
+ evaluateInitViewPlatformTransform(parent, trans);
+ }
+ }
+
+ void evaluateInitViewPlatformTransform() {
+
+ Transform3D lastLocalToVworld;
+
+ synchronized (this) {
+ lastLocalToVworld = getLastLocalToVworld();
+
+ if (lastLocalToVworld.equals(identity)) {
+ // lastLocalToVworld not yet updated
+ // for Renderer viewCache when startup
+ evaluateInitViewPlatformTransform((NodeRetained)this,
+ lastLocalToVworld);
+ }
+ }
+ }
+
+
+ // This is invoke from BehaviorStructure
+ void updateActivationRadius(float radius) {
+ schedSphere.setCenter(zeroPoint);
+ schedSphere.setRadius(radius);
+ schedSphere.transform(getCurrentLocalToVworld(null));
+ }
+
+ // This is invoke from BehaviorStructure when TransformGroup
+ // above this viewplatform changed
+ void updateTransformRegion() {
+ Transform3D tr = getCurrentLocalToVworld(null);
+ schedSphere.setCenter(zeroPoint);
+ schedSphere.transform(tr);
+ tr.transform(zeroPoint, center);
+ }
+
+ /**
+ * This setLive routine first calls the superclass's method, then
+ * it evaluates the view platform transform, and then it activates
+ * all canvases that are associated with the attached view.
+ */
+ void setLive(SetLiveState s) {
+ View views[] = getViewList();
+
+ for (int i = views.length-1; i>=0; i--) {
+ views[i].checkView();
+ }
+
+ super.doSetLive(s);
+
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("ViewPlatformRetained1"));
+ }
+
+ if (inSharedGroup) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("ViewPlatformRetained2"));
+ }
+
+ if (s.viewLists != null) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("ViewPlatformRetained3"));
+ }
+ /*
+ if (false) {
+ System.out.println("setLive: vworldToVpc = ");
+ System.out.println(this.vworldToVpc);
+ System.out.println("setLive: vpcToVworld = ");
+ System.out.println(this.vpcToVworld);
+ }
+ */
+ this.locale = s.locale;
+
+
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.VPF_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ // process switch leaf
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.VPF_TARGETS);
+ }
+ switchState = (SwitchState)s.switchStates.get(0);
+ s.nodeList.add(this);
+ s.notifyThreads |= (J3dThread.UPDATE_BEHAVIOR);
+ super.markAsLive();
+ for (int i = views.length-1; i>=0; i--) {
+ views[i].setUniverse(s.universe);
+ views[i].evaluateActive();
+ }
+
+ universe.addViewPlatform(this);
+ s.traverseFlags |= NodeRetained.CONTAINS_VIEWPLATFORM;
+ }
+
+ /**
+ * This clearLive routine first calls the superclass's method, then
+ * it deactivates all canvases that are associated with the attached
+ * view.
+ */
+ void clearLive(SetLiveState s) {
+ super.clearLive(s);
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(this, Targets.VPF_TARGETS);
+ }
+
+ View views[] = getViewList();
+ for (int i = views.length-1; i>=0; i--) {
+ views[i].evaluateActive();
+ }
+ s.nodeList.add(this);
+ if (s.transformTargets != null && s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(this, Targets.VPF_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+
+ }
+ s.notifyThreads |= (J3dThread.UPDATE_BEHAVIOR |
+ J3dThread.SOUND_SCHEDULER);
+ universe.removeViewPlatform(this);
+ }
+
+ /**
+ * Re-evaluate all View active status reference to this view
+ * platform. This procedure is called from RenderBin when switch
+ * above a view platform changed.
+ */
+ void reEvaluateView() {
+ View views[] = getViewList();
+
+ for (int i=views.length-1; i >=0; i--) {
+ views[i].evaluateActive();
+ }
+ }
+
+ /**
+ * Get a copy of cached view list
+ */
+ View[] getViewList() {
+ synchronized (viewList) {
+ if (viewListDirty) {
+ views = (View []) viewList.toArray(new View[viewList.size()]);
+ viewListDirty = false;
+ }
+ return views;
+ }
+ }
+
+ /**
+ * Use by BehaviorStructure to determine whether current
+ * ViewPlatform is active or not.
+ */
+ boolean isActiveViewPlatform() {
+ View v[] = getViewList();
+ if (v != null) {
+ for (int i=0; i < v.length; i++) {
+ if (v[i].active) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void processSwitchChanged() {
+ reEvaluateView();
+ }
+
+
+ void compile(CompileState compState) {
+
+ super.compile(compState);
+
+ // keep the parent transform group. It's not worth
+ // to push the static transform here.
+ compState.keepTG = true;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/ViewSpecificGroup.java b/src/classes/share/javax/media/j3d/ViewSpecificGroup.java
new file mode 100644
index 0000000..71dc123
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ViewSpecificGroup.java
@@ -0,0 +1,324 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+
+/**
+ * The ViewSpecificGroup node is a Group whose descendants are
+ * rendered only on a specified set of views. It
+ * contains a list of views on which its descendants are
+ * rendered. Methods are provided to add, remove, and enumerate the
+ * list of views. The list of views is initially empty, meaning that
+ * the descendants of this group will not be rendered on any view. At
+ * least one view must be added to the list of views for rendering to
+ * occur.
+ *
+ * <p>
+ * All nodes except ViewPlatform may appear as a descendant of
+ * ViewSpecificGroup (including another ViewSpecificGroup). Behavior
+ * nodes may appear under a ViewSpecificGroup, but are not affected by
+ * it--the Behavior scheduler is per-universe rather than per-View.
+ * BoundingLeaf nodes are similarly unaffected by being under a
+ * ViewSpecificGroup. A BoundingLeaf under a ViewSpecificGroup
+ * provides a valid bounds for any node that refers to it,
+ * irrespective of the view.
+ * The rest of the leaf nodes either: A) are only rendered within the
+ * specified view(s), for example, Shape3D, Morph, and Sound; or B)
+ * only affect other objects when they are rendered in the specified
+ * view(s), for example, AlternateAppearance, Clip, ModelClip, Fog,
+ * Light, Soundscape, Background.
+ *
+ * @since Java 3D 1.3
+ */
+
+public class ViewSpecificGroup extends Group {
+ /**
+ * Specifies that this ViewSpecificGroup node allows reading its
+ * view information at runtime.
+ */
+ public static final int
+ ALLOW_VIEW_READ = CapabilityBits.VIEW_SPECIFIC_GROUP_ALLOW_VIEW_READ;
+
+ /**
+ * Specifies that this ViewSpecificGroup node allows writing its
+ * view information at runtime.
+ */
+ public static final int
+ ALLOW_VIEW_WRITE = CapabilityBits.VIEW_SPECIFIC_GROUP_ALLOW_VIEW_WRITE;
+
+
+ /**
+ * Constructs and initializes a new ViewSpecificGroup node object.
+ */
+ public ViewSpecificGroup() {
+ }
+
+
+ /**
+ * Creates the retained mode ViewSpecificGroupRetained object that this
+ * ViewSpecificGroup component object will point to.
+ */
+ void createRetained() {
+ this.retained = new ViewSpecificGroupRetained();
+ this.retained.setSource(this);
+ }
+
+
+ /**
+ * Replaces the view at the specified index in this node's
+ * list of views with the specified View object.
+ *
+ * @param view the View object to be stored at the specified index.
+ * @param index the index of the View object to be replaced.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void setView(View view, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup1"));
+
+ ((ViewSpecificGroupRetained)this.retained).setView(view, index);
+ }
+
+
+ /**
+ * Retrieves the View object at the specified index from this node's
+ * list of views.
+ *
+ * @param index the index of the View object to be returned.
+ * @return the View object at the specified index.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public View getView(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup2"));
+
+ return ((ViewSpecificGroupRetained)this.retained).getView(index);
+ }
+
+
+ /**
+ * Inserts the specified View object into this node's
+ * list of views at the specified index.
+ *
+ * @param view the View object to be inserted at the specified index.
+ * @param index the index at which the View object is inserted.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void insertView(View view, int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup1"));
+
+ ((ViewSpecificGroupRetained)this.retained).insertView(view, index);
+ }
+
+
+ /**
+ * Removes the View object at the specified index from this node's
+ * list of views.
+ * If this operation causes the list of views to become empty,
+ * then the descendants of this ViewSpecificGroup node will not be
+ * rendered.
+ *
+ * @param index the index of the View object to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void removeView(int index) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup1"));
+ ((ViewSpecificGroupRetained)this.retained).removeView(index);
+
+ }
+
+
+ /**
+ * Returns an enumeration of this ViewSpecificGroup node's list
+ * of views.
+ *
+ * @return an Enumeration object containing all the views.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public Enumeration getAllViews() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup2"));
+
+ return ((ViewSpecificGroupRetained)this.retained).getAllViews();
+ }
+
+
+ /**
+ * Appends the specified View object to this node's list of views.
+ *
+ * @param view the View object to be appended.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public void addView(View view) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup1"));
+
+ ((ViewSpecificGroupRetained)this.retained).addView(view);
+ }
+
+
+ /**
+ * Returns the number of View objects in this node's list of views.
+ * If this number is 0, then the list of views is empty and
+ * the descendants of this ViewSpecificGroup node will not be
+ * rendered.
+ *
+ * @return the number of views in this node's list of views.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ */
+ public int numViews() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup2"));
+
+ return ((ViewSpecificGroupRetained)this.retained).numViews();
+ }
+
+
+ /**
+ * Retrieves the index of the specified View object in this
+ * node's list of views.
+ *
+ * @param view the View object to be looked up.
+ * @return the index of the specified View object;
+ * returns -1 if the object is not in the list.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public int indexOfView(View view) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_READ))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup2"));
+
+ return ((ViewSpecificGroupRetained)this.retained).indexOfView(view);
+ }
+
+
+ /**
+ * Removes the specified View object from this
+ * node's list of views. If the specified object is not in the
+ * list, the list is not modified.
+ * If this operation causes the list of views to become empty,
+ * then the descendants of this ViewSpecificGroup node will not be
+ * rendered.
+ *
+ * @param view the View object to be removed.
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeView(View view) {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup1"));
+
+ ((ViewSpecificGroupRetained)this.retained).removeView(view);
+ }
+
+
+ /**
+ * Removes all View objects from this node's
+ * list of views.
+ * Since this method clears the list of views, the descendants of
+ * this ViewSpecificGroup node will not be rendered.
+ *
+ * @exception CapabilityNotSetException if appropriate capability is
+ * not set and this object is part of live or compiled scene graph
+ *
+ * @since Java 3D 1.3
+ */
+ public void removeAllViews() {
+ if (isLiveOrCompiled())
+ if(!this.getCapability(ALLOW_VIEW_WRITE))
+ throw new CapabilityNotSetException(J3dI18N.getString("ViewSpecificGroup1"));
+
+ ((ViewSpecificGroupRetained)this.retained).removeAllViews();
+ }
+
+
+ /**
+ * 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) {
+ ViewSpecificGroup vsg = new ViewSpecificGroup();
+ vsg.duplicateNode(this, forceDuplicate);
+ return vsg;
+ }
+
+
+ /**
+ * Copies all ViewSpecificGroup 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 Group#cloneNode
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
+
+ // TODO: implement this
+ super.duplicateAttributes(originalNode, forceDuplicate);
+
+ ViewSpecificGroupRetained attr = (ViewSpecificGroupRetained) originalNode.retained;
+ ViewSpecificGroupRetained rt = (ViewSpecificGroupRetained) retained;
+
+ for (Enumeration e = attr.getAllViews(); e.hasMoreElements(); ) {
+ rt.addView((View)e.nextElement());
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java b/src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java
new file mode 100644
index 0000000..da3bd8c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java
@@ -0,0 +1,747 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * The ViewSpecificGroup node retained object.
+ */
+
+class ViewSpecificGroupRetained extends GroupRetained {
+
+ ArrayList apiViewList = new ArrayList();
+
+ // Used by leaf objects particularly GAs
+ // Updated in a MT Safe manner and also used by RenderBin
+ ArrayList cachedViewList = new ArrayList();
+
+ // The object that contains the dynamic HashKey - a string type object
+ // Used in scoping
+ HashKey tempKey = new HashKey(250);
+
+ // ArrayList of Integer indices
+ ArrayList parentLists = new ArrayList();
+
+
+ static final int SET_VIEW = 0x1;
+ static final int ADD_VIEW = 0x2;
+ static final int REMOVE_VIEW = 0x4;
+
+ // Construct retained object
+ ViewSpecificGroupRetained() {
+ this.nodeType = NodeRetained.VIEWSPECIFICGROUP;
+ viewLists = new ArrayList();
+ }
+
+ void addView(View view) {
+ int i;
+ Integer mtype = new Integer(ADD_VIEW);
+
+ apiViewList.add(view);
+ if (source.isLive() && view != null) {
+ // Gather all affected leaf nodes and send a message to
+ // RenderingEnv and RenderBin
+ if (inSharedGroup) {
+ ArrayList parentList;
+ for (int k = 0; k < localToVworldKeys.length; k++) {
+ parentList = (ArrayList)parentLists.get(k);
+ // If the parentList contains this view or if this is the
+ // first VSG then ..
+ if (parentList == null || parentList.contains(view)) {
+ Object[] objAry = new Object[4];
+ ArrayList addVsgList = new ArrayList();
+ ArrayList addLeafList = new ArrayList();
+ int[] addKeyList = new int[10];
+
+ HashKey key = localToVworldKeys[k];
+ addVsgList.add(this);
+ addKeyList[0] = k;
+ objAry[0] = view;
+ objAry[1] = addVsgList;
+ objAry[2] = addLeafList;
+ /*
+ for (int n = 0; n < addLeafList.size(); n++) {
+ System.out.println("Shared:n = "+n+" addLeafList = "+addLeafList.get(n));
+ }
+ */
+ objAry[3] = super.processViewSpecificInfo(ADD_VIEW,
+ (HashKey)key, view,
+ addVsgList, addKeyList, addLeafList);
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+ message.universe = universe;
+ message.args[0] = mtype;
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ }
+ else {
+ ArrayList parentList = (ArrayList)parentLists.get(0);
+
+ // If the parentList contains this view or if this is the
+ // first VSG then ..
+ if (parentList == null || parentList.contains(view)) {
+ Object[] objAry = new Object[4];
+ ArrayList addVsgList = new ArrayList();
+ ArrayList addLeafList = new ArrayList();
+ int[] addKeyList = new int[10];
+
+ objAry[0] = view;
+ objAry[1] = addVsgList;
+ objAry[2] = addLeafList;
+
+ addVsgList.add(this);
+ addKeyList[0] = 0;
+ tempKey.reset();
+ objAry[3] = super.processViewSpecificInfo(ADD_VIEW,
+ tempKey, view,
+ addVsgList, addKeyList, addLeafList);
+
+ /*
+ for (int n = 0; n < addLeafList.size(); n++) {
+ System.out.println("n = "+n+" addLeafList = "+addLeafList.get(n));
+ }
+ */
+
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+ message.universe = universe;
+ message.args[0] = mtype;
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+
+ }
+ }
+
+
+ void setView(View view, int index) {
+ int i;
+
+ View oldView = (View)apiViewList.get(index);
+ Integer mtype = new Integer(SET_VIEW);
+
+ if (oldView == view)
+ return;
+
+ apiViewList.set(index, view);
+ if (source.isLive()) {
+ // Gather all affected leaf nodes and send a message to
+ // RenderingEnv and RenderBin
+ if (inSharedGroup) {
+ ArrayList parentList;
+ for (int k = 0; k < localToVworldKeys.length; k++) {
+ parentList = (ArrayList)parentLists.get(k);
+ Object[] objAry = new Object[8];
+ ArrayList addVsgList = new ArrayList();
+ ArrayList removeVsgList = new ArrayList();
+ ArrayList addLeafList = new ArrayList();
+ ArrayList removeLeafList = new ArrayList();
+ int[] addKeyList = new int[10];
+ int[] removeKeyList = new int[10];
+
+ objAry[0] = view;
+ objAry[1] = addVsgList ;
+ objAry[2] = addLeafList;
+ objAry[4] = oldView;
+ objAry[5] = removeVsgList;
+ objAry[6] = removeLeafList;
+
+ HashKey key = localToVworldKeys[k];
+ if (oldView != null && (parentList == null || parentList.contains(oldView))) {
+ removeVsgList.add(this);
+ removeKeyList[0] = k;
+ objAry[7] = super.processViewSpecificInfo(REMOVE_VIEW, (HashKey)key,
+ oldView, removeVsgList, removeKeyList, removeLeafList);
+ }
+
+ if (view != null && (parentList == null || parentList.contains(view))) {
+ addVsgList.add(this);
+ addKeyList[0] = k;
+ objAry[3] = super.processViewSpecificInfo(ADD_VIEW, (HashKey)key,
+ view, addVsgList, addKeyList, addLeafList);
+ }
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+ message.universe = universe;
+ message.args[0] = mtype;
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+
+ }
+ else {
+ ArrayList parentList = (ArrayList)parentLists.get(0);
+ Object[] objAry = new Object[8];
+ ArrayList addVsgList = new ArrayList();
+ ArrayList removeVsgList = new ArrayList();
+ ArrayList addLeafList = new ArrayList();
+ ArrayList removeLeafList = new ArrayList();
+ int[] addKeyList = new int[10];
+ int[] removeKeyList = new int[10];
+
+ objAry[0] = view;
+ objAry[1] = addVsgList ;
+ objAry[2] = addLeafList;
+ objAry[4] = oldView;
+ objAry[5] = removeVsgList;
+ objAry[6] = removeLeafList;
+
+
+
+ tempKey.reset();
+ if (oldView != null && (parentList == null || parentList.contains(oldView))) {
+ removeVsgList.add(this);
+ removeKeyList[0] = 0;
+ objAry[7] = super.processViewSpecificInfo(REMOVE_VIEW, (HashKey)tempKey,
+ oldView, removeVsgList, removeKeyList, removeLeafList);
+ }
+ if (view != null && (parentList == null || parentList.contains(view))) {
+ tempKey.reset();
+ addVsgList.add(this);
+ addKeyList[0] = 0;
+ objAry[3] = super.processViewSpecificInfo(ADD_VIEW, (HashKey)tempKey,
+ view, addVsgList, addKeyList, addLeafList);
+ }
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+
+ message.universe = universe;
+ message.args[0] = mtype;
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+
+
+ }
+
+ }
+
+ int[] processViewSpecificInfo(int mode, HashKey key, View v, ArrayList vsgList, int[] keyList, ArrayList leaflist) {
+ int hkIndex = 0;
+ Integer hashInt = null;
+ int[] newKeyList = null;
+ // Get the intersection of the viewList with this view,
+
+ if (source.isLive()) {
+ if (inSharedGroup) {
+ hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
+ }
+
+ if (mode == ADD_VIEW) {
+ ArrayList parentList = (ArrayList)parentLists.get(hkIndex);
+ parentList.add(v);
+ }
+ else if (mode == REMOVE_VIEW) {
+ ArrayList parentList = (ArrayList)parentLists.get(hkIndex);
+ parentList.remove(v);
+ }
+ if(apiViewList.contains(v)) {
+ // System.out.println("processViewSpecificInfo, this = "+this+" key = "+key);
+ vsgList.add(this);
+ if (keyList.length< vsgList.size()) {
+ // System.out.println("====> allocating new array");
+ newKeyList = new int[keyList.length+20];
+ System.arraycopy(keyList, 0, newKeyList, 0, keyList.length);
+ keyList = newKeyList;
+ }
+ if (mode == ADD_VIEW) {
+ if (inSharedGroup) {
+ keyList[vsgList.size()-1] = hkIndex;
+
+ }
+ else {
+ keyList[vsgList.size()-1] = 0;
+ }
+ }
+ else if (mode == REMOVE_VIEW) {
+ if (inSharedGroup) {
+ keyList[vsgList.size()-1] = hkIndex;
+ }
+ else {
+ keyList[vsgList.size()-1] = 0;
+ }
+ }
+ return super.processViewSpecificInfo(mode, key, v, vsgList, keyList, leaflist);
+ }
+ }
+ return keyList;
+ }
+
+ View getView(int index) {
+ return (View)apiViewList.get(index);
+ }
+
+ void insertView(View view, int index) {
+ int i;
+ Integer mtype = new Integer(ADD_VIEW);
+
+ apiViewList.add(index, view);
+ if (source.isLive() && view != null) {
+ // Gather all affected leaf nodes and send a message to
+ // RenderingEnv and RenderBin
+ if (inSharedGroup) {
+ ArrayList parentList;
+ for (int k = 0; k < localToVworldKeys.length; k++) {
+ parentList = (ArrayList)parentLists.get(k);
+ // If the parentList contains this view or if this is the
+ // first VSG then ..
+ if (parentList == null || parentList.contains(view)) {
+ Object[] objAry = new Object[4];
+ ArrayList addVsgList = new ArrayList();
+ ArrayList addLeafList = new ArrayList();
+ int[] addKeyList = new int[10];
+
+ HashKey key = localToVworldKeys[k];
+ addVsgList.add(this);
+ addKeyList[0] = k;
+ objAry[0] = view;
+ objAry[1] = addVsgList;
+ objAry[2] = addLeafList;
+ /*
+ for (int n = 0; n < addLeafList.size(); n++) {
+ System.out.println("Shared:n = "+n+" addLeafList = "+addLeafList.get(n));
+ }
+ */
+ objAry[3] = super.processViewSpecificInfo(ADD_VIEW,
+ (HashKey)key, view,
+ addVsgList, addKeyList, addLeafList);
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+ message.universe = universe;
+ message.args[0] = mtype;
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ }
+ else {
+ ArrayList parentList = (ArrayList)parentLists.get(0);
+
+ // If the parentList contains this view or if this is the
+ // first VSG then ..
+ if (parentList == null || parentList.contains(view)) {
+ Object[] objAry = new Object[4];
+ ArrayList addVsgList = new ArrayList();
+ ArrayList addLeafList = new ArrayList();
+ int[] addKeyList = new int[10];
+
+ objAry[0] = view;
+ objAry[1] = addVsgList;
+ objAry[2] = addLeafList;
+
+ addVsgList.add(this);
+ addKeyList[0] = 0;
+ tempKey.reset();
+ objAry[3] = super.processViewSpecificInfo(ADD_VIEW,
+ tempKey, view,
+ addVsgList, addKeyList, addLeafList);
+
+ /*
+ for (int n = 0; n < addLeafList.size(); n++) {
+ System.out.println("n = "+n+" addLeafList = "+addLeafList.get(n));
+ }
+ */
+
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+ message.universe = universe;
+ message.args[0] = mtype;
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+
+ }
+ }
+
+ void removeView(int index) {
+ int i;
+ View v = (View) apiViewList.remove(index);
+ if (source.isLive() && v != null) {
+ // Gather all affected leaf nodes and send a message to
+ // RenderingEnv and RenderBin
+ if (inSharedGroup) {
+ ArrayList parentList;
+ for (int k = 0; k < localToVworldKeys.length; k++) {
+ parentList = (ArrayList)parentLists.get(k);
+ // If the parentList contains this view or if this is the
+ // first VSG then ..
+ if (parentList == null || parentList.contains(v)) {
+ Object[] objAry = new Object[4];
+ ArrayList removeVsgList = new ArrayList();
+ ArrayList removeLeafList = new ArrayList();
+ int[] removeKeyList = new int[10];
+
+ objAry[0] = v;
+ objAry[1] = removeVsgList;
+ objAry[2] = removeLeafList;
+ HashKey key = localToVworldKeys[k];
+
+ removeVsgList.add(this);
+ removeKeyList[0] = k;
+
+ objAry[3] = super.processViewSpecificInfo(REMOVE_VIEW,
+ (HashKey)key,v,
+ removeVsgList, removeKeyList, removeLeafList);
+
+
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+ message.universe = universe;
+ message.args[0] = new Integer(REMOVE_VIEW);
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ }
+ else {
+ ArrayList parentList = (ArrayList)parentLists.get(0);
+
+ // If the parentList contains this view or if this is the
+ // first VSG then ..
+ if (parentList == null || parentList.contains(v)) {
+ Object[] objAry = new Object[4];
+ ArrayList removeVsgList = new ArrayList();
+ ArrayList removeLeafList = new ArrayList();
+ int[] removeKeyList = new int[10];
+
+ objAry[0] = v;
+ objAry[1] = removeVsgList;
+ objAry[2] = removeLeafList;
+ removeVsgList.add(this);
+ removeKeyList[0] = 0;
+
+ tempKey.reset();
+ objAry[3] = super.processViewSpecificInfo(REMOVE_VIEW,
+ (HashKey)tempKey, v,
+ removeVsgList, removeKeyList, removeLeafList);
+
+ /*
+ for (int n = 0; n < removeKeyList.size(); n++) {
+ System.out.println("n = "+n+" keyValue = "+removeKeyList.get(n));
+ }
+ */
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.VIEWSPECIFICGROUP_CHANGED;
+ message.threads = (J3dThread.UPDATE_RENDERING_ENVIRONMENT|
+ J3dThread.UPDATE_RENDER |
+ J3dThread.UPDATE_SOUND|
+ J3dThread.SOUND_SCHEDULER);
+ message.universe = universe;
+ message.args[0] = new Integer(REMOVE_VIEW);
+ message.args[1] = objAry;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ }
+ }
+
+ Enumeration getAllViews() {
+ Vector viewList = new Vector();
+ for (int i = 0; i < apiViewList.size(); i++) {
+ viewList.add(apiViewList.get(i));
+ }
+ return viewList.elements();
+ }
+
+ int numViews() {
+ return apiViewList.size();
+ }
+
+ int indexOfView(View view) {
+ return apiViewList.indexOf(view);
+ }
+
+ void removeView(View view) {
+ removeView(apiViewList.indexOf(view));
+ }
+
+ void removeAllViews() {
+ int size = apiViewList.size();
+ for (int i = 0; i < size; i++) {
+ removeView(0);
+ }
+ }
+
+ void compile(CompileState compState) {
+ super.compile(compState);
+
+ // don't remove this group node
+ mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
+
+ // TODO: complete this
+ }
+
+ void setLive(SetLiveState s) {
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("ViewSpecificGroup3"));
+ }
+
+ s.inViewSpecificGroup = true;
+ ArrayList savedViewList = s.viewLists;
+ if (s.changedViewGroup == null) {
+ s.changedViewGroup = new ArrayList();
+ s.changedViewList = new ArrayList();
+ s.keyList = new int[10];
+ s.viewScopedNodeList = new ArrayList();
+ s.scopedNodesViewList = new ArrayList();
+ }
+ super.setLive(s);
+ s.viewLists = savedViewList;
+
+ }
+
+ void clearLive(SetLiveState s) {
+ ArrayList savedViewList = s.viewLists;
+ if (s.changedViewGroup == null) {
+ s.changedViewGroup = new ArrayList();
+ s.changedViewList = new ArrayList();
+ s.keyList = new int[10];
+ s.viewScopedNodeList = new ArrayList();
+ s.scopedNodesViewList = new ArrayList();
+ }
+ // TODO: This is a hack since removeNodeData is called before
+ // children are clearLives
+ int[] tempIndex = null;
+ // Don't keep the indices if everything will be cleared
+ if (inSharedGroup && (s.keys.length != localToVworld.length)) {
+ tempIndex = new int[s.keys.length];
+ for (int i = 0; i < s.keys.length; i++) {
+ tempIndex[i] = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length);
+ }
+ }
+ super.clearLive(s);
+ // Do this after children clearlive since part of the viewLists may get cleared
+ // during removeNodeData
+
+ // If the last SharedGroup is being cleared
+ if((!inSharedGroup) || (localToVworld == null)) {
+ viewLists.clear();
+ }
+ else {
+ // Must be in reverse, to preserve right indexing.
+ for (int i = tempIndex.length-1; i >= 0 ; i--) {
+ if(tempIndex[i] >= 0) {
+ viewLists.remove(tempIndex[i]);
+ }
+ }
+ }
+ s.viewLists = savedViewList;
+ }
+
+
+ void removeNodeData(SetLiveState s) {
+ if((!inSharedGroup) || (s.keys.length == localToVworld.length)) {
+ s.changedViewGroup.add(this);
+ // Remove everything ..
+ int size = s.changedViewGroup.size();
+ if (s.keyList.length < size) {
+ int[] newKeyList = new int[s.keyList.length+20];
+ System.arraycopy(s.keyList, 0, newKeyList, 0, s.keyList.length);
+ s.keyList = newKeyList;
+ // System.out.println("====> RemovedNodeData: Allocating Non-shared");
+ }
+ s.keyList[size -1] = -1;
+ parentLists.clear();
+ }
+ // A path of the shared group is removed
+ else {
+ int i, index;
+ int size = s.changedViewGroup.size();
+ if (s.keyList.length < size+1+s.keys.length) {
+ int[] newKeyList = new int[s.keyList.length+s.keys.length+20];
+ System.arraycopy(s.keyList, 0, newKeyList, 0, s.keyList.length);
+ s.keyList = newKeyList;
+ // System.out.println("====> RemovedNodeData: Allocating Shared");
+ }
+ // Must be in reverse, to preserve right indexing.
+ for (i = s.keys.length-1; i >= 0; i--) {
+ index = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length);
+ if(index >= 0) {
+ s.changedViewGroup.add(this);
+ s.keyList[s.changedViewGroup.size() -1] = index;
+ parentLists.remove(index);
+ }
+ }
+ }
+ s.viewLists =viewLists;
+ super.removeNodeData(s);
+ }
+
+ void updateCachedInformation(int component, View view, int index ) {
+ ArrayList list = (ArrayList) cachedViewList.get(index);
+
+ /*
+ System.out.println("updateCachedInformation v = "+this+" index = "+index+" list = "+list+" cachedViewList.size() = "+cachedViewList.size());
+ for (int k = 0; k < cachedViewList.size(); k++) {
+ System.out.println("v = "+this+" k = "+k+" v.cachedViewList.get(k) = "+cachedViewList.get(k));
+ }
+ */
+ if ((component & ADD_VIEW) != 0) {
+ list.add(view);
+ }
+ else if ((component & REMOVE_VIEW) != 0) {
+ list.remove(view);
+ }
+ /*
+ System.out.println("After updateCachedInformation v = "+this+" index = "+index+" list = "+list+" cachedViewList.size() = "+cachedViewList.size());
+ for (int k = 0; k < cachedViewList.size(); k++) {
+ System.out.println("v = "+this+" k = "+k+" v.cachedViewList.get(k) = "+cachedViewList.get(k));
+ }
+ */
+
+ }
+
+ void setNodeData(SetLiveState s) {
+ super.setNodeData(s);
+ if (!inSharedGroup) {
+ int size = s.changedViewGroup.size();
+ if (s.keyList.length < size+1) {
+ int[] newKeyList = new int[s.keyList.length+20];
+ System.arraycopy(s.keyList, 0, newKeyList, 0, s.keyList.length);
+ s.keyList = newKeyList;
+ // System.out.println("====> setNodeData: Allocating Non-shared");
+ }
+ setAuxData(s, 0, 0);
+ } else {
+ // For inSharedGroup case.
+ int j, hkIndex;
+
+ int size = s.changedViewGroup.size();
+ if (s.keyList.length < size+1+s.keys.length) {
+ int[] newKeyList = new int[s.keyList.length+s.keys.length+20];
+ System.arraycopy(s.keyList, 0, newKeyList, 0, s.keyList.length);
+ s.keyList = newKeyList;
+ // System.out.println("====> setNodeData: Allocating Shared");
+ }
+
+ for(j=0; j<s.keys.length; j++) {
+ hkIndex = s.keys[j].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+
+ if(hkIndex >= 0) {
+ setAuxData(s, j, hkIndex);
+
+ } else {
+ System.out.println("Can't Find matching hashKey in setNodeData.");
+ System.out.println("We're in TROUBLE!!!");
+ }
+ }
+ }
+ // Make the VSG's viewLists as the relavant one for its children
+ s.viewLists = viewLists;
+
+ }
+
+ void setAuxData(SetLiveState s, int index, int hkIndex) {
+ ArrayList vl;
+ ArrayList parentList = null;
+ int size = apiViewList.size();
+ if (s.viewLists != null) {
+ // System.out.println("=====> VSG: = "+this+" hkIndex = "+hkIndex+" s.viewLists = "+s.viewLists);
+ parentList = (ArrayList) s.viewLists.get(hkIndex);
+ if (parentList != null) {
+ vl = new ArrayList();
+ for (int i = 0; i < size; i++) {
+ Object obj1 = apiViewList.get(i);
+ // Get the intersection of the parentlist and this vsg's api list
+ for (int j = 0; j < parentList.size(); j++) {
+ if (obj1 == parentList.get(j)) {
+ vl.add(obj1);
+ break;
+ }
+ }
+ }
+ }
+ else {
+ vl = new ArrayList();
+ // Only include the non null ones in the apiViewList
+ for (int i = 0; i < size; i++) {
+ Object obj = apiViewList.get(i);
+ if (obj != null) {
+ vl.add(obj);
+ }
+ }
+ }
+ }
+ else {
+ vl = new ArrayList();
+ // Only include the non null ones in the apiViewList
+ for (int i = 0; i < size; i++) {
+ Object obj = apiViewList.get(i);
+ if (obj != null) {
+ vl.add(obj);
+ }
+ }
+ }
+ if (parentList != null) {
+ parentLists.add(hkIndex, parentList.clone());
+ }
+ else {
+ parentLists.add(hkIndex, null);
+ }
+
+ viewLists.add(hkIndex,vl);
+ s.changedViewGroup.add(this);
+ s.changedViewList.add(vl);
+ if (localToVworldKeys != null) {
+ s.keyList[s.changedViewGroup.size() -1] = hkIndex;
+ }
+ else {
+ s.keyList[s.changedViewGroup.size() -1] = 0;
+ }
+
+
+
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/VirtualUniverse.java b/src/classes/share/javax/media/j3d/VirtualUniverse.java
new file mode 100644
index 0000000..9ac8940
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/VirtualUniverse.java
@@ -0,0 +1,910 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Map;
+
+/**
+ * A VirtualUniverse object is the top-level container for all scene
+ * graphs. A virtual universe consists of a set of Locale objects,
+ * each of which has a high-resolution position within the virtual
+ * universe. An application or applet may have more than one
+ * VirtualUniverse objects, but many applications will need only one.
+ * Virtual universes are separate entities in that no node object may
+ * exist in more than one virtual universe at any one time. Likewise,
+ * the objects in one virtual universe are not visible in, nor do they
+ * interact with objects in, any other virtual universe.
+ * <p>
+ * A VirtualUniverse object defines methods to enumerate its Locale
+ * objects and to remove them from the virtual universe.
+ *
+ * @see Locale
+ */
+
+public class VirtualUniverse extends Object {
+ // NOTE TO DEVELOPERS:
+ //
+ // Developers who modify Java 3D in any way are required to modify
+ // the auxiliary implementation vendor string in VersionInfo.java.
+ // See that file for instructions.
+
+ // The global MasterControl object. There is only one of these
+ // for all of Java 3D.
+ static MasterControl mc = null;
+
+ // The lock to acquire before traversing the scene graph
+ Object sceneGraphLock = new Object();
+ Object behaviorLock = new Object();
+
+ // A list of locales that are contained within this universe
+ Vector listOfLocales = new Vector();
+
+ // The list of view platforms.
+ ArrayList viewPlatforms = new ArrayList();
+
+
+ // The cached list of vp's
+ Object[] viewPlatformList = null;
+
+ // A flag that indicates that the list of view platforms has changed
+ boolean vpChanged = false;
+
+ // The list of backgrounds
+ Vector backgrounds = new Vector();
+
+ // The list of clips
+ Vector clips = new Vector();
+
+ // The list of sounds
+ Vector sounds = new Vector();
+
+ // The list of soundscapes
+ Vector soundscapes = new Vector();
+
+ // The Behavior Scheduler Thread for this Virtual Universe.
+ BehaviorScheduler behaviorScheduler = null;
+
+
+ // The geometry structure for this Universe
+ GeometryStructure geometryStructure = null;
+
+ // The transform structure for this Universe
+ TransformStructure transformStructure = null;
+
+ // The behavior structure for this Universe
+ BehaviorStructure behaviorStructure = null;
+
+ // The sound structure for this Universe
+ SoundStructure soundStructure = null;
+
+ // The rendering attributes structure for this Universe
+ RenderingEnvironmentStructure renderingEnvironmentStructure = null;
+
+ // Reference count of users of the RenderingEnvironmentStructure
+ int renderingEnvironmentStructureRefCount = 0;
+
+ // This is a global counter for node id's.
+ long nodeIdCount = 0;
+
+ // This is a global counter for view id's.
+ int viewIdCount = 0;
+
+ // This is a vector of free nodeid's
+ Vector nodeIdFreeList = new Vector();
+
+ // This is a vector of free viewid's
+ ArrayList viewIdFreeList = new ArrayList();
+
+ // The number of nodes in this universe
+ int numNodes = 0;
+
+ // The State object used when branch graphs are added
+ SetLiveState setLiveState;
+
+ // This is an array of references to objects that need their mirror
+ // copies updated. It is updated by the traverser and emptied by
+ // the view thread.
+ ObjectUpdate[] updateObjects = new ObjectUpdate[16];
+
+ // The number of valid entries in updateObjects
+ int updateObjectsLen = 0;
+
+ // A list of all mirror geometry object that are dirty
+ ArrayList dirtyGeomList = new ArrayList();
+
+ // The current primary view for this universe
+ View currentView;
+
+ // A flag to indicate that we are in a behavior routine
+ boolean inBehavior = false;
+
+ // Flags to indicate if events need to be delivered
+ boolean enableComponent = false;
+ boolean enableFocus = false;
+ boolean enableKey = false;
+ boolean enableMouse = false;
+ boolean enableMouseMotion = false;
+
+ // Keep track of how many active View use this universe
+ int activeViewCount = 0;
+
+ // Root ThreadGroup for creating Java 3D threads
+ static ThreadGroup rootThreadGroup;
+
+ // Lock for MasterControl Object creation
+ static Object mcLock = new Object();
+
+ // Properties object for getProperties
+ private static J3dQueryProps properties = null;
+
+ // Flag to indicate that user thread has to
+ // stop until MC completely register/unregister View.
+ View regViewWaiting = null;
+ View unRegViewWaiting = null;
+ boolean isSceneGraphLock = false;
+
+ private Object waitLock = new Object();
+
+ /**
+ * Constructs a new VirtualUniverse.
+ */
+ public VirtualUniverse() {
+ setLiveState = new SetLiveState(this);
+ initMCStructure();
+ }
+
+
+ void initMCStructure() {
+ if (geometryStructure != null) {
+ geometryStructure.cleanup();
+ }
+ geometryStructure = new GeometryStructure(this);
+ if (transformStructure != null) {
+ transformStructure.cleanup();
+ }
+ transformStructure = new TransformStructure(this);
+ if (behaviorStructure != null) {
+ behaviorStructure.cleanup();
+ }
+ behaviorStructure = new BehaviorStructure(this);
+ if (soundStructure != null) {
+ soundStructure.cleanup();
+ }
+ soundStructure = new SoundStructure(this);
+ if (renderingEnvironmentStructure != null) {
+ renderingEnvironmentStructure.cleanup();
+ }
+ renderingEnvironmentStructure = new
+ RenderingEnvironmentStructure(this);
+
+ }
+
+ static void createMC() {
+ synchronized (mcLock) {
+ if (mc == null) {
+ mc = new MasterControl();
+ }
+ }
+ }
+
+ static void destroyMC() {
+ synchronized (mcLock) {
+ mc = null;
+ }
+ }
+
+ /**
+ * Initialize the native interface and anything else that needs
+ * to be initialized.
+ */
+ static void loadLibraries() {
+ // No need to do anything. The act of calling any method in this
+ // class is sufficient to cause the static MasterControl object
+ // to be created which, in turn, loads the native libraries.
+ }
+
+ static {
+ if(J3dBuildInfo.isDebug) {
+ System.out.println("Initializing Java 3D runtime system:");
+ System.out.println(" version = " + VersionInfo.getVersion());
+ System.out.println(" vendor = " + VersionInfo.getVendor());
+ System.out.println(" specification.version = " +
+ VersionInfo.getSpecificationVersion());
+ System.out.println(" specification.vendor = " +
+ VersionInfo.getSpecificationVendor());
+ }
+
+ MasterControl.loadLibraries();
+ createMC();
+
+ if(J3dBuildInfo.isDebug) {
+ System.out.println("Java 3D system initialized");
+ System.out.println();
+ }
+ }
+
+ /**
+ * Adds a locale at the end of list of locales
+ * @param locale the locale to be added
+ */
+ void addLocale(Locale locale) {
+ listOfLocales.addElement(locale);
+ }
+
+ /**
+ * Removes a Locale and its associates branch graphs from this
+ * universe. All branch graphs within the specified Locale are
+ * detached, regardless of whether their ALLOW_DETACH capability
+ * bits are set. The Locale is then marked as being dead: no
+ * branch graphs may subsequently be attached.
+ *
+ * @param locale the Locale to be removed.
+ *
+ * @exception IllegalArgumentException if the specified Locale is not
+ * attached to this VirtualUniverse.
+ *
+ * @since Java 3D 1.2
+ */
+ public void removeLocale(Locale locale) {
+ if (locale.getVirtualUniverse() != this) {
+ throw new IllegalArgumentException(J3dI18N.getString("VirtualUniverse0"));
+ }
+
+ listOfLocales.removeElement(locale);
+ locale.removeFromUniverse();
+ if (isEmpty()) {
+ VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE,
+ this);
+ }
+ setLiveState.reset(null);
+ }
+
+
+ /**
+ * Removes all Locales and their associates branch graphs from
+ * this universe. All branch graphs within each Locale are
+ * detached, regardless of whether their ALLOW_DETACH capability
+ * bits are set. Each Locale is then marked as being dead: no
+ * branch graphs may subsequently be attached. This method
+ * should be called by applications and applets to allow
+ * Java 3D to cleanup its resources.
+ *
+ * @since Java 3D 1.2
+ */
+ public void removeAllLocales() {
+ // NOTE: this is safe because Locale.removeFromUniverse does not
+ // remove the Locale from the listOfLocales
+ int i;
+
+
+ for (i = listOfLocales.size()-1; i > 0; i--) {
+ ((Locale)listOfLocales.get(i)).removeFromUniverse();
+ }
+
+ if (i >= 0) {
+ // We have to clear() the listOfLocales first before
+ // invoke the last removeFromUniverse() so that isEmpty()
+ // (call from View.deactivate() ) will return true and
+ // threads can destroy from MC.
+ Locale loc = (Locale) listOfLocales.get(0);
+ listOfLocales.clear();
+ loc.removeFromUniverse();
+ }
+ VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE,
+ this);
+
+ setLiveState.reset(null);
+ }
+
+
+ /**
+ * Returns the enumeration object of all locales in this virtual universe.
+ * @return the enumeration object
+ */
+ public Enumeration getAllLocales() {
+ return this.listOfLocales.elements();
+ }
+
+ /**
+ * Returns the number of locales.
+ * @return the count of locales
+ */
+ public int numLocales() {
+ return this.listOfLocales.size();
+ }
+
+
+ /**
+ * Sets the priority of all Java 3D threads to the specified
+ * value. The default value is the priority of the thread that
+ * started Java 3D.
+ *
+ * @param priority the new thread priority
+ *
+ * @exception IllegalArgumentException if the priority is not in
+ * the range MIN_PRIORITY to MAX_PRIORITY
+ *
+ * @exception SecurityException if the priority is greater than
+ * that of the calling thread
+ *
+ * @since Java 3D 1.2
+ */
+ public static void setJ3DThreadPriority(int priority) {
+ if (priority > Thread.MAX_PRIORITY) {
+ priority = Thread.MAX_PRIORITY;
+ } else if (priority < Thread.MIN_PRIORITY) {
+ priority = Thread.MIN_PRIORITY;
+ }
+ VirtualUniverse.mc.setThreadPriority(priority);
+ }
+
+
+ /**
+ * Retrieves that priority of Java 3D's threads.
+ *
+ * @return the current priority of Java 3D's threads
+ *
+ * @since Java 3D 1.2
+ */
+ public static int getJ3DThreadPriority() {
+ return VirtualUniverse.mc.getThreadPriority();
+ }
+
+
+ /**
+ * Returns a read-only Map object containing key-value pairs that
+ * define various global properties for Java 3D. All of the keys
+ * are String objects. The values are key-specific, but most will
+ * be String objects.
+ *
+ * <p>
+ * The set of global Java 3D properties always includes values for
+ * the following keys:
+ *
+ * <p>
+ * <ul>
+ * <table BORDER=1 CELLSPACING=1 CELLPADDING=1>
+ * <tr>
+ * <td><b>Key (String)</b></td>
+ * <td><b>Value Type</b></td>
+ * </tr>
+ * <tr>
+ * <td><code>j3d.version</code></td>
+ * <td>String</td>
+ * </tr>
+ * <tr>
+ * <td><code>j3d.vendor</code></td>
+ * <td>String</td>
+ * </tr>
+ * <tr>
+ * <td><code>j3d.specification.version</code></td>
+ * <td>String</td>
+ * </tr>
+ * <tr>
+ * <td><code>j3d.specification.vendor</code></td>
+ * <td>String</td>
+ * </tr>
+ * <tr>
+ * <td><code>j3d.renderer</code></td>
+ * <td>String</td>
+ * </tr>
+ * </table>
+ * </ul>
+ *
+ * <p>
+ * The descriptions of the values returned for each key are as follows:
+ *
+ * <p>
+ * <ul>
+ *
+ * <li>
+ * <code>j3d.version</code>
+ * <ul>
+ * A String that defines the Java 3D implementation version.
+ * The portion of the implementation version string before the first
+ * space must adhere to one of the the following three formats
+ * (anything after the first space is an optional free-form addendum
+ * to the version):
+ * <ul>
+ * <i>x</i>.<i>y</i>.<i>z</i><br>
+ * <i>x</i>.<i>y</i>.<i>z</i>_<i>p</i><br>
+ * <i>x</i>.<i>y</i>.<i>z</i>-<i>ssss</i><br>
+ * </ul>
+ * where:
+ * <ul>
+ * <i>x</i> is the major version number<br>
+ * <i>y</i> is the minor version number<br>
+ * <i>z</i> is the sub-minor version number<br>
+ * <i>p</i> is the patch revision number <br>
+ * <i>ssss</i> is a string, identifying a non-release build
+ * (e.g., beta1, build47, rc1, etc.). It may only
+ * contain letters, numbers, periods, dashes, or
+ * underscores.
+ * </ul>
+ * </ul>
+ * </li>
+ * <p>
+ *
+ * <li>
+ * <code>j3d.vendor</code>
+ * <ul>
+ * String that specifies the Java 3D implementation vendor.
+ * </ul>
+ * </li>
+ * <p>
+ *
+ * <li>
+ * <code>j3d.specification.version</code>
+ * <ul>
+ * A String that defines the Java 3D specification version.
+ * This string must be of the following form:
+ * <ul>
+ * <i>x</i>.<i>y</i>
+ * </ul>
+ * where:
+ * <ul>
+ * <i>x</i> is the major version number<br>
+ * <i>y</i> is the minor version number<br>
+ * </ul>
+ * No other characters are allowed in the specification version string.
+ * </ul>
+ * </li>
+ * <p>
+ *
+ * <li>
+ * <code>j3d.specification.vendor</code>
+ * <ul>
+ * String that specifies the Java 3D specification vendor.
+ * </ul>
+ * </li>
+ * <p>
+ *
+ * <li>
+ * <code>j3d.renderer</code>
+ * <ul>
+ * String that specifies the Java 3D rendering library. This could
+ * be one of: "OpenGL" or "DirectX".
+ * </ul>
+ * </li>
+ * <p>
+ *
+ * </ul>
+ *
+ * @return the global Java 3D properties
+ *
+ * @since Java 3D 1.3
+ */
+ public static final Map getProperties() {
+ if (properties == null) {
+ // Create lists of keys and values
+ ArrayList keys = new ArrayList();
+ ArrayList values = new ArrayList();
+
+ // Implementation version string is obtained from the
+ // ImplementationVersion class.
+ keys.add("j3d.version");
+ values.add(VersionInfo.getVersion());
+
+ keys.add("j3d.vendor");
+ values.add(VersionInfo.getVendor());
+
+ keys.add("j3d.specification.version");
+ values.add(VersionInfo.getSpecificationVersion());
+
+ keys.add("j3d.specification.vendor");
+ values.add(VersionInfo.getSpecificationVendor());
+
+ keys.add("j3d.renderer");
+ values.add(mc.isD3D() ? "DirectX" : "OpenGL");
+
+ // Now Create read-only properties object
+ properties =
+ new J3dQueryProps((String[]) keys.toArray(new String[0]),
+ values.toArray());
+ }
+ return properties;
+ }
+
+
+ /**
+ * This returns the next available nodeId as a string.
+ */
+ // TODO: reuse of id's imply a slight collision problem in the
+ // render queue's.
+ // BUG 4181362
+ String getNodeId() {
+ String str;
+
+ if (nodeIdFreeList.size() == 0) {
+ str = Long.toString(nodeIdCount);
+ nodeIdCount++;
+ } else {
+ str = (String) nodeIdFreeList.lastElement();
+ nodeIdFreeList.removeElement(str);
+ }
+ return(str);
+ }
+
+ /**
+ * This returns the next available viewId
+ */
+ Integer getViewId() {
+ Integer id;
+ int size;
+
+ synchronized (viewIdFreeList) {
+ size = viewIdFreeList.size();
+ if (size == 0) {
+ id = new Integer(viewIdCount++);
+ } else {
+ id = (Integer) viewIdFreeList.remove(size-1);
+ }
+ }
+ return(id);
+ }
+
+ /**
+ * This returns a viewId to the freelist
+ */
+ void addViewIdToFreeList(Integer viewId) {
+ synchronized (viewIdFreeList) {
+ viewIdFreeList.add(viewId);
+ }
+ }
+
+ void addViewPlatform(ViewPlatformRetained vp) {
+ vpChanged = true;
+ viewPlatforms.add(vp);
+ }
+
+ void removeViewPlatform(ViewPlatformRetained vp) {
+ vpChanged = true;
+ viewPlatforms.remove(viewPlatforms.indexOf(vp));
+ }
+
+ synchronized Object[] getViewPlatformList() {
+ if (vpChanged) {
+ viewPlatformList = viewPlatforms.toArray();
+ vpChanged = false;
+ }
+ return viewPlatformList;
+ }
+
+ void checkForEnableEvents() {
+ enableComponentEvents();
+ if (enableFocus) {
+ enableFocusEvents();
+ }
+ if (enableKey) {
+ enableKeyEvents();
+ }
+ if (enableMouse) {
+ enableMouseEvents();
+ }
+ if (enableMouseMotion) {
+ enableMouseMotionEvents();
+ }
+ }
+
+ void enableComponentEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ ViewPlatformRetained vp;
+ View views[];
+ Object[] vps = getViewPlatformList();
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.enableComponentEvents();
+ }
+ }
+ }
+ }
+ }
+
+ void disableFocusEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ ViewPlatformRetained vp;
+ View views[];
+ Object[] vps = getViewPlatformList();
+ enableFocus = false;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.disableFocusEvents();
+ }
+ }
+ }
+ }
+
+ }
+
+ void enableFocusEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ ViewPlatformRetained vp;
+ View views[];
+ Object[] vps = getViewPlatformList();
+ enableFocus = true;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.enableFocusEvents();
+ }
+ }
+ }
+ }
+ }
+
+
+ void disableKeyEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ ViewPlatformRetained vp;
+ Object[] vps = getViewPlatformList();
+ View views[];
+
+ enableKey = false;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.disableKeyEvents();
+ }
+ }
+ }
+ }
+ }
+
+
+ void enableKeyEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ ViewPlatformRetained vp;
+ Object[] vps = getViewPlatformList();
+ View views[];
+
+ enableKey = true;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.enableKeyEvents();
+ }
+ }
+ }
+ }
+ }
+
+
+ void disableMouseEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ View views[];
+ ViewPlatformRetained vp;
+ Object[] vps = getViewPlatformList();
+
+ enableMouse = false;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.disableMouseEvents();
+ }
+ }
+ }
+ }
+ }
+
+ void enableMouseEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ View views[];
+ ViewPlatformRetained vp;
+ Object[] vps = getViewPlatformList();
+
+ enableMouse = true;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.enableMouseEvents();
+ }
+ }
+ }
+ }
+ }
+
+
+ void disableMouseMotionEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ View views[];
+ ViewPlatformRetained vp;
+ Object[] vps = getViewPlatformList();
+
+ enableMouseMotion = false;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.disableMouseMotionEvents();
+ }
+ }
+ }
+ }
+ }
+
+ void enableMouseMotionEvents() {
+ Enumeration cvs;
+ Canvas3D cv;
+ View views[];
+ ViewPlatformRetained vp;
+ Object[] vps = getViewPlatformList();
+
+ enableMouseMotion = true;
+
+ if (vps != null) {
+ for (int i=0; i<vps.length; i++) {
+ vp =(ViewPlatformRetained)vps[i];
+ views = vp.getViewList();
+ for (int j=views.length-1; j>=0; j--) {
+ cvs = views[j].getAllCanvas3Ds();
+ while(cvs.hasMoreElements()) {
+ cv = (Canvas3D) cvs.nextElement();
+ // offscreen canvas does not have event catcher
+ if (cv.eventCatcher != null)
+ cv.eventCatcher.enableMouseMotionEvents();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the "current" view (during view activation) for this virtual
+ * universe.
+ * @param last activated view
+ */
+ final void setCurrentView(View view) {
+ this.currentView = view;
+ }
+
+ /**
+ * Returns the "current" view (the last view activated for this virtual
+ * universe.
+ * @return last activated view
+ */
+ final View getCurrentView() {
+ return this.currentView;
+ }
+
+
+ /**
+ * Method to return the root thread group. This must be called from
+ * within a doPrivileged block.
+ */
+ static ThreadGroup getRootThreadGroup() {
+ return rootThreadGroup;
+ }
+
+ /**
+ * return true if all Locales under it don't have branchGroup
+ * attach to it.
+ */
+ boolean isEmpty() {
+ Enumeration elm = listOfLocales.elements();
+
+ while (elm.hasMoreElements()) {
+ Locale loc = (Locale) elm.nextElement();
+ if (!loc.branchGroups.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void resetWaitMCFlag() {
+ synchronized (waitLock) {
+ regViewWaiting = null;
+ unRegViewWaiting = null;
+ isSceneGraphLock = true;
+ }
+ }
+
+ void waitForMC() {
+ synchronized (waitLock) {
+ if (unRegViewWaiting != null) {
+ if ((regViewWaiting == null) ||
+ (regViewWaiting != unRegViewWaiting)) {
+ while (!unRegViewWaiting.doneUnregister) {
+ MasterControl.threadYield();
+ }
+ unRegViewWaiting.doneUnregister = false;
+ unRegViewWaiting = null;
+ }
+ }
+
+ if (regViewWaiting != null) {
+ while (!VirtualUniverse.mc.isRegistered(regViewWaiting)) {
+ MasterControl.threadYield();
+ }
+ regViewWaiting = null;
+ }
+ isSceneGraphLock = false;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupAnd.java b/src/classes/share/javax/media/j3d/WakeupAnd.java
new file mode 100644
index 0000000..2649077
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupAnd.java
@@ -0,0 +1,115 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+
+/**
+ * Class specifying any number of wakeup conditions ANDed together.
+ * This WakeupCondition object specifies that Java 3D should awaken
+ * this Behavior when all of the WakeupCondition's constituent wakeup
+ * criteria become valid.
+ * <p>
+ * Note that a unique WakeupCriterion object must be used
+ * for each individual element in the array of wakeup criteria.
+ */
+
+public final class WakeupAnd extends WakeupCondition {
+
+ WakeupCriterion conditions[];
+ boolean conditionsMet[];
+
+ /**
+ * Constructs a new WakeupAnd criterion.
+ * @param conditions a vector of individual Wakeup conditions
+ */
+ public WakeupAnd(WakeupCriterion conditions[]) {
+ this.conditions = new WakeupCriterion[conditions.length];
+ this.conditionsMet = new boolean[conditions.length];
+
+ for(int i = 0; i < conditions.length; i++){
+ this.conditions[i] = conditions[i];
+ // It is false by default when array is initialized.
+ // this.conditionsMet[i] = false;
+ }
+ }
+
+
+ /**
+ * This sets the bit for the given child, then checks if the full condition is met
+ */
+ void setConditionMet(int id, Boolean checkSchedulingRegion) {
+ conditionsMet[id] = true;
+
+ for (int i=0; i<this.conditionsMet.length; i++) {
+ if (!conditionsMet[i]) {
+ return;
+ }
+ }
+
+ if (parent == null) {
+ super.setConditionMet(this.id, checkSchedulingRegion);
+ } else {
+ parent.setConditionMet(this.id, checkSchedulingRegion);
+ }
+ }
+
+ /**
+ * This gets called when this condition is added to the AndOr tree.
+ */
+ void buildTree(WakeupCondition parent, int id, BehaviorRetained b) {
+ super.buildTree(parent, id, b);
+
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].buildTree(this, i, b);
+ }
+ }
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure lists
+ */
+ void cleanTree(BehaviorStructure bs) {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i].cleanTree(bs);
+ conditionsMet[i] = false;
+ }
+ }
+
+
+ void reInsertElapseTimeCond() {
+ super.reInsertElapseTimeCond();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].reInsertElapseTimeCond();
+ }
+ }
+ }
+
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure.
+ */
+ void resetTree() {
+ super.resetTree();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].resetTree();
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupAndOfOrs.java b/src/classes/share/javax/media/j3d/WakeupAndOfOrs.java
new file mode 100644
index 0000000..44a3316
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupAndOfOrs.java
@@ -0,0 +1,116 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+
+/**
+ * Class specifying any number of OR wakeup conditions ANDed together.
+ * This WakeupCondition object specifies that Java 3D should awaken this
+ * Behavior when all of the WakeupCondition's constituent WakeupOr
+ * conditions become valid.
+ * <p>
+ * Note that a unique WakeupCriterion object must be used for each
+ * individual element in the set of arrays specified by the array of
+ * WakeupOr objects.
+ */
+
+public final class WakeupAndOfOrs extends WakeupCondition {
+
+ WakeupOr conditions[];
+ boolean conditionsMet[];
+
+ /**
+ * Constructs a new WakeupAndOfOrs criterion.
+ * @param conditions a vector of individual Wakeup conditions
+ */
+ public WakeupAndOfOrs(WakeupOr conditions[]) {
+ this.conditions = new WakeupOr[conditions.length];
+ this.conditionsMet = new boolean[conditions.length];
+
+ for(int i = 0; i < conditions.length; i++){
+ this.conditions[i] = conditions[i];
+ // conditionsMet is false by default when it is initilized
+ // this.conditionsMet[i] = false;
+ }
+ }
+
+
+ /**
+ * This sets the bit for the given child, then checks if the full condition is met
+ */
+ void setConditionMet(int id, Boolean checkSchedulingRegion) {
+ conditionsMet[id] = true;
+
+ for (int i=0; i<this.conditionsMet.length; i++) {
+ if (!conditionsMet[i]) {
+ return;
+ }
+ }
+
+ if (parent == null) {
+ super.setConditionMet(this.id, checkSchedulingRegion);
+ } else {
+ parent.setConditionMet(this.id, checkSchedulingRegion);
+ }
+ }
+
+ /**
+ * This gets called when this condition is added to the AndOr tree.
+ */
+ void buildTree(WakeupCondition parent, int id, BehaviorRetained b) {
+
+ super.buildTree(parent, id, b);
+
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].buildTree(this, i, b);
+ }
+ }
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure lists
+ */
+ void cleanTree(BehaviorStructure bs) {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i].cleanTree(bs);
+ conditionsMet[i] = false;
+ }
+ }
+
+
+ void reInsertElapseTimeCond() {
+ super.reInsertElapseTimeCond();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].reInsertElapseTimeCond();
+ }
+ }
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure.
+ */
+ void resetTree() {
+ super.resetTree();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].resetTree();
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupCondition.java b/src/classes/share/javax/media/j3d/WakeupCondition.java
new file mode 100644
index 0000000..ebe9572
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupCondition.java
@@ -0,0 +1,132 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+
+/**
+ * An abstract class specifying a single wakeup Condition. This class
+ * is extended by the WakeupCriterion, WakeupOr, WakeupAnd,
+ * WakeupOrOfAnds, and WakeupAndOfOr classes. A Behavior node hands a
+ * WakeupCondition object to the behavior scheduler and the behavior
+ * scheduler hands back an enumeration of that WakeupCondition.
+ */
+
+public abstract class WakeupCondition extends Object {
+
+ static final int ALL_ELEMENTS = 0;
+ static final int TRIGGERED_ELEMENTS = 1;
+
+ /**
+ * This boolean indicates whether this condition has been fully met.
+ */
+ boolean conditionMet = false;
+
+ /**
+ * This points to the parent of this criterion in the AndOr tree
+ */
+ WakeupCondition parent = null;
+
+ /**
+ * The location of this criterion in the parents array.
+ */
+ int id;
+
+ /**
+ * The BehaviorRetained node that is using this condition
+ */
+ BehaviorRetained behav = null;
+
+ /**
+ * This is the allElements enumerator
+ */
+ WakeupCriteriaEnumerator allEnum = null;
+
+ /**
+ * This is the triggeredElements enumerator
+ */
+ WakeupCriteriaEnumerator trigEnum = null;
+
+ // Use in WakeupIndexedList
+ int listIdx[][];
+
+ /**
+ * Returns an enumeration of all WakeupCriterias in this Condition.
+ */
+ public Enumeration allElements() {
+ if (allEnum == null) {
+ allEnum = new WakeupCriteriaEnumerator(this, ALL_ELEMENTS);
+ } else {
+ allEnum.reset(this, ALL_ELEMENTS);
+ }
+ return allEnum;
+ }
+
+ /**
+ * Returns an enumeration of all triggered WakeupCriterias in this Condition.
+ */
+ public Enumeration triggeredElements() {
+ if (trigEnum == null) {
+ trigEnum = new WakeupCriteriaEnumerator(this, TRIGGERED_ELEMENTS);
+ } else {
+ trigEnum.reset(this, TRIGGERED_ELEMENTS);
+ }
+ return trigEnum;
+ }
+
+ /**
+ * this sets the conditionMet flag.
+ */
+ void setConditionMet(int id, Boolean checkSchedulingRegion) {
+
+ if (!conditionMet) {
+ conditionMet = true;
+ J3dMessage message = VirtualUniverse.mc.getMessage();
+ message.type = J3dMessage.COND_MET;
+ message.threads = J3dThread.UPDATE_BEHAVIOR;
+ message.universe = behav.universe;
+ message.args[0] = behav;
+ message.args[1] = checkSchedulingRegion;
+ message.args[2] = this;
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ /**
+ * Initialize And/Or tree and add criterion to the BehaviourStructure
+ */
+ void buildTree(WakeupCondition parent, int id, BehaviorRetained b){
+ this.parent = parent;
+ this.behav = b;
+ this.id = id;
+ conditionMet = false;
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure.
+ * We can't use behav.universe.behaviorStructure since behav
+ * may reassign to another universe at this time.
+ */
+ void cleanTree(BehaviorStructure bs) {
+ conditionMet = false;
+ }
+
+ void reInsertElapseTimeCond() {
+ conditionMet = false;
+ }
+
+ void resetTree() {
+ conditionMet = false;
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupCriteriaEnumerator.java b/src/classes/share/javax/media/j3d/WakeupCriteriaEnumerator.java
new file mode 100644
index 0000000..1b6f1c9
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupCriteriaEnumerator.java
@@ -0,0 +1,133 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * A class that enumerates all wakeup criteria in a wakeup condition
+ */
+
+class WakeupCriteriaEnumerator implements Enumeration {
+
+ // An array used for the current criteria in this object
+ WakeupCriterion[] criterion = null;
+
+ // A pointer to the current criteria
+ int currentIndex = 0;
+
+ // The number of valid criteria in the array, may be less than criterion.length
+ int length = 0;
+
+ WakeupCriteriaEnumerator(WakeupCondition cond, int type) {
+ this.reset(cond, type);
+ }
+
+ void reset(WakeupCondition cond, int type) {
+ int i, j;
+
+ currentIndex = 0;
+ length = 0;
+ if (cond instanceof WakeupCriterion) {
+ WakeupCriterion crit = (WakeupCriterion)cond;
+
+ if (criterion == null || criterion.length < 1) {
+ criterion = new WakeupCriterion[1];
+ }
+ if (crit.triggered || type == WakeupCondition.ALL_ELEMENTS) {
+ criterion[0] = crit;
+ length = 1;
+ }
+ } else {
+ if (cond instanceof WakeupAnd) {
+ WakeupAnd condAnd = (WakeupAnd)cond;
+
+ if (criterion == null || criterion.length < condAnd.conditions.length) {
+ criterion = new WakeupCriterion[condAnd.conditions.length];
+ }
+ for (i=0; i<condAnd.conditions.length; i++) {
+ if (condAnd.conditions[i].triggered || type == WakeupCondition.ALL_ELEMENTS) {
+ criterion[length++] = condAnd.conditions[i];
+ }
+ }
+ } else if (cond instanceof WakeupOr) {
+ WakeupOr condOr = (WakeupOr)cond;
+
+ if (criterion == null || criterion.length < condOr.conditions.length) {
+ criterion = new WakeupCriterion[condOr.conditions.length];
+ }
+ for (i=0; i<condOr.conditions.length; i++) {
+ if (condOr.conditions[i].triggered || type == WakeupCondition.ALL_ELEMENTS) {
+ criterion[length++] = condOr.conditions[i];
+ }
+ }
+ } else if (cond instanceof WakeupOrOfAnds) {
+ WakeupOrOfAnds condOrOfAnds = (WakeupOrOfAnds)cond;
+ int lengthNeeded = 0;
+
+ for (i=0; i<condOrOfAnds.conditions.length; i++) {
+ lengthNeeded += condOrOfAnds.conditions[i].conditions.length;
+ }
+
+ if (criterion == null || criterion.length < lengthNeeded) {
+ criterion = new WakeupCriterion[lengthNeeded];
+ }
+
+ for (i=0; i<condOrOfAnds.conditions.length; i++) {
+ for (j=0; j<condOrOfAnds.conditions[i].conditions.length; j++) {
+ if (condOrOfAnds.conditions[i].conditions[j].triggered ||
+ type == WakeupCondition.ALL_ELEMENTS) {
+ criterion[length++] = condOrOfAnds.conditions[i].conditions[j];
+ }
+ }
+ }
+ } else {
+ WakeupAndOfOrs condAndOfOrs = (WakeupAndOfOrs)cond;
+ int lengthNeeded = 0;
+
+ for (i=0; i<condAndOfOrs.conditions.length; i++) {
+ lengthNeeded += condAndOfOrs.conditions[i].conditions.length;
+ }
+
+ if (criterion == null || criterion.length < lengthNeeded) {
+ criterion = new WakeupCriterion[lengthNeeded];
+ }
+
+ for (i=0; i<condAndOfOrs.conditions.length; i++) {
+ for (j=0; j<condAndOfOrs.conditions[i].conditions.length; j++) {
+ if (condAndOfOrs.conditions[i].conditions[j].triggered ||
+ type == WakeupCondition.ALL_ELEMENTS) {
+ criterion[length++] = condAndOfOrs.conditions[i].conditions[j];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public boolean hasMoreElements() {
+ if (currentIndex == length) {
+ return false;
+ }
+ return true;
+ }
+
+ public Object nextElement() {
+ if (currentIndex < length) {
+ return ((Object)criterion[currentIndex++]);
+ } else {
+ throw new NoSuchElementException(J3dI18N.getString("WakeupCriteriaEnumerator0"));
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupCriterion.java b/src/classes/share/javax/media/j3d/WakeupCriterion.java
new file mode 100644
index 0000000..a19d93d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupCriterion.java
@@ -0,0 +1,108 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * An abstract class specifying a singleton wakeup Criterion. This
+ * class consists of several subclasses, each of which specifies one
+ * particular wakeup criterion, that criterion's associated arguments
+ * (if any), and either a flag that indicates whether this criterion
+ * caused a Behavior object to awaken or a return field containing the
+ * information that caused the Behavior object to awaken.
+ * <p>
+ * Note that a unique WakeupCriterion object must be used with each instance
+ * of a Behavior. Sharing wakeup criteria among different instances of
+ * a Behavior is illegal. Similarly, a unique WakeupCriterion object
+ * must be used for each individual element in the set of arrays used
+ * to construct WakeupOr, WakeupAnd, WakeupOrOfAnds, and
+ * WakeupAndOfOrs objects.
+ */
+
+public abstract class WakeupCriterion extends WakeupCondition {
+
+ /**
+ * Flag specifying whether this criterion triggered a wakeup
+ */
+ boolean triggered;
+
+ /**
+ * Returns true if this criterion triggered the wakeup.
+ * @return true if this criterion triggered the wakeup.
+ */
+ public boolean hasTriggered(){
+ return this.triggered;
+ }
+
+ /**
+ * Set the Criterion's trigger flag to true.
+ */
+ void setTriggered(){
+ this.triggered = true;
+ if (this.parent == null) {
+ super.setConditionMet(id, Boolean.TRUE);
+ } else {
+ parent.setConditionMet(id, Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Initialize And/Or tree and add criterion to the BehaviourStructure.
+ *
+ */
+ void buildTree(WakeupCondition parent, int id, BehaviorRetained b) {
+ super.buildTree(parent, id, b);
+ triggered = false;
+ addBehaviorCondition(b.universe.behaviorStructure);
+ }
+
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure.
+ * We can't use behav.universe.behaviorStructure since behav
+ * may reassign to another universe at this time.
+ *
+ */
+ void cleanTree(BehaviorStructure bs){
+ conditionMet = false;
+ removeBehaviorCondition(bs);
+ };
+
+
+ /**
+ * This goes through the AndOr tree to reset various criterion.
+ */
+ void resetTree() {
+ conditionMet = false;
+ triggered = false;
+ resetBehaviorCondition(behav.universe.behaviorStructure);
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ abstract void addBehaviorCondition(BehaviorStructure bs);
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ abstract void removeBehaviorCondition(BehaviorStructure bs);
+
+ /**
+ * It is used reset wakeupCondition when it is reused.
+ */
+ abstract void resetBehaviorCondition(BehaviorStructure bs);
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupIndexedList.java b/src/classes/share/javax/media/j3d/WakeupIndexedList.java
new file mode 100644
index 0000000..c033086
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupIndexedList.java
@@ -0,0 +1,584 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A strongly type unorder indexed array list.
+ * All operations remove(WakeupCondition), add(WakeupCondition),
+ * contains(WakeupCondition) etc. take O(1) time.
+ * The class is designed to optimize speed. So many reductance
+ * procedures call and range check as found in ArrayList are
+ * removed.
+ *
+ * <p>
+ * Use the following code to iterate through an array.
+ *
+ * <pre>
+ * WakeupIndexedList list = new WakeupIndexedList(YourClass.class);
+ * // add element here
+ *
+ * YourClass[] arr = (YourClass []) list.toArray();
+ * int size = list.arraySize();
+ * for (int i=0; i < size; i++) {
+ * YourClass obj = arr[i];
+ * ....
+ * }
+ * </pre>
+ *
+ * <p>
+ * Note:
+ * <ul>
+ * 1) The array return is a copied of internal array.<br>
+ * 2) Don't use arr.length , use list.arraySize();<br>
+ * 3) No need to do casting for individual element as in
+ * ArrayList.<br>
+ * 4) WakeupIndexedList is thread safe.<br>
+ * 5) Object implement this interface MUST initialize the index
+ * to -1.<br>
+ * </ul>
+ *
+ * <p>
+ * Limitation:
+ * <ul>
+ * - Same element can't add in two different WakeupIndexedList<br>
+ * - Order of WakeupCondition is not important<br>
+ * - Can't modify the clone() copy.<br>
+ * - Object can't be null<br>
+ * </ul>
+ */
+
+class WakeupIndexedList implements Cloneable, java.io.Serializable {
+
+ // TODO: set to false when release
+ final static boolean debug = false;
+
+ /**
+ * The array buffer into which the elements of the ArrayList are stored.
+ * The capacity of the ArrayList is the length of this array buffer.
+ *
+ * It is non-private to enable compiler do inlining for get(),
+ * set(), remove() when -O flag turn on.
+ */
+ transient WakeupCondition elementData[];
+
+ /**
+ * Clone copy of elementData return by toArray(true);
+ */
+ transient Object cloneData[];
+ // size of the above clone objec.
+ transient int cloneSize;
+
+ transient boolean isDirty = true;
+
+ /**
+ * Component Type of individual array element entry
+ */
+ Class componentType;
+
+ /**
+ * The size of the ArrayList (the number of elements it contains).
+ *
+ * We make it non-private to enable compiler do inlining for
+ * getSize() when -O flag turn on.
+ */
+ int size;
+
+ int listType;
+
+ // Current VirtualUniverse using this structure
+ VirtualUniverse univ;
+
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ * and the class data Type
+ *
+ * @param initialCapacity the initial capacity of the list.
+ * @param componentType class type of element in the list.
+ */
+ WakeupIndexedList(int initialCapacity, Class componentType,
+ int listType, VirtualUniverse univ) {
+ this.componentType = componentType;
+ this.elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
+ componentType, initialCapacity);
+ this.listType = listType;
+ this.univ = univ;
+ }
+
+ /**
+ * Constructs an empty list.
+ * @param componentType class type of element in the list.
+ */
+ WakeupIndexedList(Class componentType, int listType,
+ VirtualUniverse univ) {
+ this(10, componentType, listType, univ);
+ }
+
+
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ *
+ * @param initialCapacity the initial capacity of the list.
+ */
+ WakeupIndexedList(int initialCapacity, int listType,
+ VirtualUniverse univ) {
+ this(initialCapacity, WakeupCondition.class, listType, univ);
+ }
+
+
+ /**
+ * Constructs an empty list.
+ * componentType default to Object.
+ */
+ WakeupIndexedList(int listType, VirtualUniverse univ) {
+ this(10, WakeupCondition.class, listType, univ);
+ }
+
+
+ /**
+ * Initialize all indexes to -1
+ */
+ final static void init(WakeupCondition obj, int len) {
+ obj.listIdx = new int[2][len];
+
+ for (int i=0; i < len; i++) {
+ obj.listIdx[0][i] = -1;
+ obj.listIdx[1][i] = -1;
+ }
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ final int size() {
+ return size;
+ }
+
+ /**
+ * Returns the size of entry use in toArray() number of elements
+ * in this list.
+ *
+ * @return the number of elements in this list.
+ */
+ final int arraySize() {
+ return cloneSize;
+ }
+
+ /**
+ * Tests if this list has no elements.
+ *
+ * @return <tt>true</tt> if this list has no elements;
+ * <tt>false</tt> otherwise.
+ */
+ final boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this list contains the specified element.
+ *
+ * @param o element whose presence in this List is to be tested.
+ */
+ synchronized final boolean contains(WakeupCondition o) {
+ return (o.listIdx[o.behav.getIdxUsed(univ)][listType] >= 0);
+ }
+
+
+ /**
+ * Searches for the last occurence of the given argument, testing
+ * for equality using the <tt>equals</tt> method.
+ *
+ * @param o an object.
+ * @return the index of the first occurrence of the argument in this
+ * list; returns <tt>-1</tt> if the object is not found.
+ * @see Object#equals(Object)
+ */
+ synchronized final int indexOf(WakeupCondition o) {
+ return o.listIdx[o.behav.getIdxUsed(univ)][listType];
+ }
+
+ /**
+ * Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
+ * elements themselves are not copied.)
+ *
+ * @return a clone of this <tt>ArrayList</tt> instance.
+ */
+ synchronized protected final Object clone() {
+ try {
+ WakeupIndexedList v = (WakeupIndexedList)super.clone();
+ v.elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
+ componentType, size);
+ System.arraycopy(elementData, 0, v.elementData, 0, size);
+ isDirty = true; // can't use the old cloneData reference
+ return v;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+ }
+
+
+ /**
+ * Returns an array containing all of the elements in this list.
+ * The size of the array may longer than the actual size. Use
+ * arraySize() to retrieve the size.
+ * The array return is a copied of internal array. if copy
+ * is true.
+ *
+ * @return an array containing all of the elements in this list
+ */
+ synchronized final Object[] toArray(boolean copy) {
+ if (copy) {
+ if (isDirty) {
+ if ((cloneData == null) || cloneData.length < size) {
+ cloneData = (Object[])java.lang.reflect.Array.newInstance(
+ componentType, size);
+ }
+ System.arraycopy(elementData, 0, cloneData, 0, size);
+ cloneSize = size;
+ isDirty = false;
+ }
+ return cloneData;
+ } else {
+ cloneSize = size;
+ return elementData;
+ }
+
+ }
+
+ /**
+ * Returns an array containing all of the elements in this list.
+ * The size of the array may longer than the actual size. Use
+ * arraySize() to retrieve the size.
+ * The array return is a copied of internal array. So another
+ * thread can continue add/delete the current list. However,
+ * it should be noticed that two call to toArray() may return
+ * the same copy.
+ *
+ * @return an array containing all of the elements in this list
+ */
+ synchronized final Object[] toArray() {
+ return toArray(true);
+ }
+
+
+ /**
+ * Returns an array containing elements starting from startElement
+ * all of the elements in this list. A new array of exact size
+ * is always allocated.
+ *
+ * @param startElement starting element to copy
+ *
+ * @return an array containing elements starting from
+ * startElement, null if element not found.
+ *
+ */
+ synchronized final Object[] toArray(WakeupCondition startElement) {
+ int idx = indexOf(startElement);
+ if (idx < 0) {
+ return (Object[])java.lang.reflect.Array.newInstance(componentType, 0);
+ }
+
+ int s = size - idx;
+ Object data[] = (Object[])java.lang.reflect.Array.newInstance(componentType, s);
+ System.arraycopy(elementData, idx, data, 0, s);
+ return data;
+ }
+
+ /**
+ * Trims the capacity of this <tt>ArrayList</tt> instance to be the
+ * list's current size. An application can use this operation to minimize
+ * the storage of an <tt>ArrayList</tt> instance.
+ */
+ synchronized final void trimToSize() {
+ if (elementData.length > size) {
+ Object oldData[] = elementData;
+ elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
+ componentType,
+ size);
+ System.arraycopy(oldData, 0, elementData, 0, size);
+ }
+ }
+
+
+ // Positional Access Operations
+
+ /**
+ * Returns the element at the specified position in this list.
+ *
+ * @param index index of element to return.
+ * @return the element at the specified position in this list.
+ * @throws IndexOutOfBoundsException if index is out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final Object get(int index) {
+ return elementData[index];
+ }
+
+ /**
+ * Replaces the element at the specified position in this list with
+ * the specified element.
+ *
+ * @param index index of element to replace.
+ * @param o element to be stored at the specified position.
+ * @return the element previously at the specified position.
+ * @throws IndexOutOfBoundsException if index out of range
+ * <tt>(index &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final void set(int index, WakeupCondition o) {
+
+ WakeupCondition oldElm = elementData[index];
+ if (oldElm != null) {
+ oldElm.listIdx[oldElm.behav.getIdxUsed(univ)][listType] = -1;
+ }
+ elementData[index] = o;
+
+ int univIdx = o.behav.getIdxUsed(univ);
+
+ if (debug) {
+ if (o.listIdx[univIdx][listType] != -1) {
+ System.out.println("Illegal use of UnorderIndexedList idx in set " +
+ o.listIdx[univIdx][listType]);
+ Thread.dumpStack();
+ }
+ }
+
+ o.listIdx[univIdx][listType] = index;
+ isDirty = true;
+ }
+
+ /**
+ * Appends the specified element to the end of this list.
+ * It is the user responsible to ensure that the element add is of
+ * the same type as array componentType.
+ *
+ * @param o element to be appended to this list.
+ */
+ synchronized final void add(WakeupCondition o) {
+ if (elementData.length == size) {
+ WakeupCondition oldData[] = elementData;
+ elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
+ componentType,
+ (size << 1));
+ System.arraycopy(oldData, 0, elementData, 0, size);
+
+ }
+
+ int univIdx = o.behav.getIdxUsed(univ);
+ // System.out.println(this + " add " + o + " univ " + univIdx);
+ if (debug) {
+ int idx = o.listIdx[univIdx][listType];
+ if (idx >= 0) {
+ if (elementData[idx] != o) {
+ System.out.println("Illegal use of UnorderIndexedList idx in add " + idx);
+ Thread.dumpStack();
+ }
+ }
+ }
+
+ int idx = size++;
+ elementData[idx] = o;
+ o.listIdx[univIdx][listType] = idx;
+ isDirty = true;
+ }
+
+
+ /**
+ * Removes the element at the specified position in this list.
+ * Replace the removed element by the last one.
+ *
+ * @param index the index of the element to removed.
+ * @throws IndexOutOfBoundsException if index out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final void remove(int index) {
+ WakeupCondition elm = elementData[index];
+ int univIdx = elm.behav.getIdxUsed(univ);
+
+ if (debug) {
+ if (elm.listIdx[univIdx][listType] != index) {
+ System.out.println("Inconsistent idx in remove, expect " + index +
+ " actual " + elm.listIdx[univIdx][listType]);
+ Thread.dumpStack();
+ }
+ }
+
+ elm.listIdx[univIdx][listType] = -1;
+ size--;
+ if (index != size) {
+ elm = elementData[size];
+ elm.listIdx[univIdx][listType] = index;
+ elementData[index] = elm;
+ }
+ elementData[size] = null;
+ isDirty = true;
+ /*
+ if ((cloneData != null) && (index < cloneData.length)) {
+ cloneData[index] = null; // for gc
+ }
+ */
+ }
+
+
+ /**
+ * Removes the element at the last position in this list.
+ * @return The element remove
+ * @throws IndexOutOfBoundsException if array is empty
+ */
+ synchronized final Object removeLastElement() {
+ WakeupCondition elm = elementData[--size];
+ elementData[size] = null;
+ elm.listIdx[elm.behav.getIdxUsed(univ)][listType] = -1;
+ isDirty = true;
+ /*
+ if ((cloneData != null) && (size < cloneData.length)) {
+ cloneData[size] = null; // for gc
+ }
+ */
+ return elm;
+ }
+
+
+ /**
+ * Removes the specified element in this list.
+ * Replace the removed element by the last one.
+ *
+ * @param o the element to removed.
+ * @return true if object remove
+ * @throws IndexOutOfBoundsException if index out of range <tt>(index
+ * &lt; 0 || index &gt;= size())</tt>.
+ */
+ synchronized final boolean remove(WakeupCondition o) {
+ int univIdx = o.behav.getIdxUsed(univ);
+ int idx = o.listIdx[univIdx][listType];
+
+ // System.out.println(this + " remove " + o + " univ " + univIdx);
+
+ if (idx >= 0) {
+ // Object in the container
+ if (debug) {
+ if (o != elementData[idx]) {
+ System.out.println(" Illegal use of UnorderIndexedList in remove expect " + o + " actual " + elementData[idx] + " idx = " + idx);
+ Thread.dumpStack();
+ }
+ }
+ size--;
+ if (idx != size) {
+ WakeupCondition elm = elementData[size];
+ elementData[idx] = elm;
+ elm.listIdx[elm.behav.getIdxUsed(univ)][listType] = idx;
+ }
+ elementData[size] = null;
+ o.listIdx[univIdx][listType] = -1;
+ isDirty = true;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Removes all of the elements from this list. The list will
+ * be empty after this call returns.
+ */
+ synchronized final void clear() {
+ WakeupCondition o;
+
+ for (int i = size-1; i >= 0; i--) {
+ o = elementData[i];
+ o.listIdx[o.behav.getIdxUsed(univ)][listType] = -1;
+ elementData[i] = null; // Let gc do its work
+ }
+
+ size = 0;
+ isDirty = true;
+ }
+
+ synchronized final void clearMirror() {
+ if (cloneData != null) {
+ for (int i = cloneData.length-1; i >= 0; i--) {
+ // don't set index to -1 since the original
+ // copy is using this.
+ cloneData[i] = null; // Let gc do its work
+ }
+ }
+ cloneSize = 0;
+ isDirty = true;
+ }
+
+ final Class getComponentType() {
+ return componentType;
+ }
+
+ synchronized public String toString() {
+ StringBuffer sb = new StringBuffer(hashCode() + " Size = " + size + "[");
+ int len = size-1;
+ Object obj;
+
+ for (int i=0; i < size; i++) {
+ obj = elementData[i];
+ if (obj != null) {
+ sb.append(elementData[i].toString());
+ } else {
+ sb.append("NULL");
+ }
+ if (i != len) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /**
+ * Save the state of the <tt>ArrayList</tt> instance to a stream (that
+ * is, serialize it).
+ *
+ * @serialData The length of the array backing the <tt>ArrayList</tt>
+ * instance is emitted (int), followed by all of its elements
+ * (each an <tt>Object</tt>) in the proper order.
+ */
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException{
+ // Write out element count, and any hidden stuff
+ s.defaultWriteObject();
+
+ // Write out array length
+ s.writeInt(elementData.length);
+
+ // Write out all elements in the proper order.
+ for (int i=0; i<size; i++)
+ s.writeObject(elementData[i]);
+
+ }
+
+ /**
+ * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
+ * deserialize it).
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ // Read in size, and any hidden stuff
+ s.defaultReadObject();
+
+ // Read in array length and allocate array
+ int arrayLength = s.readInt();
+ elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
+ componentType, arrayLength);
+
+ // Read in all elements in the proper order.
+ for (int i=0; i<size; i++)
+ elementData[i] = (WakeupCondition) s.readObject();
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnAWTEvent.java b/src/classes/share/javax/media/j3d/WakeupOnAWTEvent.java
new file mode 100644
index 0000000..8c81612
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnAWTEvent.java
@@ -0,0 +1,145 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.awt.AWTEvent;
+import java.awt.event.*;
+import java.util.Vector;
+
+/**
+ * Class that specifies a Behavior wakeup when a specific AWT event occurs.
+ */
+public final class WakeupOnAWTEvent extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 1;
+
+ // one of these two variables must be equal to zero
+ int AwtId;
+ long EventMask;
+ long enableAWTEventTS = 0L;
+ Vector events = new Vector();
+
+ /**
+ * Constructs a new WakeupOnAWTEvent object that informs the Java 3D
+ * scheduler to wake up the specified Behavior object whenever the
+ * specified AWT event occurs.
+ * @param AWTId the AWT ids that this behavior wishes to intercept
+ */
+ public WakeupOnAWTEvent(int AWTId) {
+ this.AwtId = AWTId;
+ this.EventMask = 0L;
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Constructs a new WakeupOnAWTEvent using Ored EVENT_MASK values.
+ * @param eventMask the AWT EVENT_MASK values Ored together
+ */
+ public WakeupOnAWTEvent(long eventMask) {
+ this.EventMask = eventMask;
+ this.AwtId = 0;
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Retrieves the array of consecutive AWT event that triggered this wakeup.
+ * A value of null implies that this event was not the trigger for the
+ * behavior wakeup.
+ * @return either null (if not resposible for wakeup) or the array of
+ * AWTEvents responsible for the wakeup.
+ */
+ public AWTEvent[] getAWTEvent(){
+ AWTEvent eventArray[];
+
+ synchronized (events) {
+ eventArray = new AWTEvent[events.size()];
+ events.copyInto(eventArray);
+ events.removeAllElements();
+ }
+
+ return eventArray;
+ }
+
+
+ /**
+ * Sets the AWT event that will cause a behavior wakeup.
+ * @param event The event causing this wakeup
+ */
+ void addAWTEvent(AWTEvent event){
+ events.addElement(event);
+ this.setTriggered();
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ resetBehaviorCondition(bs);
+ bs.wakeupOnAWTEvent.add(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ bs.wakeupOnAWTEvent.remove(this);
+ }
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+
+ if (enableAWTEventTS != bs.awtEventTimestamp) {
+ if ((AwtId >= ComponentEvent.COMPONENT_FIRST &&
+ AwtId <= ComponentEvent.COMPONENT_LAST) ||
+ (EventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) {
+ behav.universe.enableComponentEvents();
+ }
+ if ((AwtId >= FocusEvent.FOCUS_FIRST && AwtId <= FocusEvent.FOCUS_LAST) ||
+ (EventMask & AWTEvent.FOCUS_EVENT_MASK) != 0) {
+ behav.universe.enableFocusEvents();
+ }
+ if ((AwtId >= KeyEvent.KEY_FIRST && AwtId <= KeyEvent.KEY_LAST) ||
+ (EventMask & AWTEvent.KEY_EVENT_MASK) != 0) {
+ behav.universe.enableKeyEvents();
+ }
+ if ((AwtId >= MouseEvent.MOUSE_FIRST) &&
+ (AwtId <= MouseEvent.MOUSE_LAST)) {
+ if ((AwtId == MouseEvent.MOUSE_DRAGGED) ||
+ (AwtId == MouseEvent.MOUSE_MOVED)) {
+ behav.universe.enableMouseMotionEvents();
+ } else {
+ behav.universe.enableMouseEvents();
+ }
+ } else {
+ if ((EventMask & AWTEvent.MOUSE_EVENT_MASK) != 0) {
+ behav.universe.enableMouseEvents();
+ }
+ if ((EventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0) {
+ behav.universe.enableMouseMotionEvents();
+ }
+ }
+ enableAWTEventTS = bs.awtEventTimestamp;
+ }
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnActivation.java b/src/classes/share/javax/media/j3d/WakeupOnActivation.java
new file mode 100644
index 0000000..56a646d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnActivation.java
@@ -0,0 +1,65 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup the first time an active Viewplatform's
+ * activation
+ * volume intersects with this object's scheduling region.
+ * This gives the behavior an explicit means of executing code when
+ * it is activated.
+ */
+public final class WakeupOnActivation extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 1;
+
+ /**
+ * Constructs a new WakeupOnActivation criterion.
+ */
+ public WakeupOnActivation() {
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ behav.wakeupArray[BehaviorRetained.WAKEUP_ACTIVATE_INDEX]++;
+ behav.wakeupMask |= BehaviorRetained.WAKEUP_ACTIVATE;
+ bs.wakeupOnActivation.add(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ behav.wakeupArray[BehaviorRetained.WAKEUP_ACTIVATE_INDEX]--;
+ if (behav.wakeupArray[BehaviorRetained.WAKEUP_ACTIVATE_INDEX] == 0) {
+ behav.wakeupMask &= ~BehaviorRetained.WAKEUP_ACTIVATE;
+ }
+ bs.wakeupOnActivation.remove(this);
+ }
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {}
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnBehaviorPost.java b/src/classes/share/javax/media/j3d/WakeupOnBehaviorPost.java
new file mode 100644
index 0000000..d8733e0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnBehaviorPost.java
@@ -0,0 +1,113 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class that specifies a Behavior wakeup when a specific behavior object
+ * posts a specific event
+ */
+public final class WakeupOnBehaviorPost extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 1;
+
+ Behavior armingBehavior, triggeringBehavior;
+ int post, triggeringPost;
+
+
+ /**
+ * Constructs a new WakeupOnBehaviorPost criterion. A behavior of null
+ * specifies a wakeup from any behavior on the specified postId. A postId
+ * of 0 specifies a wakeup on any postId from the specified behavior.
+ * A behavior of null AND a postId of 0 specify a wakeup on any postId
+ * from any behavior.
+ * @param behavior the behavior that must be the source of the post,
+ * if behavior == null, then any behavior posting the postId will cause
+ * the wakeup.
+ * @param postId the postId that will trigger a wakeup if posted by the
+ * specified behavior, if postId == 0, then any post by the specified
+ * behavior will cause the wakeup.
+ */
+ public WakeupOnBehaviorPost(Behavior behavior, int postId) {
+ this.armingBehavior = behavior;
+ this.post = postId;
+ triggeringPost = -1;
+ triggeringBehavior = null;
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Retrieve the WakeupCriterion's specified postId
+ * @return the post id specified in this object's construction.
+ */
+ public int getPostId(){
+ return post;
+ }
+
+
+ /**
+ * Returns the behavior specified in this object's constructor.
+ * @return the arming behavior
+ */
+ public Behavior getBehavior () {
+ return armingBehavior;
+ }
+
+
+ /**
+ * Returns the postId that caused the behavior to wakeup. If the postId
+ * used to construct this wakeup criterion was not zero, then the
+ * triggering postId will always be equal to the postId used in the
+ * constructor.
+ */
+ public int getTriggeringPostId() {
+ return triggeringPost;
+ }
+
+
+ /**
+ * Returns the behavior that triggered this wakeup. If the arming
+ * behavior used to construct this object was not null, then the
+ * triggering behavior will be the same as the arming behavior.
+ */
+ public Behavior getTriggeringBehavior() {
+ return triggeringBehavior;
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ bs.wakeupOnBehaviorPost.add(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ bs.wakeupOnBehaviorPost.remove(this);
+ }
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {}
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnCollisionEntry.java b/src/classes/share/javax/media/j3d/WakeupOnCollisionEntry.java
new file mode 100644
index 0000000..78386ee
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnCollisionEntry.java
@@ -0,0 +1,579 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * Class specifying a wakeup when the specified object
+ * collides with any other object in the scene graph.
+ *
+ */
+public final class WakeupOnCollisionEntry extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in GeometryStructure
+ static final int COND_IN_GS_LIST = 0;
+ static final int COLLIDEENTRY_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ /**
+ * Use geometry in computing collisions.
+ */
+ public static final int USE_GEOMETRY = 10;
+
+ /**
+ * Use geometric bounds as an approximation in computing collisions.
+ */
+ public static final int USE_BOUNDS = 11;
+
+ static final int GROUP = NodeRetained.GROUP;
+ static final int BOUNDINGLEAF = NodeRetained.BOUNDINGLEAF;
+ static final int SHAPE = NodeRetained.SHAPE;
+ static final int MORPH = NodeRetained.MORPH;
+ static final int ORIENTEDSHAPE3D = NodeRetained.ORIENTEDSHAPE3D;
+ static final int BOUND = 0;
+
+ /**
+ * Accuracy mode one of USE_GEOMETRY or USE_BOUNDS
+ */
+ int accuracyMode;
+
+ // Cached the arming Node being used when it is not BOUND
+ NodeRetained armingNode;
+
+ // A transformed Bounds of Group/Bounds, use by
+ // BOUND, GROUP
+ Bounds vwcBounds = null;
+
+ // Use by BoundingLeaf, point to mirror BoundingLeaf
+ // transformedRegion under this leaf is used.
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * Geometry atoms that this wakeup condition refer to.
+ * Only use by SHAPE, MORPH, GROUP, ORIENTEDSHAPE
+ */
+ UnorderList geometryAtoms = null;
+
+ // one of GROUP, BOUNDINGLEAF, SHAPE, MORPH, BOUND
+ int nodeType;
+
+ SceneGraphPath armingPath = null;
+ Bounds armingBounds = null;
+
+ // the following two references are set only after a collision
+ // has occurred
+ Bounds collidingBounds = null;
+ SceneGraphPath collidingPath = null;
+
+ /**
+ * Constructs a new WakeupOnCollisionEntry criterion with
+ * USE_BOUNDS for a speed hint.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or
+ * BoundingLeaf node.
+ */
+ public WakeupOnCollisionEntry(SceneGraphPath armingPath) {
+ this(armingPath, USE_BOUNDS);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionEntry criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or
+ * BoundingLeaf node.
+ */
+ public WakeupOnCollisionEntry(SceneGraphPath armingPath,
+ int speedHint) {
+ this(new SceneGraphPath(armingPath), speedHint, null);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionEntry criterion.
+ * @param armingNode the Group, Shape, or Morph node used to
+ * <em>arm</em> collision detection
+ * @exception IllegalArgumentException if object is under a
+ * SharedGroup node or object is other than a Group, Shape3D,
+ * Morph or BoundingLeaf node.
+ */
+ public WakeupOnCollisionEntry(Node armingNode) {
+ this(armingNode, USE_BOUNDS);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionEntry criterion.
+ * @param armingNode the Group, Shape, or Morph node used to
+ * <em>arm</em> collision detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object is under a
+ * SharedGroup node or object is other than a Group, Shape3D,
+ * Morph or BoundingLeaf node.
+ */
+ public WakeupOnCollisionEntry(Node armingNode, int speedHint) {
+ this(new SceneGraphPath(null, armingNode), speedHint, null);
+ }
+
+
+ /**
+ * Constructs a new WakeupOnCollisionEntry criterion.
+ * @param armingBounds the bounds object used to <em>arm</em> collision
+ * detection
+ */
+ public WakeupOnCollisionEntry(Bounds armingBounds) {
+ this(null, USE_BOUNDS, (Bounds) armingBounds.clone());
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionEntry criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @param armingBounds the bounds object used to <em>arm</em> collision
+ * detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or
+ * BoundingLeaf node.
+ */
+ WakeupOnCollisionEntry(SceneGraphPath armingPath,
+ int speedHint, Bounds armingBounds) {
+ if (armingPath != null) {
+ this.armingNode = (NodeRetained) armingPath.getObject().retained;
+ nodeType = getNodeType(armingNode, armingPath,
+ "WakeupOnCollisionEntry");
+ this.armingPath = armingPath;
+ validateSpeedHint(speedHint, "WakeupOnCollisionEntry4");
+ } else {
+ this.armingBounds = armingBounds;
+ nodeType = BOUND;
+ }
+ accuracyMode = speedHint;
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Returns the path used in specifying the collision condition.
+ * @return the SceneGraphPath object generated when arming this
+ * criterion---null implies that a bounds object armed this criteria
+ */
+ public SceneGraphPath getArmingPath() {
+ return (armingPath != null ?
+ new SceneGraphPath(armingPath) : null);
+ }
+
+ /**
+ * Returns the bounds object used in specifying the collision condition.
+ * @return the Bounds object generated when arming this
+ * criterion---null implies that a SceneGraphPath armed this criteria
+ */
+ public Bounds getArmingBounds() {
+ return (armingBounds != null ?
+ (Bounds)armingBounds.clone() : null);
+ }
+
+ /**
+ * Retrieves the path describing the object causing the collision.
+ * @return the SceneGraphPath that describes the triggering object.
+ * @exception IllegalStateException if not called from within the
+ * a behavior's processStimulus method which was awoken by a collision.
+ */
+ public SceneGraphPath getTriggeringPath() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionEntry5"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException
+ (J3dI18N.getString("WakeupOnCollisionEntry5"));
+ }
+ }
+ return (collidingPath != null ?
+ new SceneGraphPath(collidingPath): null);
+ }
+
+ /**
+ * Retrieves the Bounds object that caused the collision
+ * @return the colliding Bounds object.
+ * @exception IllegalStateException if not called from within the
+ * a behavior's processStimulus method which was awoken by a collision.
+ */
+ public Bounds getTriggeringBounds() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionEntry6"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException
+ (J3dI18N.getString("WakeupOnCollisionEntry6"));
+ }
+ }
+ return (collidingBounds != null ?
+ (Bounds)(collidingBounds.clone()): null);
+ }
+
+
+ /**
+ * Node legality checker
+ * throw Exception if node is not legal.
+ * @return nodeType
+ */
+ static int getNodeType(NodeRetained armingNode,
+ SceneGraphPath armingPath, String s)
+ throws IllegalArgumentException {
+
+ // check if SceneGraphPath is unique
+ // Note that graph may not live at this point so we
+ // can't use node.inSharedGroup.
+ if (!armingPath.validate()) {
+ throw new IllegalArgumentException(J3dI18N.getString(s + "7"));
+ }
+
+ if (armingNode.inBackgroundGroup) {
+ throw new IllegalArgumentException(J3dI18N.getString(s + "1"));
+ }
+
+ // This should come before Shape3DRetained check
+ if (armingNode instanceof OrientedShape3DRetained) {
+ return ORIENTEDSHAPE3D;
+ }
+
+ if (armingNode instanceof Shape3DRetained) {
+ return SHAPE;
+ }
+
+ if (armingNode instanceof MorphRetained) {
+ return MORPH;
+ }
+
+ if (armingNode instanceof GroupRetained) {
+ return GROUP;
+ }
+
+ if (armingNode instanceof BoundingLeafRetained) {
+ return BOUNDINGLEAF;
+ }
+
+ throw new IllegalArgumentException(J3dI18N.getString(s + "0"));
+ }
+
+ /**
+ * speedHint legality checker
+ * throw Exception if speedHint is not legal
+ */
+ static void validateSpeedHint(int speedHint, String s)
+ throws IllegalArgumentException {
+ if ((speedHint != USE_GEOMETRY) && (speedHint != USE_BOUNDS)) {
+ throw new IllegalArgumentException(J3dI18N.getString(s));
+ }
+
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+
+ switch (nodeType) {
+ case SHAPE: // Use geometryAtoms[].collisionBounds
+ case ORIENTEDSHAPE3D:
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ Shape3DRetained shape = (Shape3DRetained) armingNode;
+ geometryAtoms.add(Shape3DRetained.getGeomAtom(shape.getMirrorShape(armingPath)));
+ break;
+ case MORPH: // Use geometryAtoms[].collisionBounds
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ MorphRetained morph = (MorphRetained) armingNode;
+ geometryAtoms.add(Shape3DRetained.getGeomAtom(morph.getMirrorShape(armingPath)));
+ break;
+ case BOUNDINGLEAF: // use BoundingLeaf.transformedRegion
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ this.boundingLeaf = ((BoundingLeafRetained) armingNode).mirrorBoundingLeaf;
+ break;
+ case BOUND: // use this.vwcBounds
+ vwcBounds = (Bounds) armingBounds.clone();
+ this.armingNode = behav;
+ break;
+ case GROUP:
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (accuracyMode == USE_GEOMETRY) {
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
+ }
+ // else use this.vwcBounds
+ default:
+ }
+
+ behav.universe.geometryStructure.addWakeupOnCollision(this);
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ vwcBounds = null;
+ if (geometryAtoms != null) {
+ geometryAtoms.clear();
+ }
+ boundingLeaf = null;
+ behav.universe.geometryStructure.removeWakeupOnCollision(this);
+ }
+
+
+ // Set collidingPath & collidingBounds
+ void setTarget(BHLeafInterface leaf) {
+ SceneGraphPath path;
+ Bounds bound;
+
+ if (leaf instanceof GeometryAtom) {
+ // Find the triggered Path & Bounds for this geometry Atom
+ GeometryAtom geomAtom = (GeometryAtom) leaf;
+ Shape3DRetained shape = geomAtom.source;
+
+ path = getSceneGraphPath(shape.sourceNode,
+ shape.key,
+ shape.getCurrentLocalToVworld(0));
+ bound = getTriggeringBounds(shape);
+
+ } else {
+ // Find the triggered Path & Bounds for this alternative
+ // collision target
+ GroupRetained group = (GroupRetained) leaf;
+ path = getSceneGraphPath(group);
+ bound = getTriggeringBounds(group);
+ }
+
+ if (path != null) {
+ // colliding path may be null when branch detach before
+ // user behavior retrieve the previous colliding path
+ collidingPath = path;
+ collidingBounds = bound;
+ }
+ }
+
+
+ // Invoke from GeometryStructure to update vwcBounds of GROUP
+ void updateCollisionBounds(boolean reEvaluateGAs){
+ if (nodeType == GROUP) {
+ GroupRetained group = (GroupRetained) armingNode;
+ if (group.collisionBound != null) {
+ vwcBounds = (Bounds) group.collisionBound.clone();
+ } else {
+ // this may involve recursive tree traverse if
+ // BoundsAutoCompute is true, we can't avoid
+ // since the bound under it may change by transform
+ vwcBounds = group.getEffectiveBounds();
+ }
+ group.transformBounds(armingPath, vwcBounds);
+ } else if (nodeType == BOUND) {
+ vwcBounds.transform(armingBounds, behav.getCurrentLocalToVworld());
+ }
+
+ if (reEvaluateGAs &&
+ (nodeType == GROUP) &&
+ (accuracyMode == USE_GEOMETRY)) {
+ geometryAtoms.clear();
+ ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
+ }
+ }
+
+
+ /**
+ * Return the TriggeringBounds for node
+ */
+ static Bounds getTriggeringBounds(Shape3DRetained mirrorShape) {
+ NodeRetained node = mirrorShape.sourceNode;
+
+ if (node instanceof Shape3DRetained) {
+ Shape3DRetained shape = (Shape3DRetained) node;
+ if (shape.collisionBound == null) {
+ // TODO: get bounds by copy
+ return shape.getEffectiveBounds();
+ }
+ return shape.collisionBound;
+ }
+
+
+ MorphRetained morph = (MorphRetained) node;
+ if (morph.collisionBound == null) {
+ // TODO: get bounds by copy
+ return morph.getEffectiveBounds();
+ }
+ return morph.collisionBound;
+ }
+
+
+ /**
+ * Return the TriggeringBounds for node
+ */
+ static Bounds getTriggeringBounds(GroupRetained group) {
+ if (group.collisionBound == null) {
+ // TODO: get bounds by copy
+ return group.getEffectiveBounds();
+ }
+ return group.collisionBound;
+ }
+
+ static SceneGraphPath getSceneGraphPath(GroupRetained group) {
+ // Find the transform base on the key
+ Transform3D transform = null;
+ GroupRetained srcGroup = group.sourceNode;
+
+ synchronized (srcGroup.universe.sceneGraphLock) {
+ if (group.key == null) {
+ transform = srcGroup.getCurrentLocalToVworld();
+ } else {
+ HashKey keys[] = srcGroup.localToVworldKeys;
+ if (keys == null) {
+ // the branch is already detach when
+ // Collision got this message
+ return null;
+ }
+ transform = srcGroup.getCurrentLocalToVworld(group.key);
+ }
+ return getSceneGraphPath(srcGroup, group.key, transform);
+ }
+
+ }
+
+ /**
+ * return the SceneGraphPath of the geomAtom.
+ * Find the alternative Collision target closest to the locale.
+ */
+ static SceneGraphPath getSceneGraphPath(NodeRetained startNode,
+ HashKey key,
+ Transform3D transform) {
+ synchronized (startNode.universe.sceneGraphLock) {
+ NodeRetained target = startNode;
+
+ UnorderList path = new UnorderList(5, Node.class);
+ NodeRetained nodeR = target;
+ Locale locale = nodeR.locale;
+ String nodeId;
+ Vector parents;
+ NodeRetained linkR;
+
+ if (nodeR.inSharedGroup) {
+ // getlastNodeId() will destroy this key
+ if (key != null) {
+ key = new HashKey(key);
+ } else {
+ key = new HashKey(startNode.localToVworldKeys[0]);
+ }
+ }
+
+ do {
+ if (nodeR.source.getCapability(Node.ENABLE_COLLISION_REPORTING)){
+ path.add(nodeR.source);
+ }
+
+ if (nodeR instanceof SharedGroupRetained) {
+
+ // retrieve the last node ID
+ nodeId = key.getLastNodeId();
+ parents = ((SharedGroupRetained) nodeR).parents;
+ NodeRetained prevNodeR = nodeR;
+ for(int i=parents.size()-1; i >=0; i--) {
+ linkR = (NodeRetained) parents.elementAt(i);
+ if (linkR.nodeId.equals(nodeId)) {
+ nodeR = linkR;
+ break;
+ }
+ }
+ if (nodeR == prevNodeR) {
+ // the branch is already detach when
+ // Collision got this message
+ return null;
+ }
+ } else if ((nodeR instanceof GroupRetained) &&
+ ((GroupRetained) nodeR).collisionTarget) {
+ // we need to find the collision target closest to the
+ // root of tree
+ target = nodeR;
+
+ if (key == null) {
+ transform = nodeR.getCurrentLocalToVworld(null);
+ } else {
+ transform = nodeR.getCurrentLocalToVworld(key);
+ }
+ }
+ nodeR = nodeR.parent;
+ } while (nodeR != null); // reach Locale
+
+ Node nodes[];
+ if (target == startNode) { // in most case
+ nodes = (Node []) path.toArray(false);
+ } else { // alternativeCollisionTarget is set
+ nodes = (Node []) path.toArray(target);
+ }
+ SceneGraphPath sgpath = new SceneGraphPath(locale,
+ nodes,
+ (Node) target.source);
+ sgpath.setTransform(transform);
+ return sgpath;
+ }
+ }
+
+
+ void setTriggered(){
+ // if path not set, probably the branch is just detach.
+ if (collidingPath != null) {
+ super.setTriggered();
+ }
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+ // The reference geometryAtom will not change once
+ // Shape3D create so there is no need to set this.
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnCollisionExit.java b/src/classes/share/javax/media/j3d/WakeupOnCollisionExit.java
new file mode 100644
index 0000000..f34b0bd
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnCollisionExit.java
@@ -0,0 +1,377 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.*;
+
+/**
+ * Class specifying a wakeup when the specified object
+ * no longer collides with any other object in the scene graph.
+ */
+public final class WakeupOnCollisionExit extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in GeometryStructure
+ static final int COND_IN_GS_LIST = 0;
+ static final int COLLIDEEXIT_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ /**
+ * Use geometry in computing collisions.
+ */
+ public static final int USE_GEOMETRY = WakeupOnCollisionEntry.USE_GEOMETRY;
+
+ /**
+ * Use geometric bounds as an approximation in computing collisions.
+ */
+ public static final int USE_BOUNDS = WakeupOnCollisionEntry.USE_BOUNDS;
+
+ /**
+ * Accuracy mode one of USE_GEOMETRY or USE_BOUNDS
+ */
+ int accuracyMode;
+
+ // Cached the arming Node being used when it is not BOUND
+ NodeRetained armingNode;
+
+ // A transformed Bounds of Group/Bounds
+ // use by BOUND, GROUP
+ Bounds vwcBounds;
+
+
+ // use by GROUP to cache local bounds
+ Bounds localBounds = null;
+
+ // Use by BoundingLeaf, point to mirror BoundingLeaf
+ // transformedRegion under this leaf is used.
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * Geometry atoms that this wakeup condition refer to.
+ * Only use by SHAPE, MORPH, GROUP, ORIENTEDSHAPE
+ */
+ UnorderList geometryAtoms = null;
+
+ // one of GROUP, BOUNDINGLEAF, SHAPE, MORPH, BOUND
+ int nodeType;
+
+ SceneGraphPath armingPath = null;
+ Bounds armingBounds = null;
+
+ // the following two references are set only after a collision
+ // has occurred
+ SceneGraphPath collidingPath = null;
+ Bounds collidingBounds = null;
+
+ /**
+ * Constructs a new WakeupOnCollisionExit criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
+ */
+ public WakeupOnCollisionExit(SceneGraphPath armingPath) {
+ this(armingPath, USE_BOUNDS);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionExit criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
+ */
+ public WakeupOnCollisionExit(SceneGraphPath armingPath, int speedHint) {
+ this(new SceneGraphPath(armingPath), speedHint, null);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionExit criterion.
+ * @param armingNode the Group, Shape, or Morph node used to
+ * <em>arm</em> collision detection
+ * @exception IllegalArgumentException if object is under a
+ * SharedGroup node or object is other than a Group, Shape3D,
+ * Morph or BoundingLeaf node.
+ */
+ public WakeupOnCollisionExit(Node armingNode) {
+ this(armingNode, USE_BOUNDS);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionExit criterion.
+ * @param armingNode the Group, Shape, or Morph node used to
+ * <em>arm</em> collision detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object is under a
+ * SharedGroup node or object is other than a Group, Shape3D,
+ * Morph or BoundingLeaf node.
+ */
+ public WakeupOnCollisionExit(Node armingNode, int speedHint) {
+ this(new SceneGraphPath(null, armingNode), speedHint, null);
+ }
+
+
+ /**
+ * Constructs a new WakeupOnCollisionExit criterion.
+ * @param armingBounds the bounds object used to <em>arm</em> collision
+ * detection
+ */
+ public WakeupOnCollisionExit(Bounds armingBounds) {
+ this(null, USE_BOUNDS, (Bounds) armingBounds.clone());
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionExit criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @param armingBounds the bounds object used to <em>arm</em> collision
+ * detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
+ */
+ WakeupOnCollisionExit(SceneGraphPath armingPath,
+ int speedHint, Bounds armingBounds) {
+ if (armingPath != null) {
+ this.armingNode = (NodeRetained) armingPath.getObject().retained;
+ nodeType = WakeupOnCollisionEntry.getNodeType(armingNode, armingPath,
+ "WakeupOnCollisionExit");
+ this.armingPath = armingPath;
+ WakeupOnCollisionEntry.validateSpeedHint(speedHint,
+ "WakeupOnCollisionExit4");
+ } else {
+ this.armingBounds = armingBounds;
+ nodeType = WakeupOnCollisionEntry.BOUND;
+ }
+ accuracyMode = speedHint;
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Returns the path used in specifying the collision condition.
+ * @return the SceneGraphPath object generated when arming this
+ * criterion---null implies that a bounds object armed this criteria
+ */
+ public SceneGraphPath getArmingPath() {
+ return (armingPath != null ?
+ new SceneGraphPath(armingPath) : null);
+ }
+
+ /**
+ * Returns the bounds object used in specifying the collision condition.
+ * @return the Bounds object generated when arming this
+ * criterion---null implies that a SceneGraphPath armed this criteria
+ */
+ public Bounds getArmingBounds() {
+ return (armingBounds != null ?
+ (Bounds)armingBounds.clone() : null);
+ }
+
+ /**
+ * Retrieves the path describing the object causing the collision.
+ * @return the SceneGraphPath that describes the triggering object.
+ * @exception IllegalStateException if not called from within the
+ * a behavior's processStimulus method which was awoken by a collision.
+ */
+ public SceneGraphPath getTriggeringPath() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionExit5"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException
+ (J3dI18N.getString("WakeupOnCollisionExit5"));
+ }
+ }
+ return (collidingPath != null ?
+ new SceneGraphPath(collidingPath): null);
+ }
+
+ /**
+ * Retrieves the Bounds object that caused the collision
+ * @return the colliding Bounds object.
+ * @exception IllegalStateException if not called from within the
+ * a behavior's processStimulus method which was awoken by a collision.
+ */
+ public Bounds getTriggeringBounds() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionExit6"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException
+ (J3dI18N.getString("WakeupOnCollisionExit6"));
+ }
+ }
+ return (collidingBounds != null ?
+ (Bounds)(collidingBounds.clone()): null);
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+
+ switch (nodeType) {
+ case WakeupOnCollisionEntry.SHAPE: // Use geometryAtoms[].collisionBounds
+ case WakeupOnCollisionEntry.ORIENTEDSHAPE3D:
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ Shape3DRetained shape = (Shape3DRetained) armingNode;
+ geometryAtoms.add(Shape3DRetained.getGeomAtom(shape.getMirrorShape(armingPath)));
+ break;
+ case WakeupOnCollisionEntry.MORPH: // Use geometryAtoms[].collisionBounds
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ MorphRetained morph = (MorphRetained) armingNode;
+ geometryAtoms.add(Shape3DRetained.getGeomAtom(morph.getMirrorShape(armingPath)));
+ break;
+ case WakeupOnCollisionEntry.BOUNDINGLEAF: // use BoundingLeaf.transformedRegion
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ this.boundingLeaf = ((BoundingLeafRetained) armingNode).mirrorBoundingLeaf;
+ break;
+ case WakeupOnCollisionEntry.BOUND: // use this.vwcBounds
+ vwcBounds = (Bounds) armingBounds.clone();
+ this.armingNode = behav;
+ break;
+ case WakeupOnCollisionEntry.GROUP:
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (accuracyMode == USE_GEOMETRY) {
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
+ }
+ // else use this.vwcBounds
+ default:
+ }
+
+ behav.universe.geometryStructure.addWakeupOnCollision(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ vwcBounds = null;
+ if (geometryAtoms != null) {
+ geometryAtoms.clear();
+ }
+ boundingLeaf = null;
+ behav.universe.geometryStructure.removeWakeupOnCollision(this);
+ }
+
+
+
+ // Set collidingPath & collidingBounds
+ void setTarget(BHLeafInterface leaf) {
+ SceneGraphPath path;
+ Bounds bound;
+
+ if (leaf instanceof GeometryAtom) {
+ // Find the triggered Path & Bounds for this geometry Atom
+ GeometryAtom geomAtom = (GeometryAtom) leaf;
+ Shape3DRetained shape = geomAtom.source;
+ path = WakeupOnCollisionEntry.getSceneGraphPath(
+ shape.sourceNode,
+ shape.key,
+ shape.getCurrentLocalToVworld(0));
+ bound = WakeupOnCollisionEntry.getTriggeringBounds(shape);
+
+
+ } else {
+ // Find the triggered Path & Bounds for this alternative
+ // collision target
+ GroupRetained group = (GroupRetained) leaf;
+ path = WakeupOnCollisionEntry.getSceneGraphPath(group);
+ bound = WakeupOnCollisionEntry.getTriggeringBounds(group);
+ }
+
+ if (path != null) {
+ // colliding path may be null when branch detach before
+ // user behavior retrieve the previous colliding path
+ collidingPath = path;
+ collidingBounds = bound;
+ }
+
+ }
+
+ // Invoke from GeometryStructure to update vwcBounds of GROUP
+ void updateCollisionBounds(boolean reEvaluateGAs){
+ if (nodeType == WakeupOnCollisionEntry.GROUP) {
+ GroupRetained group = (GroupRetained) armingNode;
+ if (group.collisionBound != null) {
+ vwcBounds = (Bounds) group.collisionBound.clone();
+ } else {
+ // this may involve recursive tree traverse if
+ // BoundsAutoCompute is true, we can't avoid
+ // since the bound under it may change by transform
+ vwcBounds = group.getEffectiveBounds();
+ }
+ group.transformBounds(armingPath, vwcBounds);
+ } else if (nodeType == WakeupOnCollisionEntry.BOUND) {
+ vwcBounds.transform(armingBounds, behav.getCurrentLocalToVworld());
+ }
+
+ if (reEvaluateGAs &&
+ (nodeType == WakeupOnCollisionEntry.GROUP) &&
+ (accuracyMode == USE_GEOMETRY)) {
+ geometryAtoms.clear();
+ ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
+ }
+ }
+
+ void setTriggered(){
+ // if path not set, probably the branch is just detach.
+ if (collidingPath != null) {
+ super.setTriggered();
+ }
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+ // The reference geometryAtom will not change once
+ // Shape3D create so there is no need to set this.
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnCollisionMovement.java b/src/classes/share/javax/media/j3d/WakeupOnCollisionMovement.java
new file mode 100644
index 0000000..f7e160c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnCollisionMovement.java
@@ -0,0 +1,385 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.*;
+
+/**
+ * Class specifying a wakeup when the specified object
+ * moves while in collision with any other object in the scene graph.
+ */
+public final class WakeupOnCollisionMovement extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in GeometryStructure
+ static final int COND_IN_GS_LIST = 0;
+ static final int COLLIDEMOVE_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ /**
+ * Use geometry in computing collisions.
+ */
+ public static final int USE_GEOMETRY = WakeupOnCollisionEntry.USE_GEOMETRY;
+
+ /**
+ * Use geometric bounds as an approximation in computing collisions.
+ */
+ public static final int USE_BOUNDS = WakeupOnCollisionEntry.USE_BOUNDS;
+
+ /**
+ * Accuracy mode one of USE_GEOMETRY or USE_BOUNDS
+ */
+ int accuracyMode;
+
+ // Cached the arming Node being used when it is not BOUND
+ NodeRetained armingNode;
+
+ // transformed Bounds of Group/Bounds, use by
+ // BOUND, BOUNDINGLEAF, GROUP
+ Bounds vwcBounds;
+
+
+ // use by GROUP to cache local bounds
+ Bounds localBounds = null;
+
+ // source bound when collision occur last time
+ // These three variables are used to keep track of duplicate
+ // wakupOnMovement event
+ Bounds lastSrcBounds = null;
+ Bounds lastDstBounds = null;
+ boolean duplicateEvent = false;
+
+ // Use by BoundingLeaf, point to mirror BoundingLeaf
+ // transformedRegion under this leaf is used.
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * Geometry atoms that this wakeup condition refer to.
+ * Only use by SHAPE, MORPH, GROUP, ORIENTEDSHAPE
+ */
+ UnorderList geometryAtoms = null;
+
+ // one of GROUP, BOUNDINGLEAF, SHAPE, MORPH, BOUND
+ int nodeType;
+
+ SceneGraphPath armingPath = null;
+ Bounds armingBounds = null;
+
+ // the following two references are set only after a collision
+ // has occurred
+ SceneGraphPath collidingPath = null;
+ Bounds collidingBounds = null;
+
+ /**
+ * Constructs a new WakeupOnCollisionMovement criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
+ */
+ public WakeupOnCollisionMovement(SceneGraphPath armingPath) {
+ this(armingPath, USE_BOUNDS);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionMovement criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
+ */
+ public WakeupOnCollisionMovement(SceneGraphPath armingPath,
+ int speedHint) {
+ this(new SceneGraphPath(armingPath), speedHint, null);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionMovement criterion.
+ * @param armingNode the Group, Shape, or Morph node used to
+ * <em>arm</em> collision detection
+ * @exception IllegalArgumentException if object is under a
+ * SharedGroup node or object is other than a Group, Shape3D,
+ * Morph or BoundingLeaf node.
+ */
+ public WakeupOnCollisionMovement(Node armingNode) {
+ this(armingNode, USE_BOUNDS);
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionMovement criterion.
+ * @param armingNode the Group, Shape, or Morph node used to
+ * <em>arm</em> collision detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object is under a
+ * SharedGroup node or object is other than a Group, Shape3D,
+ * Morph or BoundingLeaf node.
+ */
+ public WakeupOnCollisionMovement(Node armingNode, int speedHint) {
+ this(new SceneGraphPath(null, armingNode), speedHint, null);
+ }
+
+
+ /**
+ * Constructs a new WakeupOnCollisionMovement criterion.
+ * @param armingBounds the bounds object used to <em>arm</em> collision
+ * detection
+ */
+ public WakeupOnCollisionMovement(Bounds armingBounds) {
+ this(null, USE_BOUNDS, (Bounds)armingBounds.clone());
+ }
+
+ /**
+ * Constructs a new WakeupOnCollisionMovement criterion.
+ * @param armingPath the path used to <em>arm</em> collision
+ * detection
+ * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
+ * accurately Java 3D will perform collision detection
+ * @param armingBounds the bounds object used to <em>arm</em> collision
+ * detection
+ * @exception IllegalArgumentException if hint is not one of
+ * USE_GEOMETRY or USE_BOUNDS.
+ * @exception IllegalArgumentException if object associated with the
+ * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
+ */
+ WakeupOnCollisionMovement(SceneGraphPath armingPath,
+ int speedHint, Bounds armingBounds) {
+ if (armingPath != null) {
+ this.armingNode = (NodeRetained) armingPath.getObject().retained;
+ nodeType = WakeupOnCollisionEntry.getNodeType(armingNode, armingPath,
+ "WakeupOnCollisionMovement");
+ this.armingPath = armingPath;
+ WakeupOnCollisionEntry.validateSpeedHint(speedHint,
+ "WakeupOnCollisionMovement4");
+ } else {
+ this.armingBounds = armingBounds;
+ nodeType = WakeupOnCollisionEntry.BOUND;
+ }
+ accuracyMode = speedHint;
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Returns the path used in specifying the collision condition.
+ * @return the SceneGraphPath object generated when arming this
+ * criterion---null implies that a bounds object armed this criteria
+ */
+ public SceneGraphPath getArmingPath() {
+ return (armingPath != null ?
+ new SceneGraphPath(armingPath) : null);
+ }
+
+ /**
+ * Returns the bounds object used in specifying the collision condition.
+ * @return the Bounds object generated when arming this
+ * criterion---null implies that a SceneGraphPath armed this criteria
+ */
+ public Bounds getArmingBounds() {
+ return (armingBounds != null ?
+ (Bounds)armingBounds.clone() : null);
+ }
+
+ /**
+ * Retrieves the path describing the object causing the collision.
+ * @return the SceneGraphPath that describes the triggering object.
+ * @exception IllegalStateException if not called from within the
+ * a behavior's processStimulus method which was awoken by a collision.
+ */
+ public SceneGraphPath getTriggeringPath() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionMovement5"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException
+ (J3dI18N.getString("WakeupOnCollisionMovement5"));
+ }
+ }
+ return (collidingPath != null ?
+ new SceneGraphPath(collidingPath): null);
+ }
+
+ /**
+ * Retrieves the Bounds object that caused the collision
+ * @return the colliding Bounds object.
+ * @exception IllegalStateException if not called from within the
+ * a behavior's processStimulus method which was awoken by a collision.
+ */
+ public Bounds getTriggeringBounds() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionMovement6"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException
+ (J3dI18N.getString("WakeupOnCollisionMovement6"));
+ }
+ }
+ return (collidingBounds != null ?
+ (Bounds)(collidingBounds.clone()): null);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+
+ switch (nodeType) {
+ case WakeupOnCollisionEntry.SHAPE: // Use geometryAtoms[].collisionBounds
+ case WakeupOnCollisionEntry.ORIENTEDSHAPE3D:
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ Shape3DRetained shape = (Shape3DRetained) armingNode;
+ geometryAtoms.add(Shape3DRetained.getGeomAtom(shape.getMirrorShape(armingPath)));
+ break;
+ case WakeupOnCollisionEntry.MORPH: // Use geometryAtoms[].collisionBounds
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ MorphRetained morph = (MorphRetained) armingNode;
+ geometryAtoms.add(Shape3DRetained.getGeomAtom(morph.getMirrorShape(armingPath)));
+ break;
+ case WakeupOnCollisionEntry.BOUNDINGLEAF: // use BoundingLeaf.transformedRegion
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ this.boundingLeaf = ((BoundingLeafRetained) armingNode).mirrorBoundingLeaf;
+ break;
+ case WakeupOnCollisionEntry.BOUND: // use this.vwcBounds
+ vwcBounds = (Bounds) armingBounds.clone();
+ this.armingNode = behav;
+ break;
+ case WakeupOnCollisionEntry.GROUP:
+ if (!armingNode.source.isLive()) {
+ return;
+ }
+ if (accuracyMode == USE_GEOMETRY) {
+ if (geometryAtoms == null) {
+ geometryAtoms = new UnorderList(1, GeometryAtom.class);
+ }
+ ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
+ }
+ // else use this.vwcBounds
+ default:
+ }
+
+ behav.universe.geometryStructure.addWakeupOnCollision(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ vwcBounds = null;
+ if (geometryAtoms != null) {
+ geometryAtoms.clear();
+ }
+ boundingLeaf = null;
+ behav.universe.geometryStructure.removeWakeupOnCollision(this);
+ }
+
+
+ // Set collidingPath & collidingBounds
+ void setTarget(BHLeafInterface leaf) {
+ SceneGraphPath path;
+ Bounds bound;
+
+ if (leaf instanceof GeometryAtom) {
+ // Find the triggered Path & Bounds for this geometry Atom
+ GeometryAtom geomAtom = (GeometryAtom) leaf;
+ Shape3DRetained shape = geomAtom.source;
+ path = WakeupOnCollisionEntry.getSceneGraphPath(
+ shape.sourceNode,
+ shape.key,
+ shape.getCurrentLocalToVworld(0));
+ bound = WakeupOnCollisionEntry.getTriggeringBounds(shape);
+
+ } else {
+ // Find the triggered Path & Bounds for this alternative
+ // collision target
+ GroupRetained group = (GroupRetained) leaf;
+ path = WakeupOnCollisionEntry.getSceneGraphPath(group);
+ bound = WakeupOnCollisionEntry.getTriggeringBounds(group);
+ }
+
+ if (path != null) {
+ // colliding path may be null when branch detach before
+ // user behavior retrieve the previous colliding path
+ collidingPath = path;
+ collidingBounds = bound;
+ }
+ }
+
+ // Invoke from GeometryStructure to update vwcBounds of GROUP
+ void updateCollisionBounds(boolean reEvaluateGAs) {
+ if (nodeType == WakeupOnCollisionEntry.GROUP) {
+ GroupRetained group = (GroupRetained) armingNode;
+ if (group.collisionBound != null) {
+ vwcBounds = (Bounds) group.collisionBound.clone();
+ } else {
+ // this may involve recursive tree traverse if
+ // BoundsAutoCompute is true, we can't avoid
+ // since the bound under it may change by transform
+ vwcBounds = group.getEffectiveBounds();
+ }
+ group.transformBounds(armingPath, vwcBounds);
+ } else if (nodeType == WakeupOnCollisionEntry.BOUND) {
+ vwcBounds.transform(armingBounds, behav.getCurrentLocalToVworld());
+ }
+
+
+ if (reEvaluateGAs &&
+ (nodeType == WakeupOnCollisionEntry.GROUP) &&
+ (accuracyMode == USE_GEOMETRY)) {
+ geometryAtoms.clear();
+ ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
+ }
+ }
+
+ void setTriggered(){
+ // if path not set, probably the branch is just detach.
+ if (collidingPath != null) {
+ super.setTriggered();
+ }
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+ // The reference geometryAtom will not change once
+ // Shape3D create so there is no need to set this.
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnDeactivation.java b/src/classes/share/javax/media/j3d/WakeupOnDeactivation.java
new file mode 100644
index 0000000..0635c14
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnDeactivation.java
@@ -0,0 +1,78 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup on first detection of a Viewplatform's
+ * activation volume no longer intersecting with this object's scheduling
+ * region. This gives the behavior an explicit means of executing code
+ * when it is deactivated.
+ */
+public final class WakeupOnDeactivation extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 1;
+
+ /**
+ * Constructs a new WakeupOnDeactivation criterion.
+ */
+ public WakeupOnDeactivation() {
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+
+ /**
+ * Set the Criterion's trigger flag to true.
+ * No need to check for scheduling region in this case
+ */
+ void setTriggered(){
+ this.triggered = true;
+ if (this.parent == null) {
+ super.setConditionMet(id, Boolean.FALSE);
+ } else {
+ parent.setConditionMet(id, Boolean.FALSE);
+ }
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ behav.wakeupArray[BehaviorRetained.WAKEUP_DEACTIVATE_INDEX]++;
+ behav.wakeupMask |= BehaviorRetained.WAKEUP_DEACTIVATE;
+ bs.wakeupOnDeactivation.add(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ behav.wakeupArray[BehaviorRetained.WAKEUP_DEACTIVATE_INDEX]--;
+ if (behav.wakeupArray[BehaviorRetained.WAKEUP_DEACTIVATE_INDEX] == 0) {
+ behav.wakeupMask &= ~BehaviorRetained.WAKEUP_DEACTIVATE;
+ }
+ bs.wakeupOnDeactivation.remove(this);
+ }
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {}
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnElapsedFrames.java b/src/classes/share/javax/media/j3d/WakeupOnElapsedFrames.java
new file mode 100644
index 0000000..256e7a2
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnElapsedFrames.java
@@ -0,0 +1,164 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup when a specific number of frames have
+ * elapsed. The wakeup criterion can either be passive or
+ * non-passive. If any behavior uses a non-passive
+ * WakeupOnElapsedFrames, the rendering system will run continuously.
+ *
+ * <p>
+ * In general, applications cannot count on behavior execution being
+ * synchronized with rendering. Behaviors that use
+ * WakeupOnElapsedFrames with a frame count of 0 are an exception to
+ * this general rule. Such behaviors will be executed every frame.
+ * Further, all modifications to scene graph objects (not including
+ * geometry by-reference or texture by-reference) made from the
+ * <code>processStimulus</code> methods of such behaviors are
+ * guaranteed to take effect in the same rendering frame.
+ */
+public final class WakeupOnElapsedFrames extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 1;
+
+ // Indicates whether the wakeup condition is passive or
+ // non-passive. Only behaviors using a non-passive
+ // WakeupOnElapsedFrames will force a continuous traversal.
+ boolean passive;
+
+ // Number of frames before wakeup
+ int frameCount;
+
+ // When this reaches 0, this criterion is met.
+ int countdown;
+
+ /**
+ * Constructs a non-passive WakeupOnElapsedFrames criterion.
+ *
+ * @param frameCount the number of frames that Java 3D should draw
+ * before awakening this behavior object; a value of N means
+ * wakeup at the end of frame N, where the current frame is zero,
+ * a value of zero means wakeup at the end of the current frame.
+ *
+ * @exception IllegalArgumentException if frameCount is less than zero
+ */
+ public WakeupOnElapsedFrames(int frameCount) {
+ this(frameCount, false);
+ }
+
+ /**
+ * Constructs a WakeupOnElapsedFrames criterion.
+ *
+ * @param frameCount the number of frames that Java 3D should draw
+ * before awakening this behavior object; a value of N means
+ * wakeup at the end of frame N, where the current frame is zero,
+ * a value of zero means wakeup at the end of the current frame.
+ *
+ * @param passive flag indicating whether this behavior is
+ * passive; a non-passive behavior will cause the rendering system
+ * to run continuously, while a passive behavior will only run
+ * when some other event causes a frame to be run.
+ *
+ * @exception IllegalArgumentException if frameCount is less than zero
+ *
+ * @since Java 3D 1.2
+ */
+ public WakeupOnElapsedFrames(int frameCount, boolean passive) {
+ if (frameCount < 0)
+ throw new IllegalArgumentException(J3dI18N.getString("WakeupOnElapsedFrames0"));
+
+ this.frameCount = frameCount;
+ this.passive = passive;
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Retrieves the elapsed frame count that was used when
+ * constructing this object.
+ *
+ * @return the elapsed frame count specified when constructing
+ * this object
+ */
+ public int getElapsedFrameCount() {
+ return frameCount;
+ }
+
+ /**
+ * Retrieves the state of the passive flag that was used when
+ * constructing this object.
+ *
+ * @return true if this wakeup criterion is passive, false otherwise
+ *
+ * @since Java 3D 1.2
+ */
+ public boolean isPassive() {
+ return passive;
+ }
+
+ /**
+ * decrement the frame count, and set trigger if 0
+ */
+ void newFrame() {
+ if (this.countdown == 0) {
+ this.setTriggered();
+ } else {
+ this.countdown--;
+ }
+ }
+
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ this.countdown = this.frameCount;
+ bs.wakeupOnElapsedFrames.add(this);
+ if (!passive && (behav != null) && behav.enable) {
+ bs.activeWakeupOnFrameCount++;
+ }
+
+ // This is necessary to invoke this condition next time
+ // Otherwise jftc won't work for static scene.
+ VirtualUniverse.mc.sendRunMessage(bs.universe,
+ J3dThread.UPDATE_BEHAVIOR);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ bs.wakeupOnElapsedFrames.remove(this);
+ if (!passive && (behav != null) && behav.enable) {
+ bs.activeWakeupOnFrameCount--;
+ }
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+ this.countdown = this.frameCount;
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnElapsedTime.java b/src/classes/share/javax/media/j3d/WakeupOnElapsedTime.java
new file mode 100644
index 0000000..354827b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnElapsedTime.java
@@ -0,0 +1,94 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup when a specific number of milliseconds
+ * have elapsed.
+ *
+ */
+public final class WakeupOnElapsedTime extends WakeupCriterion {
+
+ long wait;
+
+ /**
+ * This represents the triggered time
+ */
+ long triggeredTime;
+
+ /**
+ * Constructs a new WakeupOnElapsedTime criterion.
+ * @param milliseconds the number of milliseconds to the wakeup. A value
+ * of zero or less will cause an IllegalArgumentException to be thrown.
+ */
+ public WakeupOnElapsedTime(long milliseconds) {
+ if(milliseconds <= 0L)
+ throw new IllegalArgumentException(J3dI18N.getString("WakeupOnElapsedTime0"));
+ this.wait = milliseconds;
+ }
+
+ /**
+ * Retrieve the WakeupCriterion's elapsed time value that was used when
+ * constructing this object.
+ * @return the elapsed time specified when constructing this object
+ */
+ public long getElapsedFrameTime(){
+ return wait;
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ this.triggeredTime = wait + System.currentTimeMillis();
+ behav.wakeupArray[BehaviorRetained.WAKEUP_TIME_INDEX]++;
+ behav.wakeupMask |= BehaviorRetained.WAKEUP_TIME;
+ VirtualUniverse.mc.timerThread.add(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ behav.wakeupArray[BehaviorRetained.WAKEUP_TIME_INDEX]--;
+ if (behav.wakeupArray[BehaviorRetained.WAKEUP_TIME_INDEX] == 0) {
+ behav.wakeupMask &= ~BehaviorRetained.WAKEUP_TIME;
+ }
+ VirtualUniverse.mc.timerThread.remove(this);
+ }
+
+ /**
+ * This is invoked when Behavior processStimulus can't schedule
+ * to run because behav.active = false. In this case we must
+ * reinsert the wakeupOnElapseTime condition back to the
+ * TimerThread wakeup heap
+ */
+ void reInsertElapseTimeCond() {
+ super.reInsertElapseTimeCond();
+ this.triggeredTime = wait + System.currentTimeMillis();
+ VirtualUniverse.mc.timerThread.add(this);
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+ this.triggeredTime = wait + System.currentTimeMillis();
+ VirtualUniverse.mc.timerThread.add(this);
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnElapsedTimeHeap.java b/src/classes/share/javax/media/j3d/WakeupOnElapsedTimeHeap.java
new file mode 100644
index 0000000..9b9c570
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnElapsedTimeHeap.java
@@ -0,0 +1,210 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * A Binary heap to store WakeupOnElapsedTime. It is arranged so that the
+ * smallest triggeredTime of the wakeup object is put at the top of the heap.
+ * Add/deletion takes O(log n) time.
+ * For better performance we can consider to use Fibonacci Heaps.
+ *
+ */
+class WakeupOnElapsedTimeHeap implements Cloneable {
+
+ // entry 0 is not used so index can be calculated more efficiently
+ WakeupOnElapsedTime data[];
+ int size = 0;
+
+ /**
+ * Construct heap with user-defined capacity
+ */
+ WakeupOnElapsedTimeHeap(int initCapacity) {
+ data = new WakeupOnElapsedTime[initCapacity+1];
+ }
+
+ /**
+ * Construct heap of default capacity 10
+ */
+ WakeupOnElapsedTimeHeap() {
+ this(10);
+ }
+
+ /**
+ * Return size of heap
+ */
+ final int size() {
+ return size;
+ }
+
+ /**
+ * Return true if heap is empty
+ */
+ final boolean isEmpty() {
+ return (size == 0);
+ }
+
+ /**
+ * Get the minimum element from the heap.
+ * User has to make sure that size > 0 before it is called.
+ */
+ final WakeupOnElapsedTime getMin() {
+ return data[1];
+ }
+
+
+ /**
+ * Insert the key into the heap
+ */
+ final void insert(WakeupOnElapsedTime key) {
+ if (data.length == size + 1) {
+ WakeupOnElapsedTime oldData[] = data;
+ data = new WakeupOnElapsedTime[oldData.length << 1];
+ System.arraycopy(oldData, 0, data, 0, oldData.length);
+ }
+
+ int i = ++size;
+
+ int parentIdx = i >> 1;
+ WakeupOnElapsedTime parentKey = data[parentIdx];
+ long time = key.triggeredTime;
+
+ while ((i > 1) && (parentKey.triggeredTime > time)) {
+ data[i] = parentKey;
+ i = parentIdx;
+ parentIdx >>= 1;
+ parentKey = data[parentIdx];
+ }
+ data[i] = key;
+ }
+
+ /**
+ * Extract wakeup condition belongs to behav from the heap.
+ * Return true if wakeup is found.
+ */
+ final void extract(BehaviorRetained behav) {
+ for (int i=1; i <= size; i++) {
+ if (data[i].behav == behav) {
+ extract(i);
+ }
+ }
+ }
+
+ /**
+ * Extract wakeup from the heap.
+ * Return true if wakeup is found.
+ */
+ final boolean extract(WakeupOnElapsedTime wakeup) {
+ for (int i=1; i <= size; i++) {
+ if (data[i] == wakeup) {
+ extract(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Extract the minimum value from the heap.
+ * User has to make sure that size > 0 before it is called.
+ */
+ final WakeupOnElapsedTime extractMin() {
+ return extract(1);
+ }
+
+ /**
+ * Extract the ith value from the heap.
+ * User has to make sure that i <= size before it is called.
+ */
+ final WakeupOnElapsedTime extract(int i) {
+ WakeupOnElapsedTime min = data[i];
+ WakeupOnElapsedTime temp;
+ int l, r;
+ int smallest;
+ data[i] = data[size];
+ data[size] = null; // for gc
+ size--;
+
+
+ do {
+ l = i << 1;
+ r = l+1;
+
+ if ((l <= size) &&
+ (data[l].triggeredTime < data[i].triggeredTime)) {
+ smallest = l;
+ } else {
+ smallest = i;
+ }
+ if ((r <= size) &&
+ (data[r].triggeredTime < data[smallest].triggeredTime)) {
+ smallest = r;
+ }
+ if (smallest == i) {
+ break;
+ }
+ temp = data[smallest];
+ data[smallest] = data[i];
+ data[i] = temp;
+ i = smallest;
+ } while (true);
+
+ return min;
+ }
+
+
+ /***
+ * Trims the capacity of this instance to be the
+ * list's current size.
+ */
+ final void trimToSize() {
+ if (data.length > size+1) {
+ WakeupOnElapsedTime oldData[] = data;
+ data = new WakeupOnElapsedTime[size+1];
+ System.arraycopy(oldData, 0, data, 0, data.length);
+ }
+ }
+
+ /**
+ * Clone this heap
+ */
+ protected final Object clone() {
+ try {
+ WakeupOnElapsedTimeHeap heap = (WakeupOnElapsedTimeHeap)super.clone();
+ heap.data = new WakeupOnElapsedTime[size+1];
+ System.arraycopy(data, 0, heap.data, 0, size+1);
+ return heap;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer("[ ");
+
+ if (size > 0) {
+ sb.append(data[1]);
+ }
+
+ for (int i=2; i <= size; i++) {
+ sb.append("," + data[i]);
+ }
+ sb.append(" ]");
+ return sb.toString();
+ }
+
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnSensorEntry.java b/src/classes/share/javax/media/j3d/WakeupOnSensorEntry.java
new file mode 100644
index 0000000..a74f221
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnSensorEntry.java
@@ -0,0 +1,129 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup on first sensor intersection with the
+ * specified boundary.
+ */
+public final class WakeupOnSensorEntry extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+ static final int SENSORENTRY_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ Bounds region;
+
+ // Transformed region used by BehaviorStructure
+ Bounds transformedRegion;
+
+ Sensor armingSensor;
+
+ /**
+ * Constructs a new WakeupOnEntry criterion.
+ * @param region the region that will trigger a wakeup if a Sensor
+ * intersects.
+ */
+ public WakeupOnSensorEntry(Bounds region) {
+ this.region = (Bounds)region.clone();
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Returns this object's bounds specification
+ * @return the bounds used in constructing this WakeupCriterion.
+ */
+ public Bounds getBounds() {
+ return (Bounds) region.clone();
+ }
+
+ /**
+ * Update the cached Transfrom Region, call from BehaviorStructure
+ */
+ void updateTransformRegion() {
+ if (transformedRegion != null) {
+ transformedRegion.set(region);
+ } else {
+ // region is read only once initialize (since there is no
+ // set method for region). So no need to use cloneWithLock()
+ transformedRegion = (Bounds) region.clone();
+ }
+ transformedRegion.transform(behav.getCurrentLocalToVworld(null));
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ bs.addSensorEntryCondition(this);
+ if ((behav != null) && behav.enable) {
+ bs.activeWakeupOnSensorCount++;
+ }
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ bs.removeSensorEntryCondition(this);
+ if ((behav != null) && behav.enable) {
+ bs.activeWakeupOnSensorCount--;
+ }
+ }
+
+ /**
+ * Set the sensor that trigger this behavior
+ */
+ void setTarget(Sensor sensor) {
+ this.armingSensor = sensor;
+ }
+
+ /**
+ * Retrieves the Sensor object that caused the wakeup.
+ *
+ * @return the triggering Sensor object
+ *
+ * @exception IllegalStateException if not called from within
+ * a behavior's processStimulus method which was awoken by a sensor
+ * entry.
+ *
+ * @since Java 3D 1.2
+ */
+ public Sensor getTriggeringSensor() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnSensorEntry0"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new
+ IllegalStateException(J3dI18N.getString("WakeupOnSensorEntry0"));
+ }
+ }
+ return armingSensor;
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {}
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnSensorExit.java b/src/classes/share/javax/media/j3d/WakeupOnSensorExit.java
new file mode 100644
index 0000000..c8d355d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnSensorExit.java
@@ -0,0 +1,128 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup on first detection of sensors no
+ * longer intersecting the specified boundary.
+ */
+public final class WakeupOnSensorExit extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+ static final int SENSOREXIT_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ Bounds region;
+ // Transformed region used by BehaviorStructure
+ Bounds transformedRegion;
+
+ Sensor armingSensor;
+
+ /**
+ * Constructs a new WakeupOnExit criterion.
+ * @param region the region that will trigger a wakeup if a Sensor
+ * intersects.
+ */
+ public WakeupOnSensorExit(Bounds region) {
+ this.region = (Bounds)region.clone();
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Returns this object's bounds specification
+ * @return the bounds used in constructing this WakeupCriterion.
+ */
+ public Bounds getBounds() {
+ return (Bounds)region.clone();
+ }
+
+ /**
+ * Update the cached Transfrom Region, call from BehaviorStructure
+ */
+ void updateTransformRegion() {
+ if (transformedRegion != null) {
+ transformedRegion.set(region);
+ } else {
+ // region is read only once initialize (since there is no
+ // set method for region). So no need to use cloneWithLock()
+ transformedRegion = (Bounds) region.clone();
+ }
+ transformedRegion.transform(behav.getCurrentLocalToVworld(null));
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ bs.addSensorExitCondition(this);
+ if ((behav != null) && behav.enable) {
+ bs.activeWakeupOnSensorCount++;
+ }
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ bs.removeSensorExitCondition(this);
+ if ((behav != null) && behav.enable) {
+ bs.activeWakeupOnSensorCount--;
+ }
+ }
+
+
+ /**
+ * Set the sensor that trigger this behavior
+ */
+ void setTarget(Sensor sensor) {
+ this.armingSensor = sensor;
+ }
+
+ /**
+ * Retrieves the Sensor object that caused the wakeup.
+ *
+ * @return the triggering Sensor object
+ *
+ * @exception IllegalStateException if not called from within
+ * a behavior's processStimulus method which was awoken by a sensor
+ * exit.
+ *
+ * @since Java 3D 1.2
+ */
+ public Sensor getTriggeringSensor() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnSensorExit0"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new
+ IllegalStateException(J3dI18N.getString("WakeupOnSensorExit0"));
+ }
+ }
+ return armingSensor;
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {}
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnTransformChange.java b/src/classes/share/javax/media/j3d/WakeupOnTransformChange.java
new file mode 100644
index 0000000..ffa4b17
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnTransformChange.java
@@ -0,0 +1,80 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+import java.util.ArrayList;
+
+/**
+ * Class specifying a wakeup when the transform within a specified
+ * TransformGroup changes
+ */
+public final class WakeupOnTransformChange extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 1;
+
+ TransformGroupRetained transform;
+
+ /**
+ * Constructs a new WakeupOnTransformChange criterion.
+ *
+ * @param node the TransformGroup node that will trigger a wakeup if
+ * its transform is modified
+ */
+ public WakeupOnTransformChange(TransformGroup node) {
+ this.transform = (TransformGroupRetained)node.retained;
+ synchronized (transform) {
+ if (transform.transformChange == null) {
+ transform.transformChange = new WakeupIndexedList(1,
+ WakeupOnTransformChange.class,
+ WakeupOnTransformChange.COND_IN_BS_LIST,
+ transform.universe);
+ }
+ }
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Returns the TransformGroup node used in creating this WakeupCriterion
+ * @return the TransformGroup used in this criterion's construction
+ */
+ public TransformGroup getTransformGroup(){
+ return (TransformGroup)this.transform.source;
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ transform.addCondition(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ transform.removeCondition(this);
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {}
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnViewPlatformEntry.java b/src/classes/share/javax/media/j3d/WakeupOnViewPlatformEntry.java
new file mode 100644
index 0000000..4d0d8ae
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnViewPlatformEntry.java
@@ -0,0 +1,133 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup when an active ViewPlatform intersects the
+ * specified boundary.
+ */
+public final class WakeupOnViewPlatformEntry extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+ static final int BOUNDSENTRY_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ Bounds region;
+
+ /**
+ * Transformed region
+ */
+ Bounds transformedRegion;
+
+ /**
+ * ViewPlatform that triggered this wakeup condition.
+ */
+ ViewPlatformRetained triggeredVP;
+
+ /**
+ * Constructs a new WakeupOnEntry criterion.
+ * @param region the region that will trigger a wakeup if a ViewPlatform
+ * intersects.
+ */
+ public WakeupOnViewPlatformEntry(Bounds region) {
+ this.region = (Bounds)region.clone();
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+ /**
+ * Returns this object's bounds specification
+ * @return the bounds used in constructing this WakeupCriterion.
+ */
+ public Bounds getBounds() {
+ return (Bounds)region.clone();
+ }
+
+ /**
+ * Retrieves the ViewPlatform node that caused the wakeup.
+ *
+ * @return the triggering ViewPlatform node
+ *
+ * @exception IllegalStateException if not called from within
+ * a behavior's processStimulus method that was awoken by a
+ * view platform entry.
+ *
+ * @since Java 3D 1.3
+ */
+ public ViewPlatform getTriggeringViewPlatform() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnViewPlatformEntry0"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnViewPlatformEntry0"));
+ }
+ }
+
+ return (triggeredVP != null) ? (ViewPlatform)triggeredVP.source : null;
+ }
+
+ /**
+ * Update the cached Transfrom Region, call from BehaviorStructure
+ * when TRANSFORM_CHANGED message get. Also call from buildTree.
+ */
+ void updateTransformRegion(BehaviorRetained b) {
+ if (transformedRegion != null) {
+ transformedRegion.set(region);
+ } else {
+ // region is read only once initialize (since there is no
+ // set method for region). So no need to use cloneWithLock()
+ transformedRegion = (Bounds) region.clone();
+ }
+ transformedRegion.transform(b.getCurrentLocalToVworld(null));
+ }
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ updateTransformRegion(behav);
+ behav.wakeupArray[BehaviorRetained.WAKEUP_VP_ENTRY_INDEX]++;
+ behav.wakeupMask |= BehaviorRetained.WAKEUP_VP_ENTRY;
+ bs.addVPEntryCondition(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ behav.wakeupArray[BehaviorRetained.WAKEUP_VP_ENTRY_INDEX]--;
+ if (behav.wakeupArray[BehaviorRetained.WAKEUP_VP_ENTRY_INDEX] == 0) {
+ behav.wakeupMask &= ~BehaviorRetained.WAKEUP_VP_ENTRY;
+ }
+ bs.removeVPEntryCondition(this);
+ }
+
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+ // updateTransformRegion() is invoked in BehaviorStructure
+ // whenever Behavior transform change so there is
+ // no need to transform here every time.
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOnViewPlatformExit.java b/src/classes/share/javax/media/j3d/WakeupOnViewPlatformExit.java
new file mode 100644
index 0000000..12a95fa
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOnViewPlatformExit.java
@@ -0,0 +1,135 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Class specifying a wakeup when an active ViewPlatform no longer
+ * intersects the specified boundary.
+ */
+public final class WakeupOnViewPlatformExit extends WakeupCriterion {
+
+ // different types of WakeupIndexedList that use in BehaviorStructure
+ static final int COND_IN_BS_LIST = 0;
+ static final int BOUNDSEXIT_IN_BS_LIST = 1;
+
+ // total number of different IndexedUnorderedSet types
+ static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
+
+ Bounds region;
+
+
+ /**
+ * Transformed region
+ */
+ Bounds transformedRegion;
+
+ /**
+ * ViewPlatform that triggered this wakeup condition.
+ */
+ ViewPlatformRetained triggeredVP;
+
+ /**
+ * Constructs a new WakeupOnExit criterion.
+ * @param region the region that will trigger a wakeup if a ViewPlatform
+ * no longer intersects.
+ */
+ public WakeupOnViewPlatformExit(Bounds region) {
+ this.region = (Bounds)region.clone();
+ WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
+ }
+
+
+ /**
+ * Returns this object's bounds specification
+ * @return the bounds used in constructing this WakeupCriterion.
+ */
+ public Bounds getBounds() {
+ return (Bounds)region.clone();
+ }
+
+ /**
+ * Retrieves the ViewPlatform node that caused the wakeup.
+ *
+ * @return the triggering ViewPlatform node
+ *
+ * @exception IllegalStateException if not called from within
+ * a behavior's processStimulus method that was awoken by a
+ * view platform exit.
+ *
+ * @since Java 3D 1.3
+ */
+ public ViewPlatform getTriggeringViewPlatform() {
+ if (behav == null) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnViewPlatformExit0"));
+ }
+
+ synchronized (behav) {
+ if (!behav.inCallback) {
+ throw new IllegalStateException(J3dI18N.getString("WakeupOnViewPlatformExit0"));
+ }
+ }
+
+ return (triggeredVP != null) ? (ViewPlatform)triggeredVP.source : null;
+ }
+
+ /**
+ * Update the cached Transfrom Region, call from BehaviorStructure
+ * when TRANSFORM_CHANGED message get. Also call from buildTree.
+ */
+ void updateTransformRegion(BehaviorRetained b) {
+ if (transformedRegion != null) {
+ transformedRegion.set(region);
+ } else {
+ // region is read only once initialize (since there is no
+ // set method for region). So no need to use cloneWithLock()
+ transformedRegion = (Bounds) region.clone();
+ }
+ transformedRegion.transform(b.getCurrentLocalToVworld(null));
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to add wakeupCondition to behavior structure.
+ */
+ void addBehaviorCondition(BehaviorStructure bs) {
+ updateTransformRegion(behav);
+ behav.wakeupArray[BehaviorRetained.WAKEUP_VP_EXIT_INDEX]++;
+ behav.wakeupMask |= BehaviorRetained.WAKEUP_VP_EXIT;
+ bs.addVPExitCondition(this);
+ }
+
+
+ /**
+ * This is a callback from BehaviorStructure. It is
+ * used to remove wakeupCondition from behavior structure.
+ */
+ void removeBehaviorCondition(BehaviorStructure bs) {
+ bs.removeVPExitCondition(this);
+ behav.wakeupArray[BehaviorRetained.WAKEUP_VP_EXIT_INDEX]--;
+ if (behav.wakeupArray[BehaviorRetained.WAKEUP_VP_EXIT_INDEX] == 0) {
+ behav.wakeupMask &= ~BehaviorRetained.WAKEUP_VP_EXIT;
+ }
+ }
+
+
+ /**
+ * Perform task in addBehaviorCondition() that has to be
+ * set every time the condition met.
+ */
+ void resetBehaviorCondition(BehaviorStructure bs) {
+ // updateTransformRegion() is invoked in BehaviorStructure
+ // whenever Behavior transform change so there is
+ // no need to transform here every time.
+ }
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOr.java b/src/classes/share/javax/media/j3d/WakeupOr.java
new file mode 100644
index 0000000..c9ecb6a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOr.java
@@ -0,0 +1,100 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+
+/**
+ * Class specifying any number of wakeup conditions ORed together.
+ * This WakeupCondition object specifies that Java 3D should awaken
+ * this Behavior when any of the WakeupCondition's constituent wakeup
+ * criteria becomes valid.
+ * <p>
+ * Note that a unique WakeupCriterion object must be used
+ * for each individual element in the array of wakeup criteria.
+ */
+
+public final class WakeupOr extends WakeupCondition {
+
+ WakeupCriterion conditions[];
+
+ /**
+ * Constructs a new WakeupOr criterion.
+ * @param conditions a vector of individual Wakeup conditions
+ */
+ public WakeupOr(WakeupCriterion conditions[]) {
+ this.conditions = new WakeupCriterion[conditions.length];
+
+ for(int i = 0; i < conditions.length; i++){
+ this.conditions[i] = conditions[i];
+ }
+ }
+
+ /**
+ * This sets the bit for the given child, then checks if the full condition is met
+ */
+ void setConditionMet(int id, Boolean checkSchedulingRegion) {
+ if (parent == null) {
+ super.setConditionMet(this.id, checkSchedulingRegion);
+ } else {
+ parent.setConditionMet(this.id, checkSchedulingRegion);
+ }
+ }
+
+
+ /**
+ * This gets called when this condition is added to the AndOr tree.
+ */
+ void buildTree(WakeupCondition parent, int id, BehaviorRetained b) {
+ super.buildTree(parent, id, b);
+
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].buildTree(this, i, b);
+ }
+ }
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure lists
+ */
+ void cleanTree(BehaviorStructure bs) {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i].cleanTree(bs);
+ }
+ }
+
+ void reInsertElapseTimeCond() {
+ super.reInsertElapseTimeCond();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].reInsertElapseTimeCond();
+ }
+ }
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure.
+ */
+ void resetTree() {
+ super.resetTree();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].resetTree();
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/javax/media/j3d/WakeupOrOfAnds.java b/src/classes/share/javax/media/j3d/WakeupOrOfAnds.java
new file mode 100644
index 0000000..1f59841
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/WakeupOrOfAnds.java
@@ -0,0 +1,100 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+
+/**
+ * Class specifying any number of AND wakeup conditions ORed together.
+ * This WakeupCondition object specifies that Java 3D should awaken
+ * this Behavior when any of the WakeupCondition's constituent WakeupAnd
+ * conditions becomes valid.
+ * <p>
+ * Note that a unique WakeupCriterion object must be used for each
+ * individual element in the set of arrays specified by the array of
+ * WakeupAnd objects.
+ */
+
+public final class WakeupOrOfAnds extends WakeupCondition {
+
+ WakeupAnd conditions[];
+
+ /**
+ * Constructs a new WakeupOrOfAnds criterion.
+ * @param conditions a vector of individual Wakeup conditions
+ */
+ public WakeupOrOfAnds(WakeupAnd conditions[]) {
+ this.conditions = new WakeupAnd[conditions.length];
+ for(int i = 0; i < conditions.length; i++){
+ this.conditions[i] = conditions[i];
+ }
+ }
+
+
+ /**
+ * This sets the bit for the given child, then checks if the full condition is met
+ */
+ void setConditionMet(int id, Boolean checkSchedulingRegion) {
+ if (parent == null) {
+ super.setConditionMet(this.id, checkSchedulingRegion);
+ } else {
+ parent.setConditionMet(this.id, checkSchedulingRegion);
+ }
+ }
+
+ /**
+ * This gets called when this condition is added to the AndOr tree.
+ */
+ void buildTree(WakeupCondition parent, int id, BehaviorRetained b) {
+ super.buildTree(parent, id, b);
+
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].buildTree(this, i, b);
+ }
+ }
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure lists
+ */
+ void cleanTree(BehaviorStructure bs) {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i].cleanTree(bs);
+ }
+ }
+
+ void reInsertElapseTimeCond() {
+ super.reInsertElapseTimeCond();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].reInsertElapseTimeCond();
+ }
+ }
+ }
+
+ /**
+ * This goes through the AndOr tree to remove the various criterion from the
+ * BehaviorStructure.
+ */
+ void resetTree() {
+ super.resetTree();
+ for(int i = 0; i < conditions.length; i++) {
+ if (conditions[i] != null) {
+ conditions[i].resetTree();
+ }
+ }
+ }
+
+}